Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Perl Misc (http://www.velocityreviews.com/forums/f67-perl-misc.html)
-   -   Idiom for array index that I'm foreach'ing over? (http://www.velocityreviews.com/forums/t884021-idiom-for-array-index-that-im-foreaching-over.html)

Tim Shoppa 12-04-2003 03:42 PM

Idiom for array index that I'm foreach'ing over?
 
OK, I'm a former (and current!) Fortran programmer. But often I end up
with Perl data structures where I not only have to step through each element of
an array, but also must know the index into the array.

I end up writing code that looks like

for my $i (0..$#a) {
my $e = $a[$i];
# Do stuff with $i and $e here
}

That's a bit ugly, I have a numeric loop, an extra variable, that funny
(0..$#a) construct, etc. And if I have to "last" to bail out of the loop
I've lost my index and the array element that I bailed out at.

I'd rather write something like

for (@a) {
# Do stuff with $_ and some magical variable telling me the index
}

in the same way that I can

while (<>) {
# Do stuff with $_ and the line number $. here
}

Is there some magical variable like $. for array loops? Any better
idioms than what I'm currently doing?

Tim.

Lack Mr G M 12-04-2003 05:09 PM

Re: Idiom for array index that I'm foreach'ing over?
 
In article <bec993c8.0312040742.2940f2ce@posting.google.com >, shoppa@trailing-edge.com (Tim Shoppa) writes:
|>
|> OK, I'm a former (and current!) Fortran programmer.

Ah, yes. Real FORTRAN programmer can write FORTRAN in any language :-).

|> But often I end up
|> with Perl data structures where I not only have to step through each element of
|> an array, but also must know the index into the array.
|>
|> I end up writing code that looks like
|>
|> for my $i (0..$#a) {
|> my $e = $a[$i];
|> # Do stuff with $i and $e here
|> }
|>
|> That's a bit ugly, I have a numeric loop, an extra variable, that funny
|> (0..$#a) construct, etc. And if I have to "last" to bail out of the loop
|> I've lost my index and the array element that I bailed out at.

So:

my $i;
for ($i = 0; $i < @a; $i++) {
my $e = $a[$i];
# Do stuff with $i and $e here
last if ($e > 20_000); # eg: exit statement
}

And now, after the loop, $i is still set to the value causing the exit

|> Is there some magical variable like $. for array loops? Any better
|> idioms than what I'm currently doing?

Well, possibly better is:

my $exiter; # So it is undef
for (my $i = 0; $i < @a; $i++) {
my $e = $a[$i];
# Do stuff with $i and $e here
if ($e > 20_000) { # eg: exit statement
$exiter = $i;
last;
}
}

And now, after the loop, $exiter is set to the value causing the exit
unless you reached the end of the array, in which case it is still
undefined.


--
--------- Gordon Lack --------------- gml4410@ggr.co.uk ------------
This message *may* reflect my personal opinion. It is *not* intended
to reflect those of my employer, or anyone else.

Anno Siegel 12-04-2003 05:56 PM

Re: Idiom for array index that I'm foreach'ing over?
 
Tim Shoppa <shoppa@trailing-edge.com> wrote in comp.lang.perl.misc:
> OK, I'm a former (and current!) Fortran programmer. But often I end up
> with Perl data structures where I not only have to step through each element of
> an array, but also must know the index into the array.
>
> I end up writing code that looks like
>
> for my $i (0..$#a) {
> my $e = $a[$i];
> # Do stuff with $i and $e here
> }
>
> That's a bit ugly, I have a numeric loop, an extra variable, that funny
> (0..$#a) construct, etc. And if I have to "last" to bail out of the loop
> I've lost my index and the array element that I bailed out at.
>
> I'd rather write something like
>
> for (@a) {
> # Do stuff with $_ and some magical variable telling me the index
> }
>
> in the same way that I can
>
> while (<>) {
> # Do stuff with $_ and the line number $. here
> }
>
> Is there some magical variable like $. for array loops? Any better
> idioms than what I'm currently doing?


Not really. The variable you want has been proposed before, right down
to the name (re-use the deprecated $#), but it isn't implemented.

A single variable would be somewhat incomplete. With nested for-loops
you'd also want a way to access the indices of enclosing loops.

If you want to keep the last index accessed, a while-loop is easier
to handle than a for-loop, because it doesn't take privacy of the
loop variable so serious:


my $i = 0;
while ( $i < @array ) {
# do something with $array[ $i];
last if bored();
$i ++; # hi Abigail
}
# use $i, it has the last value from the loop

But that's hardly an idiom, it's just a straight-forward way to do
something like that.

It may suffice to know what is left over from the array (the elements that
have *not* been processed). If so, this is a bit more perlish:

while ( @array ) {
my $element = shift @array;
# do something with $element
last if bored();
}
# now @array is what the loop left over

If you happen to know the original number of elements in @array,
"$original_length - @array" is the last index used.

I say this is "more perlish" because it uses the array as a unit. In
Fortran, an array is just a declaration, the only things you can actually
do something with are array elements, and the only way to access them is
through an index.

Perl can manipulate arrays as a whole, and doing that makes better
use of its high-level features. Once you get accustomed to this style,
you'll find that you rarely want to loop over an index. You loop over
array elements directly, and in fact map and grep do much of the looping.

If you actually need random access to elements, more often than not
the right data structure is a hash and not an array (even if the indices
happen to be numbers). With hashes, indexed access, and hence loops
over hash keys, are more frequent than with arrays, though "each" and
"values" offer alternatives.

Anno

Tim Shoppa 12-04-2003 07:23 PM

Re: Idiom for array index that I'm foreach'ing over?
 
In article <bqnsfo$ogu$1@mamenchi.zrz.TU-Berlin.DE>, Anno Siegel says...
>Perl can manipulate arrays as a whole, and doing that makes better
>use of its high-level features. Once you get accustomed to this style,
>you'll find that you rarely want to loop over an index. You loop over
>array elements directly, and in fact map and grep do much of the looping.
>
>If you actually need random access to elements, more often than not
>the right data structure is a hash and not an array (even if the indices
>happen to be numbers). With hashes, indexed access, and hence loops
>over hash keys, are more frequent than with arrays, though "each" and
>"values" offer alternatives.


I agree, hashes are great, hashes are wonderful, I love hashes, but
sometimes the data really is an ordered list.

I should add that the reason that I want to know the index of the
current element is to access the same element number in another
array. (Things like a list of x coordinates in a time series and
y coordinates in a time series and the time value of each x-y
coordinates). Given this one-to-one mapping between multiple arrays, it's
probably best to make one object (possibly hash-based object-oriented
stuff) which contains x,y, and time for each point. But it seems
silly to me to go through the whole object paradigm for a simple
ten-line program, and at the same time it seems a little awkward
that the hypothetical $# doesn't exist. There are lots of ways to
make workable programs, it's just that none of them have the
simplicity and elegance that I expect out of good modern Perl
that I'm writing :-)

If the program was bigger, I'd go the full object-oriented route
with modules etc., but it seems silly to do this for such a tiny
quick little thing.

Tim.


Ben Morrow 12-04-2003 07:27 PM

Re: Idiom for array index that I'm foreach'ing over?
 

anno4000@lublin.zrz.tu-berlin.de (Anno Siegel) wrote:
> my $i = 0;
> while ( $i < @array ) {
> # do something with $array[ $i];
> last if bored();
> $i ++; # hi Abigail


I haven't been here long enough to get the 'hi Abigail', but surely
that should be in a continue block so you can 'next'?

Also, I would be more inclined to code this as

my $i = 0;
for (@array) {
# do something with $i and $_;
} continue { $i++ }

or, as you say, completely restructure the code so I didn't need to
know what $i was at all :).

Ben

--
Like all men in Babylon I have been a proconsul; like all, a slave ... During
one lunar year, I have been declared invisible; I shrieked and was not heard,
I stole my bread and was not decapitated.
~ ben@morrow.me.uk ~ Jorge Luis Borges, 'The Babylon Lottery'

Uri Guttman 12-04-2003 08:00 PM

Re: Idiom for array index that I'm foreach'ing over?
 
>>>>> "TS" == Tim Shoppa <shoppa@trailing-edge.com> writes:

TS> I should add that the reason that I want to know the index of the
TS> current element is to access the same element number in another
TS> array. (Things like a list of x coordinates in a time series and
TS> y coordinates in a time series and the time value of each x-y
TS> coordinates). Given this one-to-one mapping between multiple
TS> arrays, it's probably best to make one object (possibly hash-based
TS> object-oriented stuff) which contains x,y, and time for each
TS> point. But it seems silly to me to go through the whole object
TS> paradigm for a simple ten-line program, and at the same time it
TS> seems a little awkward that the hypothetical $# doesn't exist.
TS> There are lots of ways to make workable programs, it's just that
TS> none of them have the simplicity and elegance that I expect out of
TS> good modern Perl that I'm writing :-)

who says you need objects for that? you need proper perl data
structures. you really have to stop thinking in fortran!! :)

perl data structures use references which are not the same as
objects. to do a 1-1 mapping of two arrays is simple without
indexes. depending on where the data cam from you can just create a
array of arrays ( [ x, y ] coordinates ) or even add the time value as
the third array element. or you could do a simple hash like:

{ x => 1.2,
y => 3.4,
time => 12335,
}

and have an array or hash of those (not sure what the hash key would
be).

TS> If the program was bigger, I'd go the full object-oriented route
TS> with modules etc., but it seems silly to do this for such a tiny
TS> quick little thing.

perl works fine with or without objects. get your mind out of that
fortran gutter where all you have are individual arrays that can only be
linked by their indexes. that is such a poor design and paradigm.

read more about perl data structures in perllol and perldsc.

uri

--
Uri Guttman ------ uri@stemsystems.com -------- http://www.stemsystems.com
--Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org

Tim Shoppa 12-04-2003 08:21 PM

Re: Idiom for array index that I'm foreach'ing over?
 
In article <x7u14gz73n.fsf@mail.sysarch.com>, Uri Guttman says...
>perl data structures use references which are not the same as
>objects. to do a 1-1 mapping of two arrays is simple without
>indexes. depending on where the data cam from you can just create a
>array of arrays ( [ x, y ] coordinates ) or even add the time value as
>the third array element. or you could do a simple hash like:
>
> { x => 1.2,
> y => 3.4,
> time => 12335,
> }
>
>and have an array or hash of those (not sure what the hash key would
>be).


All those are workable but IMHO a bit ugly compared to creating a
"point" object that has x,y, and time methods. Your last suggestion
comes close but I don't like all those curly brackets (again, my
personal taste).

I did discover Class::MethodMaker which is somewhat elegant for
this sort of stuff.

> TS> If the program was bigger, I'd go the full object-oriented route
> TS> with modules etc., but it seems silly to do this for such a tiny
> TS> quick little thing.
>
>perl works fine with or without objects. get your mind out of that
>fortran gutter where all you have are individual arrays that can only be
>linked by their indexes. that is such a poor design and paradigm.


But it's where my mind goes for any quick-and-dirty program.

For big programs I'm quite used to the object-oriented approach...
but then I find that many CPAN modules still insist on, for example,
arrays ordered the "wrong" way (e.g. GD::Graph), so I'm forced
to take my elegant objects and break them down to a bunch of arrays
("the Fortran gutter").

I think more use of Class::MethodMaker will satisfy my desire for
elegance even when quick-and-dirty. And maybe I ought to just not
use CPAN modules from the Fortran Gutter.

Tim.


Anno Siegel 12-04-2003 08:52 PM

Re: Idiom for array index that I'm foreach'ing over?
 
Ben Morrow <usenet@morrow.me.uk> wrote in comp.lang.perl.misc:
>
> anno4000@lublin.zrz.tu-berlin.de (Anno Siegel) wrote:
> > my $i = 0;
> > while ( $i < @array ) {
> > # do something with $array[ $i];
> > last if bored();
> > $i ++; # hi Abigail

>
> I haven't been here long enough to get the 'hi Abigail', but surely


Oh, that's all about a blank, the one in front of "++". Abigail endorses
that style, but not everyone follows :)

> that should be in a continue block so you can 'next'?
>
> Also, I would be more inclined to code this as
>
> my $i = 0;
> for (@array) {
> # do something with $i and $_;
> } continue { $i++ }


Sure, that's more robust. I was being sketchy.

> or, as you say, completely restructure the code so I didn't need to
> know what $i was at all :).


As we have learned (news-propagation allowing), the purpose is access
to two (or more) parallel arrays. Perl isn't particularly good at that,
though no worse than comparable languages, it just isn't needed all
that much.

If you can't avoid parallel lists, I see something like

for ( map [ $_, shift @yy], @xx ) {
my ( $x, $y) = @$_;
print "x: $x, y: $y\n";
# here we go
}

which is slightly obscure and partially destructive. Maybe an indexed
approach is okay for a ten-liner.

Anno

Anno Siegel 12-04-2003 09:22 PM

Re: Idiom for array index that I'm foreach'ing over?
 
Tim Shoppa <shoppa@trailing-edge.com> wrote in comp.lang.perl.misc:
> In article <bqnsfo$ogu$1@mamenchi.zrz.TU-Berlin.DE>, Anno Siegel says...
> >Perl can manipulate arrays as a whole, and doing that makes better
> >use of its high-level features. Once you get accustomed to this style,
> >you'll find that you rarely want to loop over an index. You loop over
> >array elements directly, and in fact map and grep do much of the looping.
> >
> >If you actually need random access to elements, more often than not
> >the right data structure is a hash and not an array (even if the indices
> >happen to be numbers). With hashes, indexed access, and hence loops
> >over hash keys, are more frequent than with arrays, though "each" and
> >"values" offer alternatives.

>
> I agree, hashes are great, hashes are wonderful, I love hashes, but
> sometimes the data really is an ordered list.
>
> I should add that the reason that I want to know the index of the
> current element is to access the same element number in another
> array. (Things like a list of x coordinates in a time series and
> y coordinates in a time series and the time value of each x-y
> coordinates). Given this one-to-one mapping between multiple arrays, it's
> probably best to make one object (possibly hash-based object-oriented
> stuff) which contains x,y, and time for each point. But it seems
> silly to me to go through the whole object paradigm for a simple
> ten-line program, and at the same time it seems a little awkward
> that the hypothetical $# doesn't exist. There are lots of ways to
> make workable programs, it's just that none of them have the
> simplicity and elegance that I expect out of good modern Perl
> that I'm writing :-)


You don't need objects just because you want an advanced data structure
in Perl. Usually it is possible to build a list of data points (triplets,
in your case) right from the input.

If, for some reason, the data is given in individual arrays, there are
many ways to pre-process them into a list of triplets. I have given an
example in another article not far from here in this thread.

The main loop would look like (untested):

for ( @data_points ) { # @data_points built elsewhere
my ( $x, $y, $t) = @{ $_}; # $_ is an array(ref) of three elements
# do things with $x, $y, $t
}

Hey, it doesn't even use a hash!

> If the program was bigger, I'd go the full object-oriented route
> with modules etc., but it seems silly to do this for such a tiny
> quick little thing.


Well, that's one of the beauties of Perl that you can often use just
enough of it to fit your needs. In this case, an array-ref neatly
encapsulates your data. This is just an alternative to Uri's hashes-used-
as-records.

Anno

Uri Guttman 12-04-2003 09:27 PM

Re: Idiom for array index that I'm foreach'ing over?
 
>>>>> "TS" == Tim Shoppa <shoppa@trailing-edge.com> writes:

>> { x => 1.2,
>> y => 3.4,
>> time => 12335,
>> }
>>
>> and have an array or hash of those (not sure what the hash key would
>> be).


TS> All those are workable but IMHO a bit ugly compared to creating a
TS> "point" object that has x,y, and time methods. Your last suggestion
TS> comes close but I don't like all those curly brackets (again, my
TS> personal taste).

but you don't speak perl so you taste is not relevent. even with objects
you need to understand perl refs.

TS> I did discover Class::MethodMaker which is somewhat elegant for
TS> this sort of stuff.

overkill. you just said you didn't want to go OO for this. make up your
mind. and you still need to learn refs.

TS> If the program was bigger, I'd go the full object-oriented route
TS> with modules etc., but it seems silly to do this for such a tiny
TS> quick little thing.

that makes no sense at all.

>> perl works fine with or without objects. get your mind out of that
>> fortran gutter where all you have are individual arrays that can only be
>> linked by their indexes. that is such a poor design and paradigm.


TS> But it's where my mind goes for any quick-and-dirty program.

if you want to get better at perl, then you should stop thinking in
fortran.

TS> For big programs I'm quite used to the object-oriented approach...
TS> but then I find that many CPAN modules still insist on, for example,
TS> arrays ordered the "wrong" way (e.g. GD::Graph), so I'm forced
TS> to take my elegant objects and break them down to a bunch of arrays
TS> ("the Fortran gutter").

huh?

TS> I think more use of Class::MethodMaker will satisfy my desire for
TS> elegance even when quick-and-dirty. And maybe I ought to just not
TS> use CPAN modules from the Fortran Gutter.

huh?

uri

--
Uri Guttman ------ uri@stemsystems.com -------- http://www.stemsystems.com
--Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org


All times are GMT. The time now is 01:27 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.