Reputation: 81
I have a flat array of coordinates and I want to iterate and extract pairs of x and y coordinates. The same logic could apply to triples for RGB colors. This is what I have so far, but it doesn't feel super flexible or elegant.
my @coords = qw(1 5 2 6 3 8 6 12 7 5);
for (my $i = 0; $i < @coords; $i += 2) {
my $x = $coords[$i];
my $y = $coords[$i+1];
print "$x, $y\n";
}
There has to be a better way to do this right?
Upvotes: 8
Views: 429
Reputation: 1350
I like to use splice without overworking it by passing the result through as the test, and also add a guard condition:
@triples % 3 and die "TRIPLES ARRAY NOT MULTIPLE OF 3 IN LENGTH";
while (@triples){
my ($foo, $bar, $baz) = splice @triples, 0, 3;
...
}
in case you need @triples to survive getting consumed by the ellided code within the loop, make copies, either of the whole array or with offsets while incrementing an index. TMTOWTDI.
Upvotes: 2
Reputation: 1452
You can use shift
in a while
loop.
#!/usr/bin/perl
my @coords = qw/1 5 2 6 3 8 6 12 7 5/;
my ($x, $y);
while (@coords) {
$x = shift @coords;
$y = shift @coords;
# another shift to get triples
# Do something with $x, $y, ...
say "$x, $y";
}
The while loop runs until @coords
is empty. shift
gets the first element out and deletes it from the array.
undef
by giving a default value:If you try above with triples, you'll get an error because there will be undefined values in the last run. $x
will be 5
and then @coords
is empty.
So give a default value using //
.
#!/usr/bin/perl
my @coords = qw/1 5 2 6 3 8 6 12 7 5/;
my ($x, $y, $z);
while (@coords) {
$x = shift @coords; # is defined!
$y = shift @coords // "<undef>";
$z = shift @coords // "<undef>";
# ...
}
Don't use ||
! Because shift @coords || "<undef>";
will be "<undef>"
if the current value is evaluated to false (0
, ""
, ...).
Upvotes: 4
Reputation: 118595
splice
is a better way
while (my ($x,$y) = splice @coords, 0, 2) {
...
}
Two things to note.
splice
consumes the elements of @coords
. If you don't want your loop to destroy the contents of your array, use a temporary array.
my @tmp = @coords;
while (my ($x,$y) = splice @tmp,0,2) { ... }
If the input might not contain an even number of elements, you may want to add an additional check to make sure each iteration has access to the right number of elements
while (2 == (my ($x,$y) = splice @coords,0,2)) { ... }
Upvotes: 5
Reputation: 6798
An old school aproach to the problem
use strict;
use warnings;
use feature 'say';
my @coords = qw(1 5 2 6 3 8 6 12 7 5);
my($x,$y);
while( ($x,$y,@coords) = @coords ) {
say "$x, $y";
}
Output
1, 5
2, 6
3, 8
6, 12
7, 5
Upvotes: 4
Reputation: 66883
The module List::MoreUtils has natatime
(n-at-a-time)
use List::MoreUtils qw(natatime);
my @ary = 1..12;
my $it = natatime 3, @ary; # iterator
while (my @triplet = $it->()) { say "@triplet" }
Upvotes: 7
Reputation: 52344
You can use pairs
from the core List::Util
module to turn an even-number of elements list into a list of two-element array refs:
#!/usr/bin/env perl
use warnings;
use strict;
use List::Util qw/pairs/;
my @coords = qw(1 5 2 6 3 8 6 12 7 5);
for my $pair (pairs @coords) {
my ($x, $y) = @$pair;
# ...
}
Upvotes: 4