How to make an array loop indefinitely? - arrays

I have a list of names:
#names = qw(John Peter Michael);
I want to take 2 values from it, so I get John and Peter. If I want to take 2 more - I get Michael and John. 1 more - Peter. 3 more - Michael John and Peter, and so on.
I've started writing a subroutine where a global index ID would be set and remembered, and would reset itself to zero once it reaches scalar limit of an array, but then I read somewhere that Perl arrays "remember" the position they were looped.
Is that true or am I misunderstanding something? Is there a way to do my task an easy way?

It's not that hard to roll your own iterator, but perlfaq4 has your need covered:
How do I handle circular lists?
(contributed by brian d foy)
If you want to cycle through an array endlessly, you can increment the
index modulo the number of elements in the array:
my #array = qw( a b c );
my $i = 0;
while( 1 ) {
print $array[ $i++ % #array ], "\n";
last if $i > 20;
}
You can also use Tie::Cycle to use a scalar that always has the next element of the circular array:
use Tie::Cycle;
tie my $cycle, 'Tie::Cycle', [ qw( FFFFFF 000000 FFFF00 ) ];
print $cycle; # FFFFFF
print $cycle; # 000000
print $cycle; # FFFF00
The Array::Iterator::Circular creates an iterator object for circular arrays:
use Array::Iterator::Circular;
my $color_iterator = Array::Iterator::Circular->new(
qw(red green blue orange)
);
foreach ( 1 .. 20 ) {
print $color_iterator->next, "\n";
}
The roll-your-own variety
The subroutine is really quite simple (implemented as circularize in the code below). The value of $i is retained in $colors's scope, so no need for state variables:
sub circularize {
my #array = #_;
my $i = 0;
return sub { $array[ $i++ % #array ] }
}
my $colors = circularize( qw( red blue orange purple ) ); # Initialize
print $colors->(), "\n" for 1 .. 14; # Use

I never fully understood that mechanism (is it only on foreach?). I would just use state values, e.g.:
my #names = qw(John Peter Michael);
sub GetNames($) {
my $count = shift;
my #result = ();
state $index = 0;
state $length = scalar(#names);
while($count--) {
push(#result, $names[($index++ % $length)]);
}
return #result;
}
print join(", ", GetNames(2)), "\n\n";
print join(", ", GetNames(4)), "\n";
Outputs:
John, Peter
Michael, John, Peter, Michael

Related

For Loop Issues in creating nested array

Creating a matrix of products for three element arrays. I understand Perl does not have multi-dimensional arrays and are flattened. I have been using refs but I can't seem to get past the for loop issue in getting three products into a single array and pushing that array into a different single array. And I could be way off too. Be nice, but I've spent too many hours on this.
I have moved values inside and out of various places i.e. { }, printed out variables until I'm blue and used $last all over for debugging. I'm likely fried at this point.
use strict;
use warnings;
my #array1 = (1, 2, 3);
my #array2 = (2, 4, 6);
my #matrixArray = ();
my $matrixArray;
my #row;
my #finalArray = maths(\#array1, \#array2);
print #finalArray;
sub maths{
my $array1ref = shift;
my $array2ref = shift;
my $value1;
my $value2;
my $maths;
my #row = ();
my #array1 = #{$array1ref};
my #array2 = #{$array2ref};
my $len1 = #array1;
my $len2 = #array2;
for my $x (0 ..($len1 -1)){
#iterate through first array at each value
$value1 = $array1[$x];
#print $value1, " value1 \n";
for my $y (0 .. ($len2 -1)){
#iterate through second array at each value
$value2 = $array2[$y];
#print $value2, " value2 \n";
#calculate new values
$maths = $value1 * $value2;
#exactly right here
#print $maths, " maths \n" ;
push #row, $maths;
}
}
#and exactly right here but not set of arrays
#print #row, "\n";
return #row;
}
Currently I'm able to get this: 246481261218. Which is the correct dumb math but...
it should appear as a matrix:
2 4 6
4 8 12
6 12 18
I am not passing three arrays so it seems my issue is up in the sub routine before I can get on with anything else. This seems to be a theme that I often miss. So sorry if I sound inept.
EDIT***
This was working but I couldn't unpack it
use strict;
use warnings;
my #array1 = (1, 2, 3);
my #array2 = (2, 4, 6);
my #matrixArray = ();
maths(\#array1, \#array2);
foreach my $x (#matrixArray){
print "$x \n";
}
sub maths{
my $array1ref = shift;
my $array2ref = shift;
my $value1;
my $value2;
my $maths;
my #row = ();
my $row;
my #array1 = #{$array1ref};
my #array2 = #{$array2ref};
my $len1 = #array1;
my $len2 = #array2;
for my $x (0 ..($len1 -1)){
#iterate through first array at each value
$value1 = $array1[$x];
for my $y (0 .. ($len2 -1)){
#iterate through second array at each value
$value2 = $array2[$y];
#calculate new values
$maths = $value1 * $value2;
push #row, $maths;
$row = \#row;
}
push #matrixArray, $row;
}
return #matrixArray;
}
The output right after the function call is this:
ARRAY(0x55bbe2c667b0)
ARRAY(0x55bbe2c667b0)
ARRAY(0x55bbe2c667b0)
which would be the (line 10) print of $x.
****EDIT
This Works (almost):
print join(" ", #{$_}), "\n" for #matrixArray;
Output is a bit wrong...
2 4 6 4 8 12 6 12 18
2 4 6 4 8 12 6 12 18
2 4 6 4 8 12 6 12 18
And of note: I knew $x was an array but I seemed to run into trouble trying to unpack it correctly. And I'm no longer a fan of Perl. I'm pining for the fjords of Python.
And *****EDIT
This is working great and I get three arrays out of it:
sub maths{
my ($array1, $array2) = #_;
my #res;
for my $x (#$array1) {
my #row;
for my $y (#$array2) {
push #row, $x * $y;
}
push #res, \#row;
}
#This is the correct structure on print #res!
return #res;
}
But, though it's putting it together correctly, I have no output after the call
maths(\#array1, \#array2);
NOTHING HERE...
print #res;
print join(" ", #{$_}), "\n" for #res;
foreach my $x (#res){
print join(" ", #{$x}), "\n";
}
And of course a million thanks! I regret taking this stupid course and fear my grade will eventually do me in. Still pining for Python!
It appears that you need a matrix with rows obtained by multiplying an array by elements of another.
One way
use warnings;
use strict;
use Data::Dump qw(dd);
my #ary = (2, 4, 6);
my #factors = (1, 2, 3);
my #matrix = map {
my $factor = $_;
[ map { $_ * $factor } #ary ]
} #factors;
dd #matrix;
The array #matrix, formed by the outer map, has array references for each element and is thus (at least) a two-dimensional structure (a "matrix"). Those arrayrefs are built with [ ], which creates an anonymous array out of a list inside. That list is generated by map over the #ary.
I use Data::Dump to nicely print complex data. In the core there is Data::Dumper.
With a lot of work like this, and with large data, efficiency may matter. The common wisdom would have it that direct iteration should be a bit faster than map, but here is a benchmark. This also serves to show more basic ways as well.
use warnings;
use strict;
use feature 'say';
use Benchmark qw(cmpthese);
my $runfor = shift // 5; # run each case for these many seconds
sub outer_map {
my ($ary, $fact) = #_;
my #matrix = map {
my $factor = $_;
[ map { $_ * $factor } #$ary ]
} #$fact;
return \#matrix;
}
sub outer {
my ($ary, $fact) = #_;
my #matrix;
foreach my $factor (#$fact) {
push #matrix, [];
foreach my $elem (#$ary) {
push #{$matrix[-1]}, $elem * $factor;
}
}
return \#matrix;
}
sub outer_tmp {
my ($ary, $fact) = #_;
my #matrix;
foreach my $factor (#$fact) {
my #tmp;
foreach my $elem (#$ary) {
push #tmp, $elem * $factor;
}
push #matrix, \#tmp;
}
return \#matrix;
}
my #a1 = map { 2*$_ } 1..1_000; # worth comparing only for large data
my #f1 = 1..1_000;
cmpthese( -$runfor, {
direct => sub { my $r1 = outer(\#a1, \#f1) },
w_tmp => sub { my $r2 = outer_tmp(\#a1, \#f1) },
w_map => sub { my $r3 = outer_map(\#a1, \#f1) },
});
On a nice machine with v5.16 this prints
Rate direct w_map w_tmp
direct 11.0/s -- -3% -20%
w_map 11.4/s 3% -- -17%
w_tmp 13.8/s 25% 21% --
The results are rather similar on v5.29.2, and on an oldish laptop.
So map is a touch faster than building a matrix directly, and 15-20% slower than the method using a temporary array for rows, which I'd also consider clearest. The explicit loops can be improved a little by avoiding scopes and scalars, and the "direct" method can perhaps be sped up some by using indices. But these are dreaded micro-optimizations, and for fringe benefits at best.
Note that timings such as these make sense only with truly large amounts of data, what the above isn't. (I did test with both dimensions ten times as large, with very similar results.)
The second program is mostly correct.
The problem is that you didn't unpack the second level of the array.
foreach my $x (#matrixArray){
print "$x \n";
}
should be something like:
foreach my $x (#matrixArray) {
print join(" ", #{$x}), "\n";
}
# or just:
print join(" ", #{$_}), "\n" for #matrixArray;
Your maths function can be made shorter without losing legibility (it may actually make it more legible) by cutting out unnecessary temporary variables and indexing. For example:
sub maths {
my #array1 = #{ $_[0] };
my #array2 = #{ $_[1] }; # or: ... = #{ (shift) };
my #res = ();
for my $x (#array1) {
my #row = (); # <-- bugfix of original code
for my $y (#array2) {
my $maths = $x * $y;
push #row, $maths;
}
push #res, \#row;
}
return #res;
}

Printing "Multi-Dimensional" Array in Perl

I am having a problem attempting to print an array that contains arrays. When printing the array #dev which contains the other arrays, I am only managing to print the first three as it is indicated by the #printing in-line comments. The commented line #print($dev[4][2]); works fine, as well as any of the other combination of numbers within the allowable range. For some reason the for loop does not work. Help!?
my #dev;
my #tf;
my #a;
my #t;
my #f;
my #ofv;
my #tfv;
#tf = ('123456787', '123456788', '123456789'); #printing
#a = (78, 65, 57); #printing
#t = (70, 55, 42); #printing
#f = (77, 64, 56);
#ofv = ('true', 'false', 'false');
#tfv = ('false', 'true', 'true');
#dev = (
[#tf],
[#a],
[#t],
[#f],
[#ofv],
[#tfv],
);
#print($dev[4][2]);
for (my $i = 0; $i <= (scalar(#tf) - 1); $i++) {
for (my $j = 0; $j <= (scalar(#dev) - 1); $j++) {
print($dev[$i][$j]);
print("\n");
}
}
Thank you.
If you just want to show the data of such complex data struct, the modules Data::Dumper or Smart::Comments may be good options.
use Data::Dumper;
print Dumper(\#dev);
or
use Smart::Comments;
### #dev
The output is much more perl-style and not that readable, but is quite convenient to show the struct of such complex data.
Perl can be quite compact.
This snippet of code do the same thing for my arrays #arr1, #arr2 and #arr3:
#arr1 = (1..10);
#arr2 = ('a'..'j');
#arr3 = ('.') x 10;
#multid = \(#arr1, #arr2, #arr3);
print "#$_\n" for (#multid);
OUTPUT:
1 2 3 4 5 6 7 8 9 10
a b c d e f g h i j
. . . . . . . . . .
Also the [] copies an array and gives a reference to it (It's an anonymous array in memory, regardless of the array, a copy of which he is). If there is no need to such duplicate, it is better to use the backslash \ which instead gives a reference to existing array without coping. (like & operator in C, as tell us perldoc)
Your outermost for loop is constrained by the length of t, which is 3. It will never print more than three arrays.
If I understand what you're trying to do, you need top swap #t and #dev. That will print all your values.
That won't, however, print any array that is longer than 3 (the length of dev).
For that, you need:
#dev = (
[#tf], # Probably meant tf
[#a],
[#t],
[#f],
[#ofv],
[#tfv],
);
#print($dev[4][2]);
for (my $i = 0; $i < #dev; $i++) {
for (my $j = 0; $j < #{ $dev[$i] }; $j++) {
print($dev[$i][$j]);
print("\n");
}
}

Is it possible to assign two variables in Perl foreach loop?

Is it possible to assign two variables the same data from an array in a Perl foreach loop?
I am using Perl 5, I think I came across something in Perl 6.
Something like this:
my $var1;
my $var2;
foreach $var1,$var2 (#array){...}
It's not in the Perl 5 core language, but List::Util has a pairs function which should be close enough (and a number of other pair... functions which may be more convenient, depending on what you're doing inside the loop):
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
use List::Util 'pairs';
my #list = qw(a 1 b 2 c 3);
for my $pair (pairs #list) {
my ($first, $second) = #$pair;
say "$first => $second";
}
Output:
a => 1
b => 2
c => 3
The easiest way to use this is with a while loop that calls splice on the first two elements of the array each time,
while (my($var1, $var2) = splice(#array, 0, 2)) {
...
}
However, unlike foreach, this continually does a double-shift on the original array, so when you’re done, the array is empty. Also, the variables assigned are copies, not aliases as with foreach.
If you don’t like that, you can use a C-style for loop:
for (my $i = 0; $i < #array; $i += 2) {
my($var1, $var2) = #array[$i, $i+1];
...
}
That leaves the array in place but does not allow you to update it the way foreach does. To do that, you need to address the array directly.
my #pairlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for (my $i = 0; $i < #pairlist; $i += 2) {
$pairlist[ $i + 0 ] x= 2;
$pairlist[ $i + 1 ] *= 2;
}
print "Array is #pairlist\n";
That prints out:
Array is feefee 2 fiefie 4 foefoe 6 fumfum 8
You can get those into aliased variables if you try hard enough, but it’s probably not worth it:
my #kvlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for (my $i = 0; $i < #kvlist; $i += 2) {
our ($key, $value);
local(*key, $value) = \#kvlist[ $i, $i + 1 ];
$key x= 2;
$value *= 2;
}
print "Array is #kvlist\n";
Which prints out the expected changed array:
Array is feefee 2 fiefie 4 foefoe 6 fumfum 8
Note that the pairs offered by the List::Pairwise module, which were but very recently added to the core List::Util module (and so you probably cannot use it), are still not giving you aliases:
use List::Util 1.29 qw(pairs);
my #pairlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for my $pref (pairs(#pairlist)) {
$pref->[0] x= 2;
$pref->[1] *= 2;
}
print "Array is #pairlist\n";
That prints out only:
Array is fee 1 fie 2 foe 3 fum 4
So it didn’t change the array at all. Oops. :(
Of course, if this were a real hash, you could double the values trivially:
for my $value (values %hash) { $value *= 2 }
The reasons that works is because those are aliases into the actual hash values.
You cannot change the keys, since they’re immutable. However, you can make a new hash that’s an updated copy of the old one easily enough:
my %old_hash = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
my %new_hash;
#new_hash{ map { $_ x 2 } keys %old_hash } =
map { $_ * 2 } values %old_hash;
print "Old hash is: ", join(" " => %old_hash), "\n";
print "New hash is: ", join(" " => %new_hash), "\n";
That outputs
Old hash is: foe 3 fee 1 fum 4 fie 2
New hash is: foefoe 6 fiefie 4 fumfum 8 feefee 2
A general algorithm for more than 2 variables:
while( #array ){
my $var1 = shift #array;
my $var2 = shift #array;
my $var3 = shift #array;
# other variables from #array
# do things with $var1, $var2, $var3, ...
}
PS: Using a working copy of the array to that it is preserved for use later:
if( my #working_copy = #array ){
while( #working_copy ){
my $var1 = shift #working_copy;
my $var2 = shift #working_copy;
my $var3 = shift #working_copy;
# other variables from #working_copy
# do things with $var1, $var2, $var3, ...
}
}
PPS: another way is to use indexing. Of course, that is a sure sign that the data structure is wrong. It should be an array of arrays (AoA) or an array of hashes (AoH). See perldoc perldsc and perldoc perllol.
my $i = 0;
while( $i < #array ){
my $var1 = $array[ $i++ ];
my $var2 = $array[ $i++ ];
my $var3 = $array[ $i++ ];
# other variables from #array
# do things with $var1, $var2, $var3, ...
}
PPPS: I've been asked to clarify why the data structure is wrong. It is a flatten set of tuples (aka records aka datasets). The tuples are recreated by counting of the number of data for each. But what is the reader constructing the set has a bug and doesn't always get the number right? If, for a missing value, it just skips adding anything? Then all the remaining tuples are shifted by one, causing the following tuples to be grouped incorrectly and therefore, invalid. That is why an AoA is better; only the tuple with the missing data would be invalid.
But an better structure would be an AoH. Each datum would access by a key. Then new or optional data can be added without breaking the code downstream.
While I'm at it, I'll add some code examples:
# example code for AoA
for my $tuple ( #aoa ){
my $var1 = $tuple->[0];
my $var2 = $tuple->[1];
my $var3 = $tuple->[2];
# etc
}
# example code for AoH
for my $tuple ( #aoh ){
my $var1 = $tuple->{keyname1};
my $var2 = $tuple->{key_name_2};
my $var3 = $tuple->{'key name with spaces'};
my $var4 = $tuple->{$key_name_in_scalar_variable};
# etc
}
Here is a module-less way to "loop" by an arbitrary value ($by) and output the resulting group of elements using an array slice:
#!perl -l
#array = "1".."6";
$by = 3; $by--;
for (my $i = 0 ; $i < #array ; $i += $by ) {
print "#array[$i..$i+$by]";
$i++ ;
}
As a one-liner to test (cut and paste to a Unix shell):
perl -E '#array = "1".."6"; $by = 3; $by--;
for (my $i = 0 ; $i < #array ; $i += $by ) {
say "#array[$i..$i+$by]"; $i++ }'
Output:
1 2 3
4 5 6
If you make $by = 2; it will print pairs of numbers. To get at specific elements of the resulting slice access it as an anonymous array: (e.g. [#array[$i..$i+$by]]->[1]).
See also:
How do I read two items at a time in a Perl foreach loop?
Perl way of iterating over 2 arrays in parallel
Some good responses there, including reference to natatime which is quite easy to use. It's easy to implement too - it is essentially a wrapper around the splice solutions mentioned in the responses here.
The following is not the nicest example, but I've been using autobox::Core and made an #array->natatime() "method" ;-) like this:
use autobox::Core ;
sub autobox::Core::ARRAY::natatime {
my ($self, $by) = #_;
my #copy = #$self ;
my #array ;
push #array, [splice (#copy, 0, $by) ] while #copy ;
if ( not defined wantarray ) {
print "#{ $_ } \n" for #array ;
}
return wantarray ? #array : \#array;
}
The #copy array is spliced destructively, but $self (which is how the #array in front of the autobox method -> arrow gets passed to the function) is still there. So I can do:
my #dozen = "1" .. "12" ; # cakes to eat
#dozen->natatime(4) ; # eat 4 at time
my $arr_ref = #dozen->natatime(4) ; # make a reference
say "Group 3: #{ $arr_ref->[2] }" ; # prints a group of elements
say scalar #dozen , " cakes left" ; # eat cake; still have it
Output:
1 2 3 4
5 6 7 8
9 10 11 12
Group 3: 9 10 11 12
12 cakes left
One other approach that also uses a CPAN module (I gave this answer elsewhere but it is worth repeating). This can also be done non-destructively, with Eric Strom's excellent List::Gen module:
perl -MList::Gen=":all" -E '#n = "1".."6"; say "#$_" for every 2 => #n'
1 2
3 4
5 6
Each group of elements you grab is returned in an anonymous array so the individual values are in: $_->[0] $_->[1] ... etc.
You mentioned Perl6, which handles multiple looping values nicely:
my #qarr = 1 .. 6;
my ($x, $y, $z) ;
for #qarr -> $x , $y , $z { say $x/$y ; say "z = " ~ $z }
Output:
0.5
z = 3
0.8
z = 6
For more on the Perl6 approach see: Looping for Fun and Profit from the 2009 Perl6 Advent Calendar, or the Blocks and Statements Synopsis for details. Perhaps Perl 5 will have a similar "loop by multliple values" construct one day - à la perl5i's foreach :-)

perl to add the results of 2 arrays from 2 differnt files together

I run a report between 2 csv files, the last bit i wish to do check is to add matching elemants of the 2 arrays (built up of unique values and occurances) together. but i can't work out how to do a for each matching name in each both arrays add together, to get the output as below.
INPUT:
jon 22
james 12
ken 22
jack 33
jim 11
harry 7
dave 9
grant 12
matt 74
malc 12
INPUT1:
jon 2
james 1
ken 8
jack 5
jim 1
harry 51
dave 22
Desired Output:
jon 24
james 13
ken 30
jack 38
jim 12
harry 58
dave 31
grant 12
matt 74
malc 12
code i have so to create oput from INPUT and INPUT1
my %seen;
seek INPUT, 0, 0;
while (<INPUT>)
{
chomp;
my $line = $_;
my #elements = split (",", $line);
my $col_name = $elements[1];
#print " $col_name \n" if !
$seen{$col_name}++;
}
while ( my ( $col_name, $times_seen ) = each %seen ) {
my $loc_total = $times_seen * $dd;
print "\n";
print " $col_name \t\t : = $loc_total";
printf OUTPUT "%-34s = %15s\n", $col_name , " $loc_total ";
}
############## ###################
my %seen2;
seek INPUT1, 0, 0;
while (<INPUT1>)
{
chomp;
my $line = $_;
my #elements1 = split (",", $line);
my $col_name = $elements1[1];
my $col_type = $elements1[5];
$seen2{$col_name}++ if $col_type eq "YES";
}
while ( my ( $col_name, $times_seen2 ) = each %seen2 ) {
my $loc_total = $times_seen2 ;
print "\n $col_name \t\t= $loc_total";
printf OUTPUT "%-34s = %15s\n", $col_name , $times_seen2 ;
}
close INPUT;
Instead of using %seen, store the running total in the hash directly:
#!/usr/bin/perl
use warnings;
use strict;
my %count;
for my $file ('INPUT', 'INPUT1') {
open my $IN, '<', $file or die "$file: $!";
while (<$IN>) {
my ($name, $num) = split;
$count{$name} += $num;
}
}
for my $name (sort { $count{$b} <=> $count{$a} } keys %count) {
print "$name\t$count{$name}\n";
}
First, I'll assume that the input files are actual CSV files -- whereas your examples are just whitespace delimited. In other words:
jon,22
james,12
ken,22
jack,33
jim,11
harry,7
dave,9
grant,12
matt,74
malc,12
and
jon,2
james,1
ken,8
jack,5
jim,1
harry,51
dave,22
ASSUMING I'm correct, then your while loops will do the trick, with a couple of tweaks:
The first element of your #elements arrays have index 0, not 1. So the "key" here is at $elements[0], and the "value" is at $elements[1]. So you'd have something like:
my $col_name = $elements[0];
my $col_value = $elements[1];
Instead of incrementing %seen, it seems more useful to add the value, like so:
$seen{ $col_name } += $col_value;
In your while loop which iterates over INPUT1, do the same thing done in the first loop to extract data; also, don't use %seen2; instead, simply add to %seen as above:
my $col_name = $elements1[0];
my $col_value = $elements1[1];
$seen{$col_name} += $col_value;
Your totals will then be stored in %seen, so your final while loop is slightly modified:
while ( my ( $col_name, $times_seen2 ) = each %seen ) { # instead of %seen2
If your two processing loops are identical (and I see it's possible that they're not), then I'd suggest factoring them into a common subroutine. But that's a different matter.
The following could easily be adapted to just take file names from the command line instead.
Maintains the order of the keys in your file:
use strict;
use warnings;
use autodie;
my #names;
my %total;
local #ARGV = qw(INPUT INPUT1);
while (<>) {
my ($name, $val) = split;
push #names, $name if ! exists $total{$name};
$total{$name} += $val;
}
for (#names) {
print "$_ $total{$_}\n";
}

Perl array element manipulation

I've been trying and trying with this one, but it just doesn't seem to click.
If I have an array with let's say 6 numbers:
#a = (1,2,3,4,5,6)
How do I get every second index ( 2, 4, 6) in this case?
how do I compute the difference of every two elements, so
the output here would be:
1 1 1 (because 2-1 =1 and 4-3 =1 and so on..)
Note: don't ever use $a or $b, they're special (sort uses them) ... it's generally better to give your variables a descriptive name, name it as to what's in there rather than what type of variable it is.
for ( my $index = 0; $index < scalar( #pairs ); $index += 2 ) {
my $first = $pairs[ $index + 0 ];
my $second = $pairs[ $index + 1 ];
my $pair = $index / 2;
my $difference = $second - $first;
print "the difference of pair $pair is $difference\n";
}
I think you should post your earlier attempts. In my opinion, the best way to learn is to learn from your mistakes, not being presented a correct solution.
For this problem, I think I would use a C-style for-loop for the first part, simply because it is straightforward, and can easily be tweaked if some new requirement comes up.
The second problem can easily be solved using a regular Perl-style for-loop.
use strict;
use warnings; # always use these two pragmas
my #nums = 1..6;
my #idx;
for (my $n = 0; $n <= $#nums; $n += 2) { # loop from 0 to max index, step 2
push #idx, $n; # store number in #idx
}
print "Indexes: #idx\n";
my #diff;
for my $n (0 .. $#nums - 1) { # loop from 0 to max index minus 1
push #diff, $nums[$n + 1] - $nums[$n]; # store diff in #diff
}
print "Diff: #diff\n";
Output:
Indexes: 0 2 4
Diff: 1 1 1 1 1
Try this:
use strict;
use warnings;
my $index = 1;
my #a = (1,2,3,4,5,6);
for (#a) {
if ($index % 2 == 0) {
my $diff = $_ - $a[$index-2];
print $diff;
}
$index++;
}
You likely want to use the new List::Util pair functions.
For your first question:
use List::Util 'pairvalues';
my #seconds = pairvalues #list; # yields (2, 4, 6)
For your second question:
use List::Util 'pairmap';
my #diffs = pairmap { $b-$a } #list; # yields (1, 1, 1)
You can use map:
my #a = 1 .. 6;
print join ' ', 'Every second:', map $a[ 1 + $_ * 2 ], 0 .. $#a / 2;
print "\n";
print join ' ', 'Differences:', map $a[ 1 + $_ * 2 ] - $a[ $_ * 2 ], 0 .. $#a / 2;
print "\n";
First: Don't use variables a and b. $a and $b are special variables used in sorting. Just be a bit more descriptive of your variables (even if it's merely #my_array) and you should be fine.
You can loop through your array any which way you like. However, I prefer to use a while loop instead of the thee part for because the three part for loop is a bit misleading. It is a while loop in disguise and the promised indexing of the loop can be misleading.
#! /usr/bin/env perl
use warnings;
use strict;
use feature qw(say);
my #array = qw( 1 2 3 4 5 6 );
my $index = 1; # Remember Perl indexes start at zero!
while ( $index <= $#array ) {
say "Item is $array[$index]";
say "The difference is " . ($array[$index] - $array[$index-1]);
$index += 2;
}
You said every second element. Indexes of arrays start at 0, so you want the odd number elements. Most of the answers use map which is a very nice little command, but does an awful lot in a single line which can make it confusing for a beginner. Plus, I don't think the Perldoc on it is very clear. There should be more simple examples.
The say is a newer version of print. However say always adds a \n at the end. You should always use strict; and use warnings;. These will catch about 90% of your programming bugs.
The qw( ... ) is a quick way to make an array. Each word becomes an array element. You don't need quotes or commas.
#!/usr/bin/perl
use strict;
use warnings;
my #ar = (1, 2, 3, 4, 5, 6);
# 1. How do I get every second index ( 2, 4, 6) in this case?
my #even = map { $_ & 1 ? $ar[$_] : () } 0 .. $#ar;
# 2. how do I compute the difference of every two elements?
my (#c, #diff) = #ar;
push #diff, -1 * (shift(#c) - shift(#c)) while #c;
use Data::Dumper;
print Dumper \#even;
print Dumper \#diff;
1;
__END__
$VAR1 = [
2,
4,
6
];
$VAR1 = [
1,
1,
1
];

Resources