perl push many array in one array - perl

I have many short array
#seq1 /773..1447/ #seq2 /1 2 1843..1881 1923..2001/
but i use push
push(#add, #seq1);
push(#add, #seq2);
but it shows like it combine all array into one can't get each sub-array any more
/773..1447 1 2 1843..1881 1923..2001/
when i use
$number=#add;
it shows 6, but it should be 2. Can anyone explain the reason and how to change it.
When i use for loop to add each array
for(..){
#temp= split(/,/,$_);
push(#add, \#temp);
}
Then when i print #add; it only shows memory address, How can show all data in #add

This is normal behavior, use reference to #seq1 if you want #add to be two dimensional array,
push(#add, \#seq1);
To print all values in #add you should use Data::Dumper; print Dumper \#add;
The reason is that all parameters get flattened into list when they are pushed into array, so
#a = #b = (1,2);
push(#add, #a, #b);
is same as writing
push(#add, $a[0],$a[1], $b[0],$b[1]);
Check perlref and perllol for reference.

the push command takes an ARRAY and a LIST. It is important to understand what happens here.
The first argument must be an ARRAY, the one you want to push things on. After that it expects a LIST. What this means is that this push statement provides list context to any #seq_n array - and sort of expands the array into separate elements. So all the elements of the #seq_n are being pushed onto your #add.
Since you did not want that to happen, you wanted an array that holds the separate lists - what we call in Perl a List-of-Lists - you actually wanted to push a reference to your #seq_n arrays, using the \ character.
push #add, \#seq_1, \#seq_2, . . . \#seq_n;
Now you have an array that indeed holds references to each $seq_n.
To print them neatly, each sequence on its own line, you could iterate over each
foreach my $seq (#add) {
# $seq holds a reference to a list!
my $string = join " ", #$seq; # the # dereferences the $seq
print $string, "\n";
}
but TIMTOWTDI
print map {(join " ", #$_), "\n"} #add;
Always consider the context in Perl, and try to embrace the charms of join, grep and map.

Related

Why can't I assign a variable to a hash entry in Perl?

Sorry, I'm super rusty with Perl. See the following code:
foreach my $hash (keys %greylist)
{
$t = $greylist{$hash};
print $greylist{$hash}[4] . "\n";
print $t[4] . "\n";
}
Why does $t[4] evaluate to a blank string, yet $greylist{$hash}[4] which should be the same thing evaluates to an IP address?
$greylist{$hash} contains an array reference. When you do:
print $greylist{$hash}[4];
Perl automatically treats it as an array reference but when you do:
$t = $greylist{$hash};
print $t[4];
You're assigning the array reference to a scalar variable, $t, then attempting to access the 5th element of another variable, #t. use strict would give you an error in this scenario.
Use the arrow operator, ->, to dereference:
$t = $greylist{$hash};
print $t->[4];
perlreftut has a note about this:
If $aref holds a reference to an array, then $aref->[3] is the fourth element of the array. Don't confuse this with $aref[3] , which is the fourth element of a totally different array, one deceptively named #aref . $aref and #aref are unrelated the same way that $item and #item are.

Why does my perl hash deform my array data?

I have encountered some interesting behavior in Perl, and was hoping to be enlightened.
As you can see I create an array called #tuple that has two array references. When I assign it to a key and extract it I only get the first array VALUE as apposed to the reference. Can anyone tell me why this is happening?
my #VMstoreName = ($storeName[$i]);
my #VMdiskCap = ($diskCap[$i]);
print "VMstoreName : ". join(' ', #VMstoreName) . "\n";
print "VMdiskCap : ". join(' ', #VMdiskCap) . "\n";
# Initializing our key
my #tuple = (\#VMstoreName, \#VMdiskCap);
print "After tuple " . join(' ', #tuple) . "\n";
#virtualMachines{$vmNames[$i]} = #tuple;
my #DEBUG = #{$virtualMachines{$vmNames[$i]}};
print "After first map : " . join(' ', #DEBUG) . "\n";
print "WHAT??? $DEBUG[0] $DEBUG[1]!!!\n";
Here is the output, I want After first map to read exactly like After tuple
VMstoreName : 172.16.1.3:/export/Paul/test-0
VMdiskCap : 1
After tuple ARRAY(0x2c4ccf0) ARRAY(0x2c4cd38)
After first map 172.16.1.3:/export/Paul/test-0
WHAT??? 172.16.1.3:/export/Paul/test-0 !!!
Your problem begins here (I guess):
#virtualMachines{$vmNames[$i]} = #tuple;
#hash{...} is a hash slice: It accesses mutiple entries in the hash at once (this may be only one entry). A slice as LHS value imposes list context.
To this list we assign the values of #tuple.
Now list assignment has the property of forgetting superfluous values:
my ($x, $y) = (1, 2, 3); # nobody likes № 3
So the above statement is the same as
$virtualMachines{ $vmNames[$i] } = $tuple[0];
Specifically, this assigns an array reference to the %virtualMachines entry, as #tuple only holds arrayrefs.
On the next line, you access this entry via $virtualMachines{$vmNames[$i]}. This evaluates to an array reference. You then dereference this to an array with #{ ... }.
This assigns all the values of that array to #DEBUG, not the array reference. As seen in your debug statement, this is the VMstoreName.
Solution
Assign an arrayref to the %virtualMachines entry:
$virtualMachines{ $vmNames[$i] } = \#tuple;
Arrays and hashes can only hold scalars, so we can't use an array as value, only an array reference.
You are assigning to your Hash in list context using the same key. Keys have to be unique in hashes, so only one reference gets stored inside of your Hash. The Hash-slice is only 1-element wide so the second reference just gets ignored.
You should assign a reference to #tuple instead using scalar context:
$virtualMachines{$vmNames[$i]} = \#tuple;
You are passing references to arrays when you initialize #tuple. Instead, just pass the arrays:
my #tuple = (#VMstoreName, #VMdiskCap);
It looks like #tuple is an array of array references. You'll have to deference each element to print them out the way you intend to.
my #tuple = (\#VMstoreName, \#VMdiskCap);
print "After tuple " . join(' ', map { #$_ } #tuple) . "\n";

How to get sub array?

I have the code below:
#a = ((1,2,3),("test","hello"));
print #a[1]
I was expecting it to print
testhello
But it gives me 2.
Sorry for the newbie question (Perl is a bit unnatural to me) but why does it happen and how can I get the result I want?
The way Perl constructs #a is such that it is equivalent to your writing,
#a = (1,2,3,"test","hello");
And that is why when you ask for the value at index 1 by writing #a[1] (really should be $a[1]), you get 2. To demonstrate this, if you were to do the following,
use strict;
use warnings;
my #a = ((1,2,3), ("test","hello"));
my #b = (1,2,3,"test","hello");
print "#a\n";
print "#b\n";
Both print the same line,
1 2 3 test hello
1 2 3 test hello
What you want is to create anonymous arrays within your array - something like this,
my #c = ([1,2,3], ["test","hello"]);
Then if you write the following,
use Data::Dumper;
print Dumper $c[1];
You will see this printed,
$VAR1 = [
'test',
'hello'
];
Perl lists are one-dimensional only, which means (1,2,(3,4)) is automatically flattened to (1,2,3,4). If you want a multidimensional array, you must use references for any dimension beyond the first (which are stored in scalars).
You can get any anonymous array reference with bracket notation [1,2,3,4] or reference an existing array with a backslash my $ref = \#somearray.
So a construct such as my $aref = [1,2,[3,4]] is an array reference in which the first element of the referenced array is 1, the second element is 2, and the third element is another array reference.
(I find when working with multidimensional arrays, that it's less confusing just to use references even for the first dimension, but my #array = (1,2,[3,4]) is fine too.)
By the way, when you stringify a perl reference, you get some gibberish indicating the type of reference and the memory location, like "ARRAY(0x7f977b02ac58)".
Dereference an array reference to an array with #, or get a specific element of the reference with ->.
Example:
my $ref = ['A','B',['C','D']];
print $ref; # prints ARRAY(0x001)
print join ',', #{$ref}; # prints A,B,ARRAY(0x002)
print join ',', #$ref; # prints A,B,ARRAY(0x002) (shortcut for above)
print $ref->[0]; # prints A
print $ref->[1]; # prints B
print $ref->[2]; # prints ARRAY(0x002)
print $ref->[2]->[0]; # prints C
print $ref->[2][0]; # prints C (shortcut for above)
print $ref->[2][1] # prints D
print join ',', #{$ref->[2]}; # prints C,D
I think you're after an array of arrays. So, you need to create an array of array references by using square brackets, like this:
#a = ([1,2,3],["test","hello"]);
Then you can print the second array as follows:
print #{$a[1]};
Which will give you the output you were expecting: testhello
It's just a matter of wrong syntax:
print $a[1]

Perl: Beginner. Which data structure should I use?

Okay, not sure where to ask this, but I'm a beginner programmer, using Perl. I need to create an array of an array, but I'm not sure if it would be better use array/hash references, or array of hashes or hash of arrays etc.
I need an array of matches: #totalmatches
Each match contains 6 elements(strings):
#matches = ($chapternumber, $sentencenumber, $sentence, $grammar_relation, $argument1, $argument2)
I need to push each of these elements into the #matches array/hash/reference, and then push that array/hash/reference into the #totalmatches array.
The matches are found based on searching a file and selecting the strings based on meeting the criteria.
QUESTIONS
Which data structure would you use?
Can you push an array into another array, as you would push an element into an array? Is this an efficient method?
Can you push all 6 elements simultaneously, or have to do 6 separate pushes?
When working with 2-D, to loop through would you use:
foreach (#totalmatches) {
foreach (#matches) {
...
}
}
Thanks for any advice.
Which data structure would you use?
An array for a ordered set of things. A hash for a set of named things.
Can you push an array into another array, as you would push an element into an array? Is this an efficient method?
If you try to push an array (1) into an array (2), you'll end up pushing all the elements of 1 into 2. That is why you would push an array ref in instead.
Can you push all 6 elements simultaneously, or have to do 6 separate pushes?
Look at perldoc -f push
push ARRAY,LIST
You can push a list of things in.
When working with 2-D, to loop through would you use:
Nested foreach is fine, but that syntax wouldn't work. You have to access the values you are dealing with.
for my $arrayref (#outer) {
for my $item (#$arrayref) {
$item ...
}
}
Do not push one array into another array.
Lists just join with each other into a new list.
Use list of references.
#create an anonymous hash ref for each match
$one_match_ref = {
chapternumber => $chapternumber_value,
sentencenumber => $sentencenumber_value,
sentence => $sentence_value,
grammar_relation => $grammar_relation_value,
arg1 => $argument1,
arg2 => $argument2
};
# add the reference of match into array.
push #all_matches, $one_match_ref;
# list of keys of interest
#keys = qw(chapternumber sentencenumber sentence grammer_relation arg1 arg2);
# walk through all the matches.
foreach $ref (#all_matches) {
foreach $key (#keys) {
$val = $$ref{$key};
}
# or pick up some specific keys
my $arg1 = $$ref{arg1};
}
Which data structure would you use?
An array... I can't really justify that choice, but I can't imagine what you would use as keys if you used a hash.
Can you push an array into another array, as you would push an element into an array? Is this an efficient method?
Here's the thing; in Perl, arrays can only contain scalar variables - the ones which start with $. Something like...
#matrix = ();
#row = ();
$arr[0] = #row; # FAIL!
... wont't work. You will have to instead use a reference to the array:
#matrix = ();
#row = ();
$arr[0] = \#row;
Or equally:
push(#matrix, \#row);
Can you push all 6 elements simultaneously, or have to do 6 separate pushes?
If you use references, you need only push once... and since you don't want to concatenate arrays (you need an array of arrays) you're stuck with no alternatives ;)
When working with 2-D, to loop through would you use:
I'd use something like:
for($i=0; $i<#matrix; $i++) {
#row = #{$matrix[$i]}; # de-reference
for($j=0; $j<#row; $j++) {
print "| "$row[$j];
}
print "|\n";
}
Which data structure would you use?
Some fundamental container properties:
An array is a container for ordered scalars.
A hash is a container for scalars obtained by a unique key (there can be no duplicate keys in the hash). The order of values added later is not available anymore.
I would use the same structure like ZhangChn proposed.
Use a hash for each match.
The details of the match then can be accessed by descriptive names instead of plain numerical indices. i.e. $ref->{'chapternumber'} instead of $matches[0].
Take references of these anonymous hashes (which are scalars) and push them into an array in order to preserve the order of the matches.
To dereference items from the data structure
get an item from the array which is a hash reference
retrieve any matching detail you need from the hash reference

What is the difference between push and unshift in Perl?

Can someone please explain why push behaves the way as shown below?
Basically I am trying to print values of an array populated by push as well unshift.
When I try to print array contents populated by push using array indexes, It always prints the element at the top of the array, whereas array populated by unshift prints contents of array based on array index. I don't understand why.
with unshift
#!/usr/bin/perl
#names = ("Abhijit","Royal Enfield","Google");
#numbers=();
$number=1;
$i=0;
foreach $name (#names) {
#print $_ . "\n";
$number=$number+1;
#push(#numbers,($number));
unshift(#numbers,($number));
print("Array size is :" . #numbers . "\n");
$i=$i+1;
print("Individual Elements are:" . #numbers[i] . "\n");
pop(#numbers);
}
rhv:/var/cl_ip_down>./run.sh
Array size is :1
Individual Elements are:2
Array size is :2
Individual Elements are:3
Array size is :3
Individual Elements are:4
without unshift
#!/usr/bin/perl
#names = ("Abhijit","Royal Enfield","Google");
#numbers=();
$number=1;
$i=0;
foreach $name (#names) {
#print $_ . "\n";
$number=$number+1;
push(#numbers,($number));
#unshift(#numbers,($number));
print("Array size is :" . #numbers . "\n");
$i=$i+1;
print("Individual Elements are:" . #numbers[i] . "\n");
}
rhv:/var/cl_ip_down>./run.sh
Array size is :1
Individual Elements are:2
Array size is :2
Individual Elements are:2
Array size is :3
Individual Elements are:2
/without pop/
#!/usr/bin/perl
#names = ("Abhijit","Royal Enfield","Google");
#numbers=();
$number=1;
$i=0;
foreach $name (#names) {
#print $_ . "\n";
$number=$number+1;
#push(#numbers,($number));
unshift(#numbers,($number));
print("Array size is :" . #numbers . "\n");
$i=$i+1;
print("Individual Elements are:" . #numbers[i] . "\n");
#pop(#numbers);
}
rhv:/var/cl_ip_down>./run.sh
Array size is :1
Individual Elements are:2
Array size is :2
Individual Elements are:3
Array size is :3
Individual Elements are:4
with pop
#!/usr/bin/perl
#names = ("Abhijit","Royal Enfield","Google");
#numbers=();
$number=1;
$i=0;
foreach $name (#names) {
#print $_ . "\n";
$number=$number+1;
#push(#numbers,($number));
unshift(#numbers,($number));
print("Array size is :" . #numbers . "\n");
$i=$i+1;
print("Individual Elements are:" . #numbers[i] . "\n");
pop(#numbers);
}
rhv:/var/cl_ip_down>./run.sh
Array size is :1
Individual Elements are:2
Array size is :1
Individual Elements are:3
Array size is :1
Individual Elements are:4
You really should be using use strict; and use warnings; in your code. Having them activated will allow you to identify errors in your code.
Change all instances of the following:
foreach $name (#names) -> for my $i (#names) as you don't do anything with the elements in the #names array.
#numbers[i] -> $numbers[$i] as this is where you've made a not uncommon mistake of using an array slice rather than referring to an array element.
This is not C. Every 'variable' has to have a sigil ($, #, %, &, etc.) in front of it. That i should really be $i.
As for the difference between push and shift, the documentation explains:
perldoc -f push:
push ARRAY,LIST
Treats ARRAY as a stack, and pushes the values of LIST onto the end of ARRAY. The length of ARRAY increases by the length of LIST. ... Returns the number of elements in the array following the completed "push".
perldoc -f unshift:
unshift ARRAY,LIST
Does the opposite of a shift. Or the opposite of a push, depending on how you look at it. Prepends list to the front of the array, and returns the new number of elements in the array.
To put it ASCII-matically...
+---------+ +-----------+ +---------+
<----- | ITEM(S) | -----> | (#) ARRAY | <----- | ITEM(S) | ----->
shift +---------+ unshift +-----------+ push +---------+ pop
^ ^
FRONT END
unshift is used to add a value or values onto the beginning of an array:
Does the opposite of a shift. Or the opposite of a push, depending on how you look at it.
The new values then become the first elements in the array.
push adds elements to the end of an array:
Treats ARRAY as a stack, and pushes the values of LIST onto the end of ARRAY.
This should really be a comment but it is too long for a comment box, so here it is.
If you want to illustrate the difference between unshift and push, the following would suffice:
#!/usr/bin/perl
use strict; use warnings;
my #x;
push #x, $_ for 1 .. 3;
my #y;
unshift #y, $_ for 1 .. 3;
print "\#x = #x\n\#y = #y\n";
Output:
#x = 1 2 3
#y = 3 2 1
Note use strict; protects you against many programmer errors and use warnings; warns you when you use constructs of dubious value. At your level, neither is optional.
Note that
the preallocated array is balanced toward the 0 end of the array (meaning there is more free space at the far end of the list than there is before the list's 0 element). This is done purposely to make pushes more efficient than unshifts.
http://www.perlmonks.org/?node_id=17890
Although lists do quite fine as "Perl is smartly coded because the use of lists as queues was anticipated (Ibid.)".
For comparison, in various JavaScript engines shift/unshift on arrays seems to be significantly slower.
I haven't seen any articulation of what these methods actually do in terms of operational complexity, which is what helped me with conceptualization: quintessentially, I believe it is called "shift" because it actually has to shift all of the n elements in your array to new indices in order to properly update the length property.
push() and pop() use simpler operational complexity. No matter what the number of n values in your array, or your array.length, push or pop will always execute 1 operation. It doesn't need to deal with indexes, it doesn't need to iterate, it only needs to execute one operation, always at the end of the stack, either adding or removing a value and index.
Most importantly, notice when using push/pop, that the other elements in the array are not affected - they are the same values in the same indices of your array. The length of the array is also automatically updated properly to what you'd expect when removing or adding values.
On the other hand, shift() and unshift() not only add or remove, but also have to actually "shift" all of the other elements in your array into different indices. This is more complex and takes more time because the amount of operations is dependent on n, the number of elements in your array, or array.length. For every n+1 larger, it has to do 1 more operation to shift each of the values into the correct index, properly updating the length properly.
Otherwise, if it didn't perform n operations after shift() and move the other elements, you would have no element at index 0, and it wouldn't change the length of your array, would it? We want the length of our arrays to update intuitively, and shift and unshift have to execute more operations to accomplish this.