Perl - function with two array arguments - arrays

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);

Related

Trying to clear a array when all values are inserted on other array

So im trying to clear the array NewPassagers when all values inside passed to OnBusPassagers.
Its for a FiveM script btw!
if GetVehicleNumberOfPassengers(veh) > 1 then
for i = 1, #NewPassagers, 1 do
table.insert(OnBusPassagers, NewPassagers)
end
NewPassagers = nil
end
Maybe lets do a while, table.insert() and table.remove() do the job...
local tab1 = {1, 2, 3, 4, 5, 6, 7, 8, 9}
local tab2 = {}
while #tab1 ~= 0 do
-- Reverse order
-- table.insert(tab2, table.remove(tab1))
-- Same order
table.insert(tab2, table.remove(tab1, 1))
end
return tab2
...table.remove({}[, pos]) removes last key/value by default without pos and returning that value for table.insert().

Perl - Filter function for arrays

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.

python 3 read array (list?) into new values

I have the following array, with (I think) sub lists within it:
items = [('this', 5, 'cm'), ('that', 3, 'mm'), ('other', 15, 'mm')]
I need to read it into new values for future calculations.
For example:
item1 = this
size1 = 5
unit1 = cm
item2 = that
size2 = 3
unit2 = mm
...
There may be more than 3 items in future arrays, so ideally some form of loop is needed?
Arrays in Python can be of 2 types - Lists & Tuples.
list is mutable (i.e. you can change the elements as & when you wish)
tuple is immutable (read only array)
list is represented by [1, 2, 3, 4]
tuple is represented by (1, 2, 3, 4)
Thus, the given array is a list of tuples!
You can nest tuples in lists but not lists in tuples.
This is more pythonic -
items = [('this', 5, 'cm'), ('that', 3, 'mm'), ('other', 15, 'mm')]
found_items = [list(item) for item in items]
for i in range(len(found_items)):
print (found_items[i])
new_value = int(input ("Enter new value: "))
for i in range(len(found_items)):
recalculated_item = new_value * found_items[i][1]
print (recalculated_item)
Output from above code (taking input as 3)
['this', 5, 'cm']
['that', 3, 'mm']
['other', 15, 'mm']
15
9
45
Update : Following up on this comment & this answer I've updated the above code.
Following on Ashish Nitin Patil's answer...
If there are are going to be more than three items in the future you can use the asterisk to unpack the items in the tuples.
items = [('this', 5, 'cm'), ('that', 3, 'mm'), ('other', 15, 'mm')]
for x in items:
print(*x)
#this 5 cm
#that 3 mm
#other 15 mm
Note: Python 2.7 doesn't seem to like the asterisk in the print method.
Update:
It looks like you need to use a second list of tuples that defines the property names of each value tuple:
props = [('item1', 'size2', 'unit1'), ('item2', 'size2', 'unit2'), ('item3', 'size3', 'unit3')]
values = [('this', 5, 'cm'), ('that', 3, 'mm'), ('other', 15, 'mm')]
for i in range(len(values)):
value = values[i]
prop = props[i]
for j in range(len(item)):
print(prop[j], '=', value[j])
# output
item1 = this
size2 = 5
unit1 = cm
item2 = that
size2 = 3
unit2 = mm
item3 = other
size3 = 15
unit3 = mm
The caveat here is that you need to make sure that the elements in the props list are matched sequentially with the elements in the values list.

How to sum multiple arrays element-wise in Perl?

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;

How can I sum arrays element-wise in Perl?

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);

Resources