Modifying $_ in "map", with an array containing a gap...

R

Raymundo

Hello,

I'm sorry I'm not good at English. :)


foreach and map functions show the same result when an array has no
gap.

@array = (1, 2, 3, 4);
foreach (@array) {
$_ *= 10
}
# now, $array = (10, 20, 30, 40)

@array = (1, 2, 3, 4);
map { $_ *= 10 } @array;
# now, $array = (10, 20, 30, 40)


However, if an array contains a gap...

1 $array1[0] = 0;
2 $array1[9] = 9; # now $array1 = (0, undef,
undef, ... , 9);
3 print "@array1", "\n"; # 0 "" "" ... "" 9
4 foreach (@array1) {
5 $_ *= 10 # $array1 = (0, 0, 0, ... , 90)
6 }
7 print "@array1", "\n"; # 0 0 0 ... 0 90
8
9
10 $array2[0] = 0;
11 $array2[9] = 9;
12 print "@array2", "\n"; # 0 "" "" ... "" 9
13 map { $_ *= 10 } @array2; # ERROR!!!!!!
14 print "@array2", "\n";

line 1-7 work well, but using map, line 13 reports an error:

Modification of a read-only value attempted at t2.pl line 13.


Before line 13, line 12 prints the intervening elements, treating
undef as null string. Then why does line 13 make such error? Is it a
bug? or...?
 
M

Michele Dondi

Subject: Modifying $_ in "map", with an array containing a gap...
^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^

DON'T! That's not what map() is for.


Michele
 
M

Mirco Wahab

Raymundo said:
foreach and map functions show the same result when
an array has no gap.

There are some misunderstanding from your side ;)
1: $array1[0] = 0;
2: $array1[9] = 9; # now $array1 = (0, undef, undef, ... , 9);
3: print "@array1", "\n"; # 0 "" "" ... "" 9

Warning: "Use of uninitialized value in join or string at line 3."
4 foreach (@array1) {
5 $_ *= 10 # $array1 = (0, 0, 0, ... , 90)
6 }

Warning: "Use of uninitialized value in multiplication (*) at line 5."
7 print "@array1", "\n"; # 0 0 0 ... 0 90
8

No more warnings on #7, because the for *did* convert undef to numeric 0
10 $array2[0] = 0;
11 $array2[9] = 9;
12 print "@array2", "\n"; # 0 "" "" ... "" 9

Warning: Use of uninitialized value in join or string at line 12.
13 map { $_ *= 10 } @array2; # ERROR!!!!!!

Error: "Modification of a read-only value attempted at line 13."

Do you understand, what this error means? $_ can't be written
to ($_ = $_ * 10) in a map.
Before line 13, line 12 prints the intervening elements, treating
undef as null string. Then why does line 13 make such error? Is it a
bug? or...?

No. The map operator is intended to convert one list, element by element,
into a new list, leaving the old list as it is. The 'correct' usage of
map would be, in your case:

13: my @array3 = map { $_ * 10 } @array2;
14: print "@array3", "\n";


Regards

Mirco
 
M

Martijn Lievaart

^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^

DON'T! That's not what map() is for.

From perldoc -f map:

Note that $_ is an alias to the list value, so it can be used
to modify the elements of the LIST. While this is useful and
supported, it can cause bizarre results if the elements of LIST
are not variables. Using a regular "foreach" loop for this
purpose would be clearer in most cases.

So modifying $_ is supported, but as undef is not a variable we get
bizarre results.

This is clearly one of those cases where it isn't a bug, it's a
feature. :)
 
M

Michele Dondi

This is clearly one of those cases where it isn't a bug, it's a
feature. :)

I didn't want to imply it were a bug, in fact I didn't write so.
Indeed I know it is supported and in very rare situations may also be
useful. But generally you use map() to *map* a list to another list,
which its main use.


Michele
 
X

xhoster

Raymundo said:
Hello,

I'm sorry I'm not good at English. :)

foreach and map functions show the same result when an array has no
gap.

@array = (1, 2, 3, 4);
foreach (@array) {
$_ *= 10
}
# now, $array = (10, 20, 30, 40)

@array = (1, 2, 3, 4);
map { $_ *= 10 } @array;
# now, $array = (10, 20, 30, 40)

However, if an array contains a gap...

1 $array1[0] = 0;
2 $array1[9] = 9; # now $array1 = (0, undef,
undef, ... , 9);
3 print "@array1", "\n"; # 0 "" "" ... "" 9
4 foreach (@array1) {
5 $_ *= 10 # $array1 = (0, 0, 0, ... , 90)
6 }
7 print "@array1", "\n"; # 0 0 0 ... 0 90
8
9
10 $array2[0] = 0;
11 $array2[9] = 9;
12 print "@array2", "\n"; # 0 "" "" ... "" 9
13 map { $_ *= 10 } @array2; # ERROR!!!!!!
14 print "@array2", "\n";

line 1-7 work well, but using map, line 13 reports an error:

Modification of a read-only value attempted at t2.pl line 13.

Before line 13, line 12 prints the intervening elements, treating
undef as null string. Then why does line 13 make such error? Is it a
bug? or...?

I don't know about a bug, but at least a mal-feature, I would say.
There is a special kind of fly-weight undef used in sparse arrays.
It should get promoted to the other kind of undef automatically when
needed. Apparently being aliased through map or grep inhibits this
automatic promotion, while being aliased through foreach doesn't inhibit
it.


This also gives the error:

perl -le 'my @x; $x[5]=5; $_*=10 foreach grep 1, @x; print "@x"'


removing the "grep 1," also removes the error.

Xho
 
M

Michele Dondi

needed. Apparently being aliased through map or grep inhibits this
automatic promotion, while being aliased through foreach doesn't inhibit
it.

Thank you. While I still repeat my initial reply to the OP, the issue
was subtler than I had understood.


Michele
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top