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

Discussion in 'Perl Misc' started by Raymundo, May 17, 2007.

  1. Raymundo

    Raymundo Guest

    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...?
    Raymundo, May 17, 2007
    #1
    1. Advertising

  2. Raymundo

    Guest

    $array2[0] = 0;
    $array2[9] = 9;
    print "@array2", "\n"; # 0 "" "" ... "" 9
    map { defined $_ ? $_ *= 10 : 0 } @array2;
    print "@array2", "\n"; # 0 "" "" ... "" 90
    , May 17, 2007
    #2
    1. Advertising

  3. Raymundo

    Mirco Wahab Guest

    wrote:
    > $array2[0] = 0;
    > $array2[9] = 9;
    > print "@array2", "\n"; # 0 "" "" ... "" 9
    > map { defined $_ ? $_ *= 10 : 0 } @array2;


    map { $_ *= 10 if defined } @array2;

    will do in the context.

    > print "@array2", "\n"; # 0 "" "" ... "" 90


    I think it's generally a bad practice to
    emphasize such solutions which are clearly
    of bad style and will lead to dozens of
    warnings under 'use warnings'.

    Regards

    Mirco
    Mirco Wahab, May 17, 2007
    #3
  4. Raymundo

    Mirco Wahab Guest

    Glenn Jackman wrote:
    > At 2007-05-17 11:02AM, "" wrote:
    >>
    >> $array2[0] = 0;
    >> $array2[9] = 9;
    >> print "@array2", "\n"; # 0 "" "" ... "" 9
    >> map { defined $_ ? $_ *= 10 : 0 } @array2;
    >> print "@array2", "\n"; # 0 "" "" ... "" 90

    >
    > Of course, @array2 no longer contains undefined values at this point:
    > it contains zeroes instead.


    Wrong guess ;)

    Of course will @array2 keep the undefs.

    Regards

    Mirco
    Mirco Wahab, May 17, 2007
    #4
  5. On May 17, 8:40 am, Raymundo <> wrote:

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


    Good enough.

    > 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...?


    Yes it's a bug.

    The subtly is the difference between elements of an array that are
    undef and ones that are non-existent.

    See "perldoc -f exsts".

    If I say..

    my @array2;
    $array2[0] = 0;
    $array2[9] = 9;

    ...then elements 1..8 of @array are not just undef but non-existent.

    In for() there is special magic to allow you to have a reference to a
    non-existent element of an array without it becoming autovivified.

    In map() there's evidently no such magic. In the LIST argument in
    map() any non-existent elements are replaced by the "one true undef"
    aka PL_sv_undef. (The one you get a reference to by saying \undef).

    IMNSHO this needs to be explained in "perldoc -f map" or changed.

    Note: if you think what map() does is odd then consider what happens
    if you pass a gappy array to a subroutine! Here gap in the argument
    leave gaps in @_ but modifying the missing element in @_ gives no
    error but does not modify the missing element in the original array.

    sub inc {
    for my $i ( 0.. $#_ ) {
    $_[$i]++;
    }
    }

    my @array;
    $array[1]=666;

    inc @array;

    print join ',' => @array; # ,667
    Brian McCauley, May 17, 2007
    #5
  6. Raymundo

    Mirco Wahab Guest

    Brian McCauley wrote:
    > The subtly is the difference between elements of an array that are
    > undef and ones that are non-existent.


    Very interesting, I wasn't even aware of it.

    Lets see:
    my @array;
    $array[0] = 0;
    $array[9] = 9;

    then elements 1..8 will be(Devel::peek)
    "NULL" SV's with Refcnt of 1

    SV = NULL(0x0) at 0x182d528
    REFCNT = 1
    FLAGS = ()


    in 'for', these NULL SV's are replaced dynamically
    by something strange:

    SV = PVLV(0x18b3164) at 0x182d510
    REFCNT = 2
    FLAGS = (GMG,SMG)
    IV = 0
    NV = 0
    PV = 0
    MAGIC = 0x18e3474
    MG_VIRTUAL = &PL_vtbl_defelem
    MG_TYPE = PERL_MAGIC_defelem(y)
    TYPE = y
    TARGOFF = 1
    TARGLEN = -1
    TARG = 0x1b8d8e4
    SV = PVAV(0x22b4c4) at 0x1b8d8e4
    REFCNT = 3
    FLAGS = (PADBUSY,PADMY)
    IV = 0
    NV = 0
    ARRAY = 0x20093bc
    FILL = 9
    MAX = 11
    ARYLEN = 0x0
    FLAGS = (REAL)
    Elt No. 0
    SV = IV(0x1823db8) at 0x1ffca94
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 0
    Elt No. 1
    Elt No. 2
    Elt No. 3


    ==> Dump $_ for @array


    in 'map', the same NULL SV's will keep NULL
    and get Refcounts of -2^31 plus the READONLY flag:

    SV = NULL(0x0) at 0x224b30
    REFCNT = 2147479514
    FLAGS = (READONLY)

    ==> map Dump($_), @array


    Very interesting. Thanks for
    giving some infos on the topic.

    Regards

    Mirco
    Mirco Wahab, May 17, 2007
    #6
  7. Raymundo

    Mirco Wahab Guest

    Brian McCauley wrote:
    > Note: if you think what map() does is odd then consider what happens
    > if you pass a gappy array to a subroutine! Here gap in the argument
    > leave gaps in @_ but modifying the missing element in @_ gives no
    > error but does not modify the missing element in the original array.
    >
    > sub inc {
    > for my $i ( 0.. $#_ ) {
    > $_[$i]++;
    > }
    > }


    More funny, *in* the sub, the NULL SV
    *is* autovivicated, but *after* the sub,
    the vivicated SV* isn't propagated back
    to the original array (Devel::peek 'Dump'):

    sub inc {
    $_[1]++;
    Dump($_[1]);
    }

    SV = IV(0x191f83c) at 0x1ffcc08
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 1

    - - - - - - - - - - -

    inc @array;
    Dump($array[1]);

    SV = NULL(0x0) at 0x1b93ea8
    REFCNT = 1
    FLAGS = ()

    Rergards

    Mirco
    Mirco Wahab, May 17, 2007
    #7
  8. Raymundo

    Raymundo Guest

    On 5¿ù18ÀÏ, ¿ÀÀü2½Ã28ºÐ, Brian McCauley <> wrote:
    > On May 17, 8:40 am, Raymundo <> wrote:
    >
    > > I'm sorry I'm not good at English. :)

    >
    > Good enough.
    >
    >
    >
    >
    >
    > > 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...?

    >
    > Yes it's a bug.
    >
    > The subtly is the difference between elements of an array that are
    > undef and ones that are non-existent.
    >
    > See "perldoc -f exsts".
    >
    > If I say..
    >
    > my @array2;
    > $array2[0] = 0;
    > $array2[9] = 9;
    >
    > ..then elements 1..8 of @array are not just undef but non-existent.
    >
    > In for() there is special magic to allow you to have a reference to a
    > non-existent element of an array without it becoming autovivified.
    >
    > In map() there's evidently no such magic. In the LIST argument in
    > map() any non-existent elements are replaced by the "one true undef"
    > aka PL_sv_undef. (The one you get a reference to by saying \undef).
    >
    > IMNSHO this needs to be explained in "perldoc -f map" or changed.
    >
    > Note: if you think what map() does is odd then consider what happens
    > if you pass a gappy array to a subroutine! Here gap in the argument
    > leave gaps in @_ but modifying the missing element in @_ gives no
    > error but does not modify the missing element in the original array.
    >
    > sub inc {
    > for my $i ( 0.. $#_ ) {
    > $_[$i]++;
    > }
    >
    > }
    >
    > my @array;
    > $array[1]=666;
    >
    > inc @array;
    >
    > print join ',' => @array; # ,667- µû¿Â ÅؽºÆ® ¼û±â±â -
    >
    > - µû¿Â ÅؽºÆ® º¸±â -


    Thank you, McCauley. Now I understand (I hope I do :) the situation.

    And I thank everyone who replied in this thread. I forgot to say in my
    first post but... yes, I know that the main purpose of map() is making
    a new list rather than modifying a existing array. However I thought
    it had to be supported (as perldoc -f map says) and I had no idea why
    for() and map() operate differently.

    Raymundo at South Korea
    Raymundo, May 17, 2007
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. chlori

    Gap in IE, no gap in FF

    chlori, Jan 19, 2006, in forum: HTML
    Replies:
    1
    Views:
    436
    kchayka
    Jan 19, 2006
  2. JW
    Replies:
    1
    Views:
    433
    David Jeske
    Jul 12, 2003
  3. J W
    Replies:
    0
    Views:
    382
  4. Raymundo
    Replies:
    6
    Views:
    127
    Michele Dondi
    May 17, 2007
  5. nmvega
    Replies:
    1
    Views:
    158
Loading...

Share This Page