I have two arrays:
#arr1 = ( 1, 0, 0, 0, 1 );
#arr2 = ( 1, 1, 0, 1, 1 );
I want to sum items of both arrays to get new one like
( 2, 1, 0, 1, 2 );
Can I do it without looping through arrays?
for Perl 5:
use List::MoreUtils 'pairwise';
#sum = pairwise { $a + $b } #arr1, #arr2;
If you're using Perl 6:
#a = (1 0 0 0 1) <<+>> (1 1 0 1 1) #NB: the arrays need to be the same size
The Perl 6 Advent Calendar has more examples.
Fundamentally, no, you can't do it without "looping through arrays" because you need to access every element of both arrays in order to sum them. Both the answers so far just hide the looping under a layer of abstraction but it's still there.
If you're concerned about looping over very large arrays, it's probably best to consider other ways of keeping the sum up-to-date as you go.
what's wrong with looping over arrays? that's the fundamentals.
#arr1 = ( 1, 0, 0, 0, 1 );
#arr2 = ( 1, 1, 0, 1, 1 );
for ($i=0;$i<scalar #arr1;$i++){
print $arr[$i] + $arr2[$i] ."\n";
}
You've seen a C style for loop, and pairwise. Here's an idiomatic Perl for loop and map:
my #arr1 = ( 1, 0, 0, 0, 1 );
my #arr2 = ( 1, 1, 0, 1, 1 );
my #for_loop;
for my $i ( 0..$#arr1 ) {
push #for_loop, $arr1[$i] + $arr2[$i];
}
my #map_array = map { $arr1[$_] + $arr2[$_] } 0..$#arr1;
I like map and pairwise best. I'm not sure that I have a preference between those two options. pairwise handles some boring details of plumbing for you, but it is not a built-in like map. On the other hand, the map solution is very idiomatic, and may be opaque to a part-time perler.
So, no real wins for either approach. IMO, both pairwise and map are good.
From http://www.perlmonks.org/?node_id=122393
#a = qw(1 2 3 4);
#b = qw(1 2 3 4);
#c = ();
#c = map { $a[$_] + $b[$_] } ( 0 .. (#a > #b ? $#a : $#b) );
Or:
$c[#c] = $a[#c] + $b[#c] while defined $a[#c] or defined $b[#c];
Or:
$c[$_] = $a[$_] + $b[$_] for 0 .. (#a > #b ? $#a : $#b);
Or (in Perl 6):
#c = #a ^+ #b
If you're really afraid of looping, then you can binary chop the arrays, sum the pairs, then recursively reassemble the resulting array. No looping there, and as a bonus you get to learn how part of the fast-fourier transform derivation works.
To avoid (explicit) looping, here is a solution that uses recursion "instead":
#!/usr/bin/perl
use v5.20;
my #arr1 = ( 1, 0, 0, 0, 1 );
my #arr2 = ( 1, 1, 0, 1, 1 );
my #result=non_looping_pairwise_sum([ #arr1 ], [ #arr2 ]); # pass in copies, so the originals are not modified
say "#result";
sub non_looping_pairwise_sum { # only handles lists that have the same length
my ($a1, $a2)=#_;
return () if (scalar(#$a1)==0 and scalar(#$a2)==0);
my $e1=shift #$a1;
my $e2=shift #$a2;
return ($e1+$e2, non_looping_pairwise_sum($a1, $a2));
}
Output:
2 1 0 1 2
Note that use v5.20 means you don't have to write use strict; use warnings, I think.
Apologies/kudos to #parm for the idea.
I'm not sure what you plan to do with the sum once you have it, but you plan to do more vector-y type stuff, then Math::Matrix might be a good fit.
use Math::Matrix;
my $foo = Math::Matrix->new([ 1, 0, 0, 0, 1 ]);
my $bar = Math::Matrix->new([ 1, 1, 0, 1, 1 ]);
my $sum = $foo->add($bar);
Related
Lets say I have the following array:
array_sample = [2, 7, 3, -5, 2, -6]
Is there a method to replace '-5' and '-6' with 0 (or any other potential negative values)?
I have tried the following which does not seem to work:
for i in array_sample
if array_sample[i] < 0
array_sample[i] = 0
end
Any suggestions would be appreciated, because this seems very simple!
Here's another snippet:
array_sample.map!{|item| [0, item].max}
This replaces each array value with the original item, or 0, whichever is greater. If you want a new array that doesn't mutate the current array (good idea often), you can use map instead of map!.
Try this
array_sample = [2, 7, 3, -5, 2, -6]
array_sample.map! { |e| e > 0 ? e : 0 }
Response
[2, 7, 3, 0, 2, 0]
You can try using map
array_sample.map do |int|
if (int < 0)
0
else
int
end
end
To directly translate your example to correct code, you could do this:
array_sample.each_with_index do |value, index|
if value < 0
array_sample[index] = 0
end
end
But other answers may be more idiomatic.
Note that ruby doesn't iterate using numbers by default, like c-like languages often do. Most of the time ruby gives you back each item, not the index and then you have to look up the number. (It hides the index unless you ask for it)
Can someone tell me why the two code below give me different results? I was under the impression that the enumerate and range(len(list)) both are iterable.
Code: Trying to find the equilibrium index of an array
def equisum (arr):
tsum = sum(arr)
lsum = 0
num = len(arr)
for i in range(num):
tsum -= num
if lsum == tsum:
return i
lsum += num
return -1
arr = [-7, 1, 5, 2, -4, 3, 0]
print (equisum (arr))
Result : -1
def equisum (arr):
tsum = sum(arr)
lsum = 0
for i, num in enumerate(arr):
tsum -= num
if lsum == tsum:
return i
lsum += num
return -1
arr = [-7, 1, 5, 2, -4, 3, 0]
print (equisum (arr))
Result : 3 (the correct answer)
range(num) will give the values 0, 1, 2....
enumerate(arr) will give the values (0, -7), (1, 1), (2, 5)... where the first value is the index and the second value is value of arr at that index.
Your second function should give the correct answer because it subtracts the value of arr[i] rather than just i.
range() is used for getting the sequence of numbers. For example if you give range(10), it will give numbers from 0 to 9.
Here, as you are using range, you are calculating the numbers wrong and finally the default -1 is being returned.
While in enumerate(), it returns both index and actual element from the list.
by the way, if you are using VS Code for programming, make use of the debug feature. It will help to identify such issues much faster.
I have troubles using a function in Perl.
My function has 2 arguments which are arrays :
sub get_coordinate {
my (#array_col, #array_lin) = (#_);
do some stuff
}
I call it this way :
$index_col = int(rand(10));
$index_lin = int(rand(10));
#array_col = (0,0,0,0,0,0,0,0,0,0);
#array_lin = (0,0,0,0,0,0,0,0,0,0);
$array_col[$index_col] = 1;
$array_lin[$index_lin] = 1;
get_coordinate(#array_col, #array_lin);
My problem is that I get the error message : Use of uninitialized value within #array_lin in numeric eq (==) at
switch.pl line 82 (#1)
(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.
I don't understand why #array_col is initialized an not #array_lin.
When I print #array_col and #array_lin inside the function this way :
print "#array_col\n#array_lin\n";
I get : 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0
Any idea ?
Thx,
SLP
In Perl, all lists are flat. These two lists are equivalent.
( 1, 2, ( 3, 4, ( 5 ), (6, 7), 8), (), )
( 1, 2, 3, 4, 5, 6, 7, 8 )
The same thing also happens when take several arrays and stick them in a list.
my #foo = (1, 2, 3);
my #bar = (4, 5, 6);
my #new = (#foo, #bar); # 1, 2, 3, 4, 5, 6
When you pass things to a function, those things get turned into a list of arguments. Therefore, the arrays will both end up in one list, like #foo and #bar above.
frobnicate(#foo, #bar);
When you assign something in list context, the entire list will be assigned left to right. For scalars in the list on the left-hand-side this means they will get their values. But as soon as there is an array, this will be greedy. It will suck up all the remaining values.
my ($one, $two, #rest, $will_be_undef) = (1, 2, 3, 4, 5, 6);
The values will be assigned like this:
$one = 1;
$two = 2;
#rest = ( 3, 4, 5, 6 );
$will_be_undef = undef;
What you need to do to pass two arrays is to take references, and dereference them in our function.
frobnicate( \#foo, \#bar );
sub frobnicate {
my ($first_array, $second_array) = #_;
my #foo = #{ $first_array };
my #bar = #{ $second_array };
...
}
Assigning to several arrays (or hashes) doesn't do what you think:
my (#array1, #array2) = ...
The first array gets all the elements. How should it know where to stop?
You can use array references instead:
sub get_coordinate {
my ($arr1, $arr2) = #_;
my #arr1 = #$arr1;
my #arr2 = #$arr2;
...
}
get_coordinate(\#array1, \#array2);
i am trying to create a subroutine that does the following :
Takes two arrays as input (Filter, Base)
Outputs only the values of the second array that do not exist in the first
Example :
#a = ( 1, 2, 3, 4, 5 );
#b = ( 1, 2, 3, 4, 5, 6, 7);
Expected output : #c = ( 6, 7 );
Called as : filter_list(#filter, #base)
###############################################
sub filter_list {
my #names = shift;
my #arrayout;
foreach my $element (#_)
{
if (!($element ~~ #names )){
push #arrayout, $element;
}
}
return #arrayout
}
Test Run :
#filter = ( 'Tom', 'John' );
#array = ( 'Tom', 'John', 'Mary' );
#array3 = filter_list(#filter,#array);
print #array3;
print "\n";
Result :
JohnJohnMary
Can anyone help? Thank you.
You can't pass arrays to subs, only scalars. So when you do
my #filtered = filter_list(#filter, #base);
you are really doing
my #filtered = filter_list($filter[0], $filter[1], ..., $base[0], $base[1], ...);
As such, when you do
my #names = shift;
you are really doing
my #names = $filter[0];
which is obviously wrong.
The simplest solution is to pass references to the arrays.
my #filtered = filter_list(\#filter, \#base);
A hash permits an efficient implementation (O(N+M)).
sub filter_list {
my ($filter, $base) = #_;
my %filter = map { $_ => 1 } #$filter;
return grep { !$filter{$_} } #$base;
}
Alternatively,
my #filtered = filter_list(\#filter, #base);
could be implemented as
sub filter_list {
my $filter = shift;
my %filter = map { $_ => 1 } #$filter;
return grep { !$filter{$_} } #_;
}
What you're looking for is the difference of two sets. This, along with union, intersection, and a bunch of others are set operations. Rather than writing your own, there's plenty of modules for dealing with sets.
Set::Object is very fast and featureful. I'd avoid using the operator interface (ie. $set1 - $set2) as it makes the code confusing. Instead use explicit method calls.
use strict;
use warnings;
use v5.10;
use Set::Object qw(set);
my $set1 = set(1, 2, 3, 4, 5);
my $set2 = set(1, 2, 3, 4, 5, 6, 7);
say join ", ", $set2->difference($set1)->members;
Note that sets are unordered and cannot contain duplicates. This may or may not be what you want.
This uses List::Compare, a module with a large collection of routines for comparing lists.
Here you want get_complement
use warnings;
use strict;
use List::Compare;
my #arr1 = ( 1, 2, 3, 4, 5 );
my #arr2 = ( 1, 2, 3, 4, 5, 6, 7);
my $lc = List::Compare->new(\#arr1, \#arr2);
my #only_in_second = $lc->get_complement;
print "#only_in_second\n";
The module has many options.
If you don't need the result sorted, pass -u to the constructor for faster operation.
There is also the "Accelerated Mode", obtained by passing -a. For the purpose of efficient repeated comparisons between the same arrays many things are precomputed at construction. With this flag that is suppressed, which speeds up single comparisons. See List::Compare Modes.
These two options can be combined, List::Compare->new('-u', '-a', \#a1, \#a2).
Operations on three or more lists are supported.
There is also the functional interface, as a separate List::Compare::Functional module.
There is a question very similar to this already but I would like to do this for multiple arrays. I have an array of arrays.
my #AoA = (
$arr1 = [ 1, 0, 0, 0, 1 ],
$arr2 = [ 1, 1, 0, 1, 1 ],
$arr3 = [ 2, 0, 2, 1, 0 ]
);
I want to sum the items of all the three (or more) arrays to get a new one like
( 4, 1, 2, 2, 2 )
The use List::MoreUtils qw/pairwise/ requires two array arguments.
#new_array = pairwise { $a + $b } #$arr1, #$arr2;
One solution that comes to mind is to loop through #AoA and pass the first two arrays into the pairwise function. In the subsequent iterations, I will pass the next #$arr in #AoA and the #new_array into the pairwise function. In the case of an odd sized array of arrays, after I've passed in the last #$arr in #AoA, I will pass in an equal sized array with elements of 0's.
Is this a good approach? And if so, how do I implement this? thanks
You can easily implement a “n-wise” function:
sub nwise (&#) # ← take a code block, and any number of further arguments
{
my ($code, #arefs) = #_;
return map {$code->( do{ my $i = $_; map $arefs[$_][$i], 0 .. $#arefs } )}
0 .. $#{$arefs[0]};
}
That code is a bit ugly because Perl does not support slices of multidimensional arrays. Instead I use nested maps.
A quick test:
use Test::More;
my #a = (1, 0, 0, 0, 1);
my #b = (1, 1, 0, 1, 1);
my #c = (2, 0, 2, 1, 0);
is_deeply [ nwise { $_[0] + $_[1] + $_[2] } \#a, \#b, \#c], [4, 1, 2, 2, 2];
I prefer passing the arrays as references instead of using the \# or + prototype: This allows you to do
my #arrays = (\#a, \#b, \#c);
nwise {...} #arrays;
From List::MoreUtils you could have also used each_arrayref:
use List::Util qw/sum/;
use List::MoreUtils qw/each_arrayref/;
my $iter = each_arrayref #arrays;
my #out;
while (my #vals = $iter->()) {
push #out, sum #vals;
}
is_deeply \#out, [4, 1, 2, 2, 2];
Or just plain old loops:
my #out;
for my $i (0 .. $#a) {
my $accumulator = 0;
for my $array (#arrays) {
$accumulator += $array->[$i];
}
push #out, $accumulator;
}
is_deeply \#out, [4, 1, 2, 2, 2];
The above all assumed that all arrays were of the same length.
A note on your snippet:
Your example of the array structure is of course legal perl, which will even run as intended, but it would be best to leave out the inner assignments:
my #AoA = (
[ 1, 0, 0, 0, 1 ],
[ 1, 1, 0, 1, 1 ],
[ 2, 0, 2, 1, 0 ],
);
You might actually be looking for PDL, the Perl Data Language. It is a numerical array module for Perl. It has many functions for processing arrays of data. Unlike other numerical array modules for other languages it has this handy ability to use its functionality on arbitrary dimensions and it will do what you mean. Note that this is all done at the C level, so it is efficient and fast!
In your case you are looking for the projection method sumover which will take an N dimensional object and return an N-1 dimensional object created by summing over the first dimension. Since in your system you want to sum over the second we first have to transpose by exchanging dimensions 0 and 1.
#!/usr/bin/env perl
use strict;
use warnings;
use PDL;
my #AoA = (
[ 1, 0, 0, 0, 1 ],
[ 1, 1, 0, 1, 1 ],
[ 2, 0, 2, 1, 0 ],
);
my $pdl = pdl \#AoA;
my $sum = $pdl->xchg(0,1)->sumover;
print $sum . "\n";
# [4 1 2 2 2]
The return from sumover is another PDL object, if you need a Perl list you can use list
print "$_\n" for $sum->list;
Here's a simple iterative approach. It probably will perform terribly for large data sets. If you want a better performing solution you will probably need to change the data structure, or look on CPAN for one of the statistical packages. The below assumes that all arrays are the same size as the first array.
$sum = 0;
#rv = ();
for ($y=0; $y < scalar #{$AoA[0]}; $y++) {
for ($x=0; $x < scalar #AoA; $x++) {
$sum += ${$AoA[$x]}[$y];
}
push #rv, $sum;
$sum = 0;
}
print '('.join(',',#rv).")\n";
Assumptions:
each row in your AoA will have the same number of columns as the first row.
each value in the arrayrefs will be a number (specifically, a value in a format that "works" with the += operator)
there will be at least one "row" with sat least one "column"
Note: "$#{$AoA[0]}" means, "the index of the last element ($#) of the array that is the first arrayref in #AoA ({$AoA[0]})"
(shebang)/usr/bin/perl
use strict;
use warnings;
my #AoA = (
[ 1, 0, 0, 0, 1 ],
[ 1, 1, 0, 1, 1 ],
[ 2, 0, 2, 1, 0 ]
);
my #sums;
foreach my $column (0..$#{$AoA[0]}) {
my $sum;
foreach my $aref (#AoA){
$sum += $aref->[$column];
}
push #sums,$sum;
}
use Data::Dumper;
print Dumper \#sums;