On Thu, Feb 4, 2010 at 2:24 AM, John W. Krahn <jwkr...@shaw.ca> wrote:

> Eric Mooshagian wrote:
>
>> Dear All,
>>
>
> Hello,
>

Hi John, Thanks for the notes.

>
>
 I have a few subroutines that I use to first build an index for several
>> arrays and then, for example, take the mean for the index values.  When I
>> build the index I can exclude particular values in an array as follows:
>>
>> my $index = defindex(
>>    exclude => ["0",\...@accuracy],
>>    valueof => [...@subject,\...@side,\...@valence],
>> );
>>
>>
>> This works fine, but only only for excluding single values like (e.g,
>> "0").  What I would really like to do is pass an argument that includes
>> comparators, for example, in pseudo code:
>>
>> exclude => (responsetime <= 200) && (responsetime >= 1200) where
>> responsetime refers to an array.
>>
>> However, I have no idea how to do this using named parameters.  Is there a
>> strategy for passing such arguments to a subroutine?
>>
>
> Like Perl's built-in sort() function or the File::Find::find() function you
> can use a subroutine reference, for example, in pseudo code:
>
> exclude => sub { return something if (responsetime <= 200) && (responsetime
> >= 1200) }


Thanks! I think this is the clue I've been looking for though I don't yet
understand how to implement the code.  Are dispatch tables relevant here?

All other mistakes below were purely out of naivete.  Thank you for the
changes.

>
>  My current code:
>>
>> use strict;
>> use warnings;
>> use List::Util qw(sum);
>> use List::MoreUtils qw(indexes each_arrayref);
>>
>> #sample data
>> my @subject = qw(Sub1 Sub1 Sub1 Sub1 Sub1 Sub1 Sub1 Sub1 Sub2 Sub2 Sub2
>> Sub2 Sub2 Sub2 Sub2 Sub2);
>> my @side= qw(left left left left right right right right left left left
>> left right right right right);
>> my @updown= qw(up down down up up up down down up up down down up up down
>> down);
>> my @valence= qw(minus plus minus plus minus plus minus plus minus plus
>> minus plus minus plus minus plus);
>> my @responsetime=
>> (999.4,233.1,456.1,763.55,241.1,742.67,223.78,555.89,664.91,157.11,748.12,
>> 848.13, 253.14, 623.15, 635.16, 423.17);
>> my @accuracy= qw(1 1 0 1 0 1 1 0 1 0 1 0 0 1 1 1);
>>
>>
>> ###########################################
>> my $index = defindex(
>>    exclude => ["0",\...@accuracy],
>>    valueof => [...@subject,\...@side,\...@valence],
>> );
>>
>> my @index = @$index;
>>
>> getmean(\...@index,\...@responsetime);
>>
>> ######################################
>> #Code for the module
>>
>> #define the index
>> sub defindex {
>>    my $i;
>>    my $j;
>>    my @combined;
>>    my @meshed;
>>    my @index;
>>    my @match;
>>    my $exclude_input;
>>    my @values;
>>    my $input;
>>    my @exclude_values;
>>
>
> It is usually better to define variables where they are first used instead
> of all at the start of code.
>
>
>     #Get the input arguments
>>    my (%options) = @_;
>>    $input = $options{valueof};
>>    $exclude_input = $options{exclude};
>>        #Put the inputs into arrays
>>    @values = @$input if $input || die "No input to defindex!\n";
>>    @exclude_values = @$exclude_input if $exclude_input;
>>
>
> You should validate subroutine input at the point of input:
>
>    @_ and not @_ % 2 and my %options = @_ or die "Invalid input to
> defindex!\n";
>    my @values         = @{ $options{ valueof } };
>    my @exclude_values = @{ $options{ exclude } };
>
>
>     #Number of arguments for the index
>>    my $num_args = (scalar(@values));
>>
>
> The expression (scalar(@values)) is already in scalar context because the
> left-hand side of the assignment forces scalar context.
>
>
>     for my $i (0..($num_args-1)) {
>>        push(@combined,$values[$i]);
>>    }
>>
>
> That is usually written as:
>
>    for my $i ( 0 .. $#values ) {
>        push @combined, $values[ $i ];
>    }
>
> Or simply:
>
>    my @combined = @values;
>
>     @meshed = mesh(@combined);
>>
>
> But you don't really need @combined, you could just use @values instead.
>
>    my @meshed = mesh( @values );
>
>
>     #Get the indices of the excluded values
>>    my $exclude = exclude(@exclude_values);
>>    my @exclude_indices = @$exclude;
>>
>>    $j=0;
>>    while (@meshed) {
>>        my $match = grep /^$j$/, @exclude_indices;
>>        unless ($match) {
>>            $index[$j]= join('', splice(@meshed,0,$num_args));
>>            $j++;
>>        } else     {
>>            my $ignore = join('', splice(@meshed,0,$num_args));
>>
>
> The $ignore variable is not used anywhere so you don't really need it. The
> only required part is the splice() function.
>
>
>            splice @meshed, 0, $num_args;
>
>             $index[$j]= "_";
>>            $j++;
>>        }
>>
>
> Your loop might be better written as:
>
>    my $j = 0;
>    my @index;
>    while ( @meshed ) {
>        my $temp = join '', splice @meshed, 0, @values;
>
>        $index[ $j++ ] = grep( $_ == $j, @exclude_indices ) ? '_' : $temp;
>    }
>
>         undef my(@match);
>>
>
> This is a superfluous statement, it can be safely removed.
>
>
>      }
>>        my @filtered_idx = grep($_ ne "_",@index);
>>     return \...@filtered_idx;
>>
>> } #end defindex
>>
>>
>> sub exclude {
>>    my @values;
>>    my @match_idx;
>>    my @uniq_idx;
>>     my $i;
>>     my $j;
>>
>
> Again, it is usually better to define variables where they are first used
> instead of all at the start of code.
>
>
>      while (@_) {
>>         my ($key, $values_ref)=(shift, shift);
>>
>
> I usually like to write that as:
>
>         my ($key, $values_ref) = splice @_, 0, 2;
>
> As it is less ambiguous.
>
>         @values = @$values_ref;
>>
>
> Why not use $values_ref directly instead of making a copy?
>
>
>          for ($i = 0; $i < @values; $i++) {
>>
>
> That is usually written as:
>
>         for my $i ( 0 .. $#values ) {
>
>
>              if ($values[$i] eq $key) {
>>                 push(@match_idx, $i);    # save the index
>>             }
>>         }
>>
>
> You could always use grep() instead of a for loop:
>
>        push @match_idx, grep $key eq $values_ref->[ $_ ], 0 ..
> $#$values_ref;
>
>
>      }
>>         #find the unique elements
>>        my %seen = ();
>>        foreach $j (@match_idx) {
>>
>
>        foreach my $j (@match_idx) {
>
>             @{$_} = split /\s/, $j;
>>
>
> That statement is superfluous and can be safely removed.  (And why are you
> modifying a global variable anyway?)  (And how would $j ever contain any
> whitespace?)
>
>
>             push(@uniq_idx, $j) unless $seen{$j}++;
>>        } #foreach $a
>>
>
> That is usually written as:
>
>    #find the unique elements
>    my %seen;
>    my @uniq_idx = grep !$seen{ $_ }++, @match_idx;
>
>
>      return \...@uniq_idx;
>>
>> } #end exclude
>>
>> # From Shlomi Fish
>> sub get_combos_from_kv {
>>    my ($keys, $values) = @_;
>>
>>    my %combos;
>>        foreach my $i (0..$#$keys) {
>>        my $key = $keys ->[$i];
>>        my $value = $values ->[$i];
>>
>>        push (@{$combos{$key}},$value);
>>    }
>>        return \%combos;
>> }
>>
>> # get the mean by index
>> sub getmean {
>>    my @ke...@{$_[0]};
>>    my @valu...@{$_[1]};
>>         my $combos = get_combos_from_kv(\...@keys,\...@values);
>>    my %combos = %$combos;        #dereferenced
>>          print "ID\tMeans\n";
>>    my $key;
>>    my $mean;
>>            foreach $key (sort keys %combos) {
>>             my $mean = (sum(@{$combos{$key}}))/(scalar(@{$combos{$key}}));
>>             printf "$key\t%.3f\n", $mean;
>>        }
>>
>
> Why all the dereferencing, copying and referencing and the redundant use of
> scalar()?
>
>    my $combos = get_combos_from_kv( @_ );
>
>    print "ID\tMeans\n";
>    for my $key ( sort keys %$combos ) {
>        printf "$key\t%.3f\n", sum( @{ $combos->{ $key } } ) / @{ $combos->{
> $key } };
>
>    }
>
>  } #end of getmean
>>
>> #borrowed from the List::MoreUtils Module by Tassilo von Parseval
>>  sub mesh
>> (\...@\@;\...@\@\...@\@\...@\@\...@\@\...@\@\...@\@\...@\@\...@\@\...@\@\...@\@\...@\@\...@\@\...@\@\...@\@\...@\@)
>>  {
>>
>
> That should be written as:
>
> sub mesh {
>
> The prototype doesn't appear to be doing what you seem to think it is
> doing.
>
>
>     my $max = -1;
>>    $max < $#$_  &&  ($max = $#$_)  for @_;
>>
>>    map { my $ix = $_; map $_->[$ix], @_; } 0..$max;
>> }
>>
>
>
>
> John


Thanks,
Eric

>

Reply via email to