CombatSquirrel has asked for the wisdom of the Perl Monks concerning the following question:

I wrote a little Pel program to generate note sequences according to some simple rules. Each note is represented by an integer between 0 and 71, each interval by its number of half-tone steps (1 to 11, prime and octave left out). In a sub I need to iterate over all possible next notes. I do this by using a foreach loop like the one below (actually, it is the one below).
for my $nnot ( map { $solution[-1] + $_, $solution[-1] - $_ } nmap { $_ if ($availableIntervals[$_] > 0) } (1 .. 11) ) {
where @solution holds the beginning (for example ( 23, 15, 21 ) ) of the solution sequence, @availableIntervals the number of allowed uses left for each possible interval (i.e. the difference between two consecutive notes) and &nmap is a function which works quite similar to map. It is defined as follows:
sub nmap(&@){ my $rout = shift; # get mapping routine my @list = (); # output list my $curr; # current result for (@_) { $curr = $rout->($_); # get result if (defined $curr) { # if $curr is defined push @list, $curr; # add it to the list } } return @list; }
The sense is that the interval is just used if it can be used. This loop seems terribly awkward to me, especially in a Perl program. Is there any better way of doing the iteration (possibly without using map at all)?
I tried finding info using perldoc -f map, Google with "map undefined" and browsed through the threads on this page which I found using the search function, but none seemed to provide suitable information to help me rewrite the loop in a clearer way.

Replies are listed 'Best First'.
Re: Map function that does not return undefined values
by broquaint (Abbot) on May 13, 2003 at 14:35 UTC
    Sounds like a square peg, round hole problem - you want grep instead of map e.g
    for my $nnot ( map { $solution[-1] + $_, $solution[-1] - $_ } grep { $availableIntervals[$_] > 0 } (1 .. 11)) {

    HTH

    _________
    broquaint

      Thanks a lot, that really helped. The program is faster now as well (no wonder).
Re: Map function that does not return undefined values
by jdporter (Paladin) on May 13, 2003 at 14:41 UTC
    It looks to me like what you're trying to do is filter down the list to only those values which are "available". The way to filter down a list is, canonically, with grep.
    for my $nnot ( map { $solution[-1] + $_, $solution[-1] - $_ } grep { $availableIntervals[$_] > 0 } (1 .. 11) ) {
    You can combine the map and the grep into a single map (similar to what suaveant alluded to, above):
    for my $nnot ( map { $availableIntervals[$_] > 0 ? ( $solution[-1] + $_, $solution[-1] - $_ ) : () } (1 .. 11) ) {

    jdporter
    The 6th Rule of Perl Club is -- There is no Rule #6.

Re: Map function that does not return undefined values
by suaveant (Parson) on May 13, 2003 at 14:41 UTC
    map will flatten lists you return... so you can do something like
    map { defined $_ ? $_ : () } @array;
    and will only get back the defined elements (kind of like you could do with grep, but more powerful)

    all you have to do is add more interesting code to the map. Hope that is what you wanted...

    Update BTW - you can also use that to add extra items to the array. If you return a list of multiple items from the code in map they will all be added to the output

    print join(',',map { ($_,$_*2,$_*3) } (1,5)); #prints: 1,2,3,5,10,15

                    - Ant
                    - Some of my best work - (1 2 3)