Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Array slices: beyond the end/ Assigning an empty list to a Hash slice

by 7stud (Deacon)
on Mar 22, 2014 at 01:16 UTC ( [id://1079369]=perlquestion: print w/replies, xml ) Need Help??

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

Dear Monks,

1) From perldata/Slices:
A slice of an empty list is still an empty list. Thus: @a = ()[1,0]; # @a has no elements @b = (@a)[0,1]; # @b has no elements But: @a = (1)[1,0]; # @a has two elements @b = (1,undef)[1,0,2]; # @b has three elements More generally, a slice yields the empty list if it indexes only beyon +d the end of a list: @a = (1)[ 1,2]; # @a has no elements @b = (1)[0,1,2]; # @b has three elements

Why is indexing beyond the end of the list legal for a slice?

2) I think the slice docs should mention the case when you assign an empty list to a hash slice, like this:
use strict; use warnings; use 5.014; use Data::Dumper; my %hash = ('hello', 1, 'goodbye', 2, 'world', 3, 'mars', 4); @hash{'goodbye', 'world'} = (); say Dumper(\%hash); --output:-- $VAR1 = { 'mars' => 4, 'hello' => 1, 'world' => undef, 'goodbye' => undef };
It's not obvious(to me at least) why the value undef gets assigned to the sliced keys. It seems to me that the hash could just as well look like:
$VAR1 = { 'mars' => 4, 'hello' => 1, };
Maybe it's that way for hashes in order to be consistent with assigning an empty list to an array slice:
my @arr = (10, 20, 30, 40); @arr[1, 2] = (); say Dumper(\@arr); --output:-- $VAR1 = [ 10, undef, undef, 40 ];

Replies are listed 'Best First'.
Re: Array slices: beyond the end/ Assigning an empty list to a Hash slice
by kcott (Archbishop) on Mar 22, 2014 at 04:36 UTC

    G'day 7stud,

    "1) From perldata/Slices: ... Why is indexing beyond the end of the list legal for a slice?"

    Your question here seems to imply an assumption. This might be:

    "Why is indexing beyond the end of the list legal for a slice when it's illegal for an array?"

    which would be a false assumption.

    You may want to correct or clarify that; however, in case that is the assumption you're making, consider this code:

    #!/usr/bin/env perl -l use strict; use warnings; print '*** Create @x with no elements ***'; my @x; print 'Elements in @x: ', scalar @x; print '*** Use array index beyond end as RHS of assignment ***'; my $y = $x[42]; print 'Value assigned from beyond end of @x: ', defined $y ? $y : '<un +def>'; print 'Elements in @x: ', scalar @x; print '*** Use array index beyond end as LHS of assignment ***'; $x[21] = 'hello'; for my $i (0, 10, 21, 32) { print "Index: $i"; print 'Value: ', defined $x[$i] ? $x[$i] : '<undef>'; } print 'Elements in @x: ', scalar @x;

    Output:

    *** Create @x with no elements *** Elements in @x: 0 *** Use array index beyond end as RHS of assignment *** Value assigned from beyond end of @x: <undef> Elements in @x: 0 *** Use array index beyond end as LHS of assignment *** Index: 0 Value: <undef> Index: 10 Value: <undef> Index: 21 Value: hello Index: 32 Value: <undef> Elements in @x: 22
    "2) I think the slice docs should mention the case when you assign an empty list to a hash slice, like this:"

    I can't find any specific mention of that in perldata. Someone else may be able to point you to another manual page that does explain this. In any event, you can always use perlbug to request changes or additions to documentation.

    "It's not obvious(to me at least) why the value undef gets assigned to the sliced keys. It seems to me that the hash could just as well look like:"

    Instead of the hash slice assignment:

    @hash{'goodbye', 'world'} = ();

    Consider the individual assignments to each key:

    #!/usr/bin/env perl use strict; use warnings; use Data::Dumper; my %hash = ('hello', 1, 'goodbye', 2, 'world', 3, 'mars', 4); print Dumper \%hash; $hash{goodbye} = ()[0]; $hash{world} = ()[1]; print Dumper \%hash;

    Output:

    $VAR1 = { 'hello' => 1, 'goodbye' => 2, 'world' => 3, 'mars' => 4 }; $VAR1 = { 'hello' => 1, 'goodbye' => undef, 'world' => undef, 'mars' => 4 };

    That may help in understanding what is going on when an empty list is assigned to a hash slice.

    Here's a further example where the list being assigned has less elements than the hash slice has keys:

    #!/usr/bin/env perl use strict; use warnings; use Data::Dumper; my %hash = ('hello', 1, 'goodbye', 2, 'world', 3, 'mars', 4); print Dumper \%hash; @hash{'goodbye', 'world'} = ('farewell'); print Dumper \%hash;

    Output:

    $VAR1 = { 'hello' => 1, 'goodbye' => 2, 'world' => 3, 'mars' => 4 }; $VAR1 = { 'hello' => 1, 'goodbye' => 'farewell', 'world' => undef, 'mars' => 4 };

    -- Ken

      Your question here seems to imply an assumption. This might be: "Why is indexing beyond the end of the list legal for a slice when it's illegal for an array?

      Yes, I think I was confused by that because I did this:
      use strict; use warnings; use 5.014; my @arr = (); if (exists $arr[0]) { say 'yes'; } else { say 'no'; } say $arr[0]; --output:-- no Use of uninitialized value in say at 1.pl line 15.
      And the error convinced me that you can't index beyond the end of the array, but as you pointed out and as "Learning Perl" (6th) says on p. 45:

      If the subscript indicates an element that would be beyond the end of the array, the corresponding value will be undef.

      For comparison:

      my @arr = (undef); if (exists $arr[0]) { say 'yes'; } else { say 'no'; } say $arr[0]; --output:-- yes Use of uninitialized value $arr[0] in say at 1.pl line 14.
Re: Array slices: beyond the end/ Assigning an empty list to a Hash slice
by CountZero (Bishop) on Mar 22, 2014 at 12:54 UTC
    As the Ancients already said: "ex nihilo nihil fit". From an empty list, nothing comes. As can be read in perldata:
    A slice of an empty list is still an empty list.

    But a slice of an empty array (or beyond the present size of the array) is a list of undefs.

    So:

    • @slice = ()[1,0,2];       # @slice is empty
    • @slice = (@slice)[1,0,2]; # @slice is still empty
    • @slice = @slice[1,0,2];   # now @slice contains three undefs

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

    My blog: Imperial Deltronics
      Right, it's just that the following is a bit confusing:
      @b = (1..3)[3, 4]; # "empty" slice of a non-empty list; # @b is empty, which is fine @b = (1..3)[2, 3]; # the slice is "half empty", # and yet @b now has TWO elements, not one
        I understand you find it confusing, but there is a valid reason for it.

        The basic principle is that you get a slice of exactly the length you asked it to be, padded with undefs if necessary, UNLESS the whole slice would have to be padded with undefs, in which case you get an empty list which is FALSE in any boolean test.

        Actually it is a clear application of the principle of least surprise:

        • It is the simplest way to distinguish between a slice from an empty list and a slice of a list of undefs. The former is FALSE, the latter is TRUE in a test.
        • The slice returns you exactly the number of elements you asked it to return, not some unexpected or unpredictable smaller number.

        Sometimes I like to think that the authors of Perl have thought long and hard and deep about all these issues. It is a comforting thought.

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

        My blog: Imperial Deltronics
Re: Array slices: beyond the end/ Assigning an empty list to a Hash slice
by hazylife (Monk) on Mar 22, 2014 at 12:44 UTC
    The most important bit of the first part of your question is probably this:
    More generally, a slice yields the empty list if it indexes only beyond the end of a list
    And yeah, this does scream inconsistency to me!
    @a = (1)[1,0]; # @a has two elements # OK, this one is fine # but what about these: @b = (1,undef)[1,0,2]; # @b has three elements # where does that third element come from? # or we could rewrite the above B case like this # and still get three elements: @b = (1)[1,0,2]; # why three and not *TWO*? @a = (1)[ 1,2]; # @a has no elements # !?
    Great question, thanks for bringing this up.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1079369]
Approved by davido
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (4)
As of 2024-03-29 08:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found