Pushing an array reference repeatedly: the pushed references end up all equal - arrays

I want to create a 2d array(#combinations) that holds all combinations of another array (#indices).
I'm using push to append a reference to another array(#temp2). When I print my 2d array (using Dumper) it is not as I expect: the print statement inside the loop shows that every pushed reference is to a non-empty list, but eventually all my references point to an empty list. Why?
use Math::Combinatorics;
use Data::Dumper;
my (#combinations, #temp2);
my #indices = (0, 2, 4);
my $count = 1;
my $counter = #indices;
while ($counter>= $count) {
my $c = Math::Combinatorics->new(
count => $count,
data => \#indices,
);
$count++;
while (#temp2 = $c->next_combination) {
print "#temp2 \n";
push #combinations, \#temp2;
}
}
print Dumper(\#combinations);

Because you declare #temp2 at the top level, the reference \#temp2 will always point to the same data. Because you exit the loop as soon as #temp2 is empty, all the references in #combinations will point to this same empty array.
The remedy is easy: declare the #temp2 to be local to the while loop, by writing
while (my #temp2 = $c->next_combination) {
This will create a new variable #temp2, with its own reference, each time the loop is repeated.

Related

How to insert an array into another array at a particular index in perl

I'm very new to perl. Need some assistance in the below scenario.
I have declared an array and trying to initialize like
my #array1;
$array1[0] = 1;
$array1[1] = 2;
$array1[3] = 4;
Now I want to insert another array lets say my #data = [10,20,30]; at index 2 of array1.
So after insert array1 should look like [1, 2, [10,20,30],4]
How can I do that?
You will need to use array references.
In short you can do
$array1[2] = \#data;
This will make the second member of $array1 a reference to #data. Because this is a reference you can't access it in quite the same way as a normal array. To access #data via array1 you would use #{$array1[2]}. To access a particular member of the reference to #data you can use -> notation. See for example the simple program below:
use strict;
use warnings;
my #array1;
$array1[0] = 1;
$array1[1] = 2;
$array1[3] = 4;
my #data = (10,20,30);
$array1[2] = \#data;
print "A #array1\n"; #Note that this does not print contents of data #data
print "B #{$array1[2]}\n"; #Prints #data via array1
print "C $array1[2]->[0]\n"; #Prints 0th element of #data
You can write
$array1[2] = [10,20,30];
Be aware that what you inserting into the host array is actually an array reference. Hence the syntax: [10,20,30] is a reference while (10,20,30) is a list proper.
Perl doesn't have nested arrays.
You can use splice for doing so.
use Data::Dumper;
splice #array1, 2, 0, #data;
print Dumper \#array1;
the splice prototype is:
splice [origin array], [index], [length of elements to replace], [replacement]

Problem to get more than 1 element with same highest occurrence from array in Perl

I only get the smaller element as output although there are 2 elements with same highest occurrence in array
I have tried to remove sort function from the codes but it still returns me the smaller element
my(#a) = (undef,11,12,13,14,15,13,13,14,14);
my(%count);
foreach my $value (#a) {
$count{$value}++;
}
$max_value = (sort {$count{$b} <=> $count{$a}} #a)[0];
print "Max value = $max_value, occur $count{$max_value} times\n";
Expected result: Max value =13 14, occur 3 times
max_by from List::UtilsBy will return all values that share the maximum in list context.
use strict;
use warnings;
use List::UtilsBy 'max_by';
my #a = (undef,11,12,13,14,15,13,13,14,14);
my %count;
$count{$_}++ for #a;
my #max_values = max_by { $count{$_} } keys %count;
Your code simply takes the first maximal value it finds in the sorted data. You need to continue reading array elements until you reach one that is no longer maximal.
However, as you probably have to test all the hash values there's no great advantage to sorting it. You can just traverse it and keep track of the maximal value(s) you find.
my #a = (undef,11,12,13,14,15,13,13,14,14);
my %count;
$count{$_}++ for #a;
my ($max_count, #max_values);
while ( my ($k,$v) = each %count) {
if ($v > $max_count) {
#max_values = ($k);
$max_count = $v;
}
elsif ($v == $max_count) {
push #max_values, $k;
}
}
my $max_values = join " ", sort #max_values;
print "Max value = $max_values, occur $max_count times\n";
Note that undef is not a valid hash key - it gets converted to "".

Perl: Adding more elements to array created from hash

I have a perl script where we are converting Hash into array-
my %info = (
'name' => $1,
'ip' => $2,
'parent' => '',
'isLocal' => 0,
'clip' => 0,
);
my #siteinfos = ();
push( #siteinfos, \%info );
Now I am able to accesss the array by-
foreach my $site(#siteinfos) {
if ( $site->{'name'} eq $sitename) {
.....
}
First I am not sure how this conversion actually works.Secondly,Now I want to add more elements to this array in (key,pair) format.How can I do so?
First, there is no conversion.
my #siteinfos = ();
defines an array #siteinfos and sets to the empty list. Of course, you could have achieved the same result by using just
my #siteinfos;
Next, the statement
push( #siteinfos, \%info );
pushes a reference to the hash %info to #siteinfos as the sole element of that array.
Next, the loop
foreach my $sinfo (#siteinfos) { # assuming original is a typo
ends up aliasing $sinfo to that sole hash reference in #siteinfos. In the body of the loop, you access an element of the original hash via the reference you pushed to #siteinfos.
Now, things would have been different if you had done:
push #siteinfos, %info;
That would have set #siteinfos to a list like the following (the order is non-deterministic):
$VAR1 = [
'ip',
undef,
'clip',
0,
'isLocal',
0,
'name',
undef,
'parent',
''
];
The undef are there because the match variables were empty when I ran the script. Now, if you want to push another key-value pair to this array, that's trivial:
push #siteinfos, $key, $value;
But of course, by assigning the hash to an array, you are losing the ability to simply look up values by key. In addition, you can keep pushing duplicate keys to an array, but if you assign it back to another hash:
my %otherinfo = #siteinfos;
then only the last version of an even-indexed element (key) and its corresponding odd-indexed element (value) will survive.
You are not converting anything. You are making a new array named #siteinfos.
my #siteinfos = ();
Then you add one entry, which is a reference to the hash %info.
push( #siteinfos, \%info );
The array now holds a reference to %info. That's $siteinfo[0].
In your loop you iterate all the elements in #siteinfos. The hashref now goes into $sinfo.
If you want to add more keys and values to that hashref, just put them in.
foreach my $sinfo (#siteinfos) {
if ( $sinfo->{'name'} eq $sitename) {
$sinfo->{foo} = "bar"; # as simple as this
}
}

Modifications to array also change other array

I have two global multidimensional arrays #p and #p0e in Perl. This is part of a genetic algorith where I want to save certain keys from #p to #p0e. Modifications are then made to #p. There are several subroutines that make modifications to #p, but there's a certain subroutine where on occasion (not on every iteration) a modification to #p also leads to #p0e being modified (it receives the same keys) although #p0e should not be affected.
# this is the sub where part of #p is copied to #p0e
sub saveElite {
#p0e = (); my $i = 0;
foreach my $r (sort({$a<=>$b} keys $f{"rank"})) {
if ($i<$elN) {
$p0e[$i] = $p[$f{"rank"}{$r}]; # save chromosome
}
else {last;}
$i++;
}
}
# this is the sub that then sometimes changes #p0e
sub mutation {
for (my $i=0; $i<#p; $i++) {
for (my $j=0; $j<#{$p[$i]}; $j++) {
if (rand(1)<=$mut) { # mutation
$p[$i][$j] = mutate($p[$i][$j]);
}
}
}
}
I thought maybe I'd somehow created a reference to the original array rather than a copy, but because this unexpected behaviour doesn't happen on every iteration this shouldn't be the case.
$j = $f{"rank"}{$r};
$p0e[$i] = $p[$j];
$p[$j] is an array reference, which you can think of as pointing to a particular list of data at a particular memory address. The assignment to $p0e[$i] also tells Perl to let the $i-th row of #p0e also refer to that same block of memory. So when you later make a change to $p0e[$i][$k], you'll find the value of $p[$j][$k] has changed too.
To fix this, you'll want to assign a copy of $p[$j]. Here is one way you can do that:
$p0e[$i] = [ #{$p[$j]} ];
#{$p[$j]} deferences the array reference and [...] creates a new reference for it, so after this statement $p0e[$i] will have the same contents with the same values as $p[$j] but point to a different block of memory.
I think your problem will probably be this:
$p0e[$i] = $p[$f{"rank"}{$r}]; # save chromosome
Because it looks like #p is a multi-dimensional array.
The problem is - the way perl 'does' multi dimensional arrays is via arrays of references. So if you copy an inner array, you do so by reference.
E.g.:
#!c:\Strawberry\perl\bin
use strict;
use warnings;
use Data::Dumper;
my #list = ( [ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ], );
print Dumper \#list;
my #other_list;
push ( #other_list, #list[0,1] ); #make a sub list of two rows;
print Dumper \#other_list;
### all looks good.
## but if we:
print "List:\n";
print join ("\n",#list),"\n";
print "Other List:\n";
print join ("\n", #other_list),"\n";
$list[1][1] = 9;
print Dumper \#other_list;
You will see that by changing an element in #list we also modify #other_list - and if we just print them we get:
List:
ARRAY(0x2ea384)
ARRAY(0x12cef34)
ARRAY(0x12cf024)
Other List:
ARRAY(0x2ea384)
ARRAY(0x12cef34)
Note the duplicate numbers - that means you have the same reference.
The easiest way of working around this is by using [] judicously:
push ( #other_list, [#{$list[0]}], [#{$list[1]}] ); #make a sub list of two rows;
This will then insert anonymous arrays (new ones) containing the dereferenced elements of the list.
Whilst we're at it though - please turn on strict and warnings. They will save you a lot of pain in the long run.
That's because it's an array of arrays. The first level array stores only references to the inner arrays, if you modify the inner array, it's changed in both arrays - they both refer to the same array. Clone the deep copy instead of creating a shallow one.

Perl - How do I update (and access) an array stored in array stored in a hash?

Perhaps I have made this more complicated than I need it to be but I am currently trying to store an array that contains, among other things, an array inside a hash in Perl.
i.e. hash -> array -> array
use strict;
my %DEVICE_INFORMATION = {}; #global hash
sub someFunction() {
my $key = 'name';
my #storage = ();
#assume file was properly opened here for the foreach-loop
foreach my $line (<DATA>) {
if(conditional) {
my #ports = ();
$storage[0] = 'banana';
$storage[1] = \#ports;
$storage[2] = '0';
$DEVICE_INFORMATION{$key} = \#storage;
}
elsif(conditional) {
push #{$DEVICE_INFORMATION{$key}[1]}, 5;
}
}#end foreach
} #end someFunction
This is a simplified version of the code I am writing. I have a subroutine that I call in the main. It parses a very specifically designed file. That file guarantees that the if statement fires before subsequent elsif statement.
I think the push call in the elsif statement is not working properly - i.e. 5 is not being stored in the #ports array that should exist in the #storage array that should be returned when I hash the key into DEVICE_INFORMATION.
In the main I try and print out each element of the #storage array to check that things are running smoothly.
#main execution
&someFunction();
print $DEVICE_INFORMATION{'name'}[0];
print $DEVICE_INFORMATION{'name'}[1];
print $DEVICE_INFORMATION{'name'}[2];
The output for this ends up being... banana ARRAY(blahblah) 0
If I change the print statement for the middle call to:
print #{$DEVICE_INFORMATION{'name'}[1]};
Or to:
print #{$DEVICE_INFORMATION{'name'}[1]}[0];
The output changes to banana [blankspace] 0
Please advise on how I can properly update the #ports array while it is stored inside the #storage array that has been hash'd into DEVICE_INFORMATION and then how I can access the elements of #ports. Many thanks!
P.S. I apologize for the length of this post. It is my first question on stackoverflow.
I was going to tell you that Data::Dumper can help you sort out Perl data structures, but Data::Dumper can also tell you about your first problem:
Here's what happens when you sign open-curly + close-curly ( '{}' ) to a hash:
use Data::Dumper ();
my %DEVICE_INFORMATION = {}; #global hash
print Dumper->Dump( [ \%DEVICE_INFORMATION ], [ '*DEVICE_INFORMATION ' ] );
Here's the output:
%DEVICE_INFORMATION = (
'HASH(0x3edd2c)' => undef
);
What you did is you assigned the stringified hash reference as a key to the list element that comes after it. implied
my %DEVICE_INFORMATION = {} => ();
So Perl assigned it a value of undef.
When you assign to a hash, you assign a list. A literal empty hash is not a list, it's a hash reference. What you wanted to do for an empty hash--and what is totally unnecessary--is this:
my %DEVICE_INFORMATION = ();
And that's unnecessary because it is exactly the same thing as:
my %DEVICE_INFORMATION;
You're declaring a hash, and that statement fully identifies it as a hash. And Perl is not going to guess what you want in it, so it's an empty hash from the get-go.
Finally, my advice on using Data::Dumper. If you started your hash off right, and did the following:
my %DEVICE_INFORMATION; # = {}; #global hash
my #ports = ( 1, 2, 3 );
# notice that I just skipped the interim structure of #storage
# and assigned it as a literal
# * Perl has one of the best literal data structure languages out there.
$DEVICE_INFORMATION{name} = [ 'banana', \#ports, '0' ];
print Data::Dumper->Dump(
[ \%DEVICE_INFORMATION ]
, [ '*DEVICE_INFORMATION' ]
);
What you see is:
%DEVICE_INFORMATION = (
'name' => [
'banana',
[
1,
2,
3
],
'0'
]
);
So, you can better see how it's all getting stored, and what levels you have to deference and how to get the information you want out of it.
By the way, Data::Dumper delivers 100% runnable Perl code, and shows you how you can specify the same structure as a literal. One caveat, you would have to declare the variable first, using strict (which you should always use anyway).
You update #ports properly.
Your print statement accesses $storage[1] (reference to #ports) in wrong way.
You may use syntax you have used in push.
print $DEVICE_INFORMATION{'name'}[0], ";",
join( ':', #{$DEVICE_INFORMATION{'name'}[1]}), ";",
$DEVICE_INFORMATION{'name'}[2], "\n";
print "Number of ports: ", scalar(#{$DEVICE_INFORMATION{'name'}[1]})),"\n";
print "First port: ", $DEVICE_INFORMATION{'name'}[1][0]//'', "\n";
# X//'' -> X or '' if X is undef

Resources