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

hi monks ! :) need a little help with this map function that i'm trying to resolve..
$input = ':xxx xxxxx = #xxx :xxx @rndletters @rndletters'; @input2arr = split / /, $input; my @names = map {$i > 6 ? push(@names,$input2arr[$i]) : $i++ } @input2 +arr;
with the map function im trying to push each value into the names array starting at offset 6 in the @input2arr array. here is how it would be done without map:
my $i = 0; my $input = ':xxx xxxxx = #xxx :xxx @rndletters @rndletters'; my @names; my @input2arr = split //, $input; for my $str (@input2arr) { if ($i > 6) { push (@names, $str); } $i++; }

Replies are listed 'Best First'.
Re: bit of help with map function
by moritz (Cardinal) on Apr 30, 2012 at 18:53 UTC

    Others have already commented on better ways to achieve what you want. Let me try to explain your mistake:

    my @names = map {$i > 6 ? push(@names,$input2arr[$i]) : $i++ } @input2arr;

    pushing to the array @names inside the map block is wrong. Because map aggregates all return values from the block, and then assign them to @names, so all your previous changes are lost.

    Or phrased differently, if you just return those elements, map will do the pushing for you.

Re: bit of help with map function
by MidLifeXis (Monsignor) on Apr 30, 2012 at 18:53 UTC

    My first response would be why? map is used (usually) when you want to modify a list of values and use the modified list. In my opinion, what you are doing is clearer being done with a for loop.

    Reformatted:

    my @names = map { # 1, 3 $i > 6 # 2, 4 ? push(@names,$input2arr[$i]) # 1, 2 : $i++; # 2 } @input2arr;

    • 1) You are assigning the results of the map call to @names and modifying it within the map call. Don't do that. It will not do what your original for loop does.
    • 2) push returns the number of elements after the push in @names. The tristate ( A ? B : C returns either the size of @names when a push happens, or the value of the counter $i.
    • 3) @names, I believe, will end up with the value ( 0, 1, 2, 3, 4, 5, 6). It is also possible that it is undefined behavior.
    • 4) $i is not defined. I am assuming that, in numeric form, it starts at 0.

    If you didn't want to use the for construct, perhaps the 'x' operator, splice, or even a straight assignment would be better.

    Update: Since others posted before I completed mine, this block of code illustrates what you would be seeing:

    @x = map { $r = push(@x, $_); print(join(",", @x),"\n"); $r; } qw(a b c d e); print join(",", @x), "\n"; __DATA__ a a,b a,b,c a,b,c,d a,b,c,d,e 1,2,3,4,5

    --MidLifeXis

Re: bit of help with map function
by toolic (Bishop) on Apr 30, 2012 at 18:37 UTC
    You could just use substr instead of the for loop or map. This produces identical output to your 'for' loop code:
    my $input = ':xxx xxxxx = #xxx :xxx @rndletters @rndletters'; my @names = split //, substr $input, 7;
Re: bit of help with map function
by kennethk (Abbot) on Apr 30, 2012 at 18:39 UTC
    The easiest way to do that would probably be array slices: my @names = @input2arr[5 .. $#input2arr];

    Barring that, that's not how map functions work. Map takes a list and performs an operation on each element, returning the result. You are thinking of it more as a foreach, which is not what it's intended for. If you really want to use map, you could use an iterator and return an empty list:

    my $i = 1; my @names = map {++$i > 6 ? $_ : () } @input2arr;
    or force it into a grep:
    my $i = 1; my @names = grep {++$i > 6} @input2arr;

    But neither of these is particularly transparent.

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: bit of help with map function
by tobyink (Canon) on Apr 30, 2012 at 21:33 UTC

    This bit is quite wrong:

    my @names = map {$i > 6 ? push(@names,$input2arr[$i]) : $i++ } @input2 +arr;

    Inside the map block (i.e. the bit between { and }) you don't just put the equivalent of what you'd put inside a foreach block. Allow me to demonstrate with a simple example...

    my @letters = ('a', 'b', 'c'); my @capitals; foreach my $l (@letters) { push @capitals, uc($l); }

    Don't do this:

    my @letters = ('a', 'b', 'c'); my @capitals = map { $i++; push @capitals, uc($letters[$i]) } @letters +;

    Do this:

    my @letters = ('a', 'b', 'c'); my @capitals = map { uc($_) } @letters;

    What map does basically is to execute the contents of the block on each item in the given list, making a new list from all the results. Note in the above example, the code within the block doesn't need to do any pushing onto @capitals, and it doesn't need to look at @letters - the map function does that for you.

    Your particular example can be written as:

    my $i = 0; my @names = map { $i++ > 6 ? ($_) : () } @input2arr;

    Though using grep in this case might be better.

    my $i = 0; my @names = grep { $i++ > 6 } @input2arr;

    Or just use an array slice:

    my @names = @input2arr[ 7 .. $#input2arr ];
    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
      thank you everyone for the help :) i learnt allot from this info :)