Using undef as an array subscript

  • Thread starter Yehuda Berlinger
  • Start date
Y

Yehuda Berlinger

This one is a surprise:

@a = (0,undef);
@b = (1,2);

@c[@a] = @b;

I was hoping to see $c[0] == '1', but I got '2'. Apparently, undef is
converted to '0' when used as an array subscript. Is this documented
anywhere, or is it something too obvious to document? I coudn't find
it in the usual places.

I was hoping to avoid doing a loop such as:

@bb = @b;
foreach $a (@a) {
my $b = shift @bb;
next unless defined $a;
$c[$a] = $b;
}

Any thoughts?

Yehuda
 
G

Greg Bacon

: This one is a surprise:
:
: @a = (0,undef);
: @b = (1,2);
:
: @c[@a] = @b;
:
: I was hoping to see $c[0] == '1', but I got '2'. Apparently, undef is
: converted to '0' when used as an array subscript. Is this documented
: anywhere, or is it something too obvious to document? I coudn't find
: it in the usual places.

Well, it's defined in the perldiag manpage:

Use of uninitialized value%s
(W uninitialized) An undefined value was used as if it
were already defined. It was interpreted as a "" or a
0, but maybe it was a mistake. To suppress this
warning assign a defined value to your variables.

Certainly this is documented elsewhere, but a quick glance didn't bear
fruit.

: I was hoping to avoid doing a loop such as:
:
: @bb = @b;
: foreach $a (@a) {
: my $b = shift @bb;
: next unless defined $a;
: $c[$a] = $b;
: }
:
: Any thoughts?

Can we step up a level conceptually? What are you trying to do?

Greg
 
J

JS Bangs

Yehuda Berlinger sikyal:
This one is a surprise:

@a = (0,undef);
@b = (1,2);

@c[@a] = @b;

I was hoping to see $c[0] == '1', but I got '2'. Apparently, undef is
converted to '0' when used as an array subscript. Is this documented
anywhere, or is it something too obvious to document? I coudn't find
it in the usual places.

I was hoping to avoid doing a loop such as:

@bb = @b;
foreach $a (@a) {
my $b = shift @bb;
next unless defined $a;
$c[$a] = $b;
}

Any thoughts?

Since your goal is apparently to use the contents of @a to define
subscripts of @c, you could just filter the undefs out of @a first:

@a = grep { defined($_) } @a;

No messy loop, no undefs to mess up your subscripting. You could even put
the whole thing inside the subscript definition for @c, if you don't mind
being slightly obtuse:

@c[grep { defined($_) } @a] = @b;


Jesse S. Bangs (e-mail address removed)
http://students.washington.edu/jaspax/
http://students.washington.edu/jaspax/blog

Jesus asked them, "Who do you say that I am?"

And they answered, "You are the eschatological manifestation of the ground
of our being, the kerygma in which we find the ultimate meaning of our
interpersonal relationship."

And Jesus said, "What?"
 
J

JS Bangs

Greg Bacon sikyal:
: @c[grep { defined($_) } @a] = @b;

That doesn't throw away the corresponding elements of @b.

???? I don't understand what you mean by "throw away the corresponding
elements of @b". Based on the original code we saw, the OP doesn't seem to
want to alter @b at all, but only to make @c into a subset of @b. But
perhaps I've missed something. Can the OP herself clarify?


Jesse S. Bangs (e-mail address removed)
http://students.washington.edu/jaspax/
http://students.washington.edu/jaspax/blog

Jesus asked them, "Who do you say that I am?"

And they answered, "You are the eschatological manifestation of the ground
of our being, the kerygma in which we find the ultimate meaning of our
interpersonal relationship."

And Jesus said, "What?"
 
M

Matija Papec

X-Ftn-To: Yehuda Berlinger

@a = (0,undef);
@b = (1,2);

@c[@a] = @b;

I was hoping to see $c[0] == '1', but I got '2'. Apparently, undef is
converted to '0' when used as an array subscript. Is this documented
anywhere, or is it something too obvious to document? I coudn't find
it in the usual places.

I was hoping to avoid doing a loop such as:

@bb = @b;
foreach $a (@a) {
my $b = shift @bb;
next unless defined $a;
$c[$a] = $b;
}

Any thoughts?

you could,
$c[0] = $b[
(grep defined $a[$_] && !$a[$_], 0..$#a)[-1]
];
just after @c[@a] = @b;

but after this, foreach looks very tempting ;)

btw, why are you having undefs in @a in the first place?
 
M

Matija Papec

X-Ftn-To: JS Bangs

JS Bangs said:
: @c[grep { defined($_) } @a] = @b;

That doesn't throw away the corresponding elements of @b.

???? I don't understand what you mean by "throw away the corresponding
elements of @b". Based on the original code we saw, the OP doesn't seem to
want to alter @b at all, but only to make @c into a subset of @b. But
perhaps I've missed something. Can the OP herself clarify?

@a and @b, each have same number of elements, so when removing from @a, one
should remove corresponding element from @b, so @c[@a] = @b could work as
expected.
 
E

Eric Schwartz

JS Bangs said:
???? I don't understand what you mean by "throw away the corresponding
elements of @b".

The OP's code always shifted off an entry from @b (well, from a copy
of it named @bb) for every element of @a, even if that element was
undefined. Your code assumes that

scalar(@b) == scalar(grep { defined($_) } @a )

which is not necessarily true.
Based on the original code we saw, the OP doesn't seem to
want to alter @b at all, but only to make @c into a subset of @b. But
perhaps I've missed something. Can the OP herself clarify?

Based on the original code, the idea is that @c contains all the
elements of $b for which the corresponding element in $a is defined.
I think Greg's point was that you forgot that @b may contain elements
that correspond to undefined elements in @a, and thus shouldn't be
included.

To the OP: You can adapt JS' code to not explicitly require a loop,
but a loop is going to happen anyway, whether you make it explicit or
not. I think you'll find that using the loop code is going to be more
readable and maintainable than more arcane trickery.

-=Eric
 
G

Greg Bacon

: [...]
:
: I was hoping to avoid doing a loop such as:
:
: @bb = @b;
: foreach $a (@a) {
: my $b = shift @bb;
: next unless defined $a;
: $c[$a] = $b;
: }
:
: Any thoughts?

You could do it this way:

#! /usr/local/bin/perl

use warnings;

use Data::Dumper;

sub zip {
use strict;

my @a = @{ $_[0] || [] };
my @b = @{ $_[1] || [] };

my @result;
push @result => [shift @a, shift @b] while @a || @b;

@result;
}

@a = (0, undef);
@b = (1, 2);

$c[ $_->[0] ] = $_->[1] for grep defined($_->[0]), zip \@a, \@b;
print Dumper \@c;

Don't do it that way, though; it has little value outside a puzzle or
challenge. If we had a better idea of what you're trying to model,
we might be able to offer better suggestions.

Perl 6 will have parallel traversal, so you'll someday be able to

for @a; @b -> $a; $b {
@c[$a] = $b if defined $a;
}

Greg
 
G

Greg Bacon

: Perl 6 will have parallel traversal, so you'll someday be able to
:
: for @a; @b -> $a; $b {
: @c[$a] = $b if defined $a;
: }

Sorry, no reference. The foreshadowing comes from Apocalypse 4,
RFC 173. The URL is hideous, so visit

http://makeashorterlink.com/?X6E411E15

Greg
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top