Re: Perlplexation - foreach shoulda Known
by toolic (Bishop) on Apr 13, 2012 at 19:52 UTC
|
| [reply] [Watch: Dir/Any] |
Re: Perlplexation - foreach shoulda Known
by eyepopslikeamosquito (Archbishop) on Apr 14, 2012 at 11:17 UTC
|
The foreach (and for and map, etc) command can be used to modify the list that it is iterating over
I recommend the Effective Perl Programming book to you and your workmates, especially item 20, "Use foreach, map and grep as appropriate", which gives an excellent summary of when to use foreach, map and grep:
- Use foreach to iterate read-only over each element of a list
- Use map to create a list based on the contents of another list
- Use foreach to modify elements of a list
- Use grep to select elements in a list
I've often asked new Perl programmers at work to read this item when I find them writing inappropriate C-style for loops or discover they are confused about foreach and have never even used map or grep.
Note that Hall, McAdams and foy further caution against modifying a list via map:
"For efficiency, $_ is actually an alias for the current element in the iteration. If you modify $_ within the transform expression of a map, you modify the input data. This is generally considered to be bad style, and -- who knows? -- you may even wind up confusing yourself this way. If you want to modify the contents of a list, use foreach."
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
| [reply] [Watch: Dir/Any] |
|
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
This is generally considered to be bad style, and -- who knows? -- you may even wind up confusing yourself this way. If you want to modify the contents of a list, use foreach.
You know, I can understand people who say "I find modifying list elements using an alias confusing", and I can understand people who say "I'm fine using all the features Perl gives me, including list aliasing". I don't know what to think of people who say "I'm fine with modifying list elements in a block, but only if the keyword is foreach, if the keyword is map, it confuses me". Those aren't the people I would want to hire.
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Perlplexation - foreach shoulda Known
by Riales (Hermit) on Apr 13, 2012 at 19:58 UTC
|
Sort of on the same topic (but maybe a bit in the opposite direction), does anybody know if there is a special variable that keeps count of which iteration in a for loop it's on?
Say I want to do something to every third element:
my $i = 0;
foreach my $element (@array) {
do_something($element) unless ($i++ % 3);
}
Is there a special variable that tracks what $i is tracking in that example? | [reply] [Watch: Dir/Any] [d/l] [select] |
|
while (my ($i, $element) = each @array)
{
do_something( $element ) unless $i % 3;
}
| [reply] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
|
|
|
Is there a special variable that tracks what $i is tracking in that example?
Curiously, this is very easy in Python:
x = [ 'apple', 'banana', 'orange' ]
for i, val in enumerate(x): print i, val
which prints:
0 apple
1 banana
2 orange
and fairly easy in Ruby:
x = [ 'apple', 'banana', 'orange' ]
x.each_with_index { |val, i| print "#{i} #{val}\n" }
and I'm sure (need to wait for moritz to show me how) it's
easy in Perl 6 too (probably via Array kv and/or pairs methods?).
I was hoping List::Util or List::MoreUtils
might have something nice, but the best I could find is
to use an iterator like so:
use List::MoreUtils qw(each_array);
my @x = ( 'apple', 'banana', 'orange' );
my $it = each_array( @{[0..$#x]}, @x );
while ( my ($i, $val) = $it->() ) {
print "$i $val\n";
}
which is horrific.
Is there a better way in List::Util or List::MoreUtils
that I missed?
While I was writing this, chromatic showed how
to do it in Perl 5.12 or above:
my @x = ( 'apple', 'banana', 'orange' );
while ( my ($i, $val) = each @x ) {
print "$i $val\n";
}
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
eyepopslikeamosquito writes:
Curiously, this is very easy in Python...
And Riales's task too: remember -- slices win ;-)
for elem in some_iterable[::3]:
do_something(elem)
Then again, I'd argue that it's really not so curious given how much iteration support/use/avoidance is baked-in (iterator and sequence protocols, generators, comprehensions, slices, etc). | [reply] [Watch: Dir/Any] [d/l] |
|
foreach my $idx ( 0 .. $#array ) {
do_something($array[$idx]) unless $idx % 3;
# Do something else that isn't filtered by iteration number.
}
In your example, it's probably clearer to iterate over the list rather than the indices, but in some cases the code becomes clearer the other way.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Is there a special variable that tracks what $i is tracking in that example?
No. If you want that, use a C-style for loop. A reason not to have an special variable with an interation counter: how should that work with nested loops?
| [reply] [Watch: Dir/Any] |
|
| [reply] [Watch: Dir/Any] [d/l] |
|
|
|
Re: Perlplexation - foreach shoulda Known
by tinita (Parson) on Apr 14, 2012 at 09:46 UTC
|
I guess I just assumed it didn't because when you declare the variable in a foreach loop, you don't think of it as a reference (because it really isn't one, but it sorta acts like one in that it represents the actual element in the list.)
It is called an alias.
| [reply] [Watch: Dir/Any] |