Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

bug in map?? 1

Status
Not open for further replies.
Dec 12, 2003
24
US
Hi,

I dont know if I'm just staring at this wrong, but it looks like a bug to me...

'map' is supposed to go through a list and perform an expression on each element and return the new list with the result...

@nums=(1,2,3);
foreach $n (@nums) {print "init: $n\n"}
@nums_plus_one=map {$_++} @nums;
foreach $n (@nums) {print "should_be_init: $n\n"}
foreach $n (@nums_plus_one) {print "should_be_plussed: $n\n"}



produces:

init: 1
init: 2
init: 3
should_be_init: 2
should_be_init: 3
should_be_init: 4
should_be_plussed: 1
should_be_plussed: 2
should_be_plussed: 3



why is it modifying the input list and not returning the result to the output list??
 
I don't know how to address whether their is a bug or not, but I do find that

@nums=(1,2,3);
foreach $n (@nums) {print "init: $n\n"}
@nums_plus_one=map {$_++} @nums;
foreach $n (@nums) {print "should_be_init: $n\n"}
foreach $n (@nums_plus_one) {print "should_be_plussed: $n\n"}


produces

init: 1
init: 2
init: 3
should_be_init: 1
should_be_init: 2
should_be_init: 3
should_be_plussed: 2
should_be_plussed: 3
should_be_plussed: 4
 
After some searching around the web I find that this is a known issue (or feature) of map...if you modify $_ in the loop it will corrupt the input list...I find this recommendation:



@nums_plus_one=map {$x=$_+1} @nums;


so it's the $x that gets the incr. if you're doing a tr or something you have to go:

@trd_list = map {($x=$_)=~tr/abc/123;$x} @init_list;


or you'll get the replace count and not the result in @trd_list.


oh well
 
Sorry, error in my post. I meant to say that

@nums=(1,2,3);
foreach $n (@nums) {print "init: $n\n"}
@nums_plus_one=map {$_+1} @nums;
foreach $n (@nums) {print "should_be_init: $n\n"}
foreach $n (@nums_plus_one) {print "should_be_plussed: $n\n"}


produces

init: 1
init: 2
init: 3
should_be_init: 1
should_be_init: 2
should_be_init: 3
should_be_plussed: 2
should_be_plussed: 3
should_be_plussed: 4


The section in bold {$_+1} gives the proper result. It only seems to fail when you use ++
 
It's a combination of pre- versus post-increment and aliasing. If you say
Code:
$_++ for(@list);
Then everything in @list gets incremented. For(each) loops alias each element of the array for the duration of the block. So the first time it runs, $_ is the same variable as $list[0]. This is also why the following loop will fail:
Code:
$_++ for(0,1,2,3);
Because $_ is aliasing the constant values in the list, and constants can't be modified. The point of all this is to say map aliases each element, too. This is a speed increase to keep from copying all the memory, as well as a nice feature to change an entire array.

map returns the last value returned in its code block, which is why you have to jump through hoops to make tr/// and other functions work like you want. When you want to modify a given array, you're better off using a for loop. When you want to make a new array based off [some alteration of] another, use map.

The difference between pre- and post-increment are causing problems above, too. $_++ is a post-increment and ++$_ is pre-increment. Post-increment returns the value first then increments it. Pre-increment increments first, then returns the new, changed value.

$_++ is the same as $_; $_ = $_ + 1;
++$_ is the same as $_ = $_ + 1; $_;

Since map returns a list of how block was evaluated each time and sets each element as an aliased $_, the code [tt]map $_++, @list[/tt] first returns the value of each element of @list to map (why the returned array is just a copy of the initial) and then increments the value (since it's an alias to the original, the original is incremented).

In the end, that's why you should use racklet's suggestion [tt]@nums_plus_one=map {$_+1} @nums;[/tt] It returns one more than each element of @nums and doesn't alter any elements in @nums.

Sorry the response is a little tardy, I just saw you over in the MVP list and didn't recognize you. :)

________________________________________
Andrew - Perl Monkey
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top