Print array multiple variables - perl

Kindly advice I am unable to print value of a ,b &c
my #array = qw($a,$b,$c);
push(#new_array ,#array);
for my $x (#new_array)
{
DEBUG(" DEBUG : $x);
}
It showing me $a,$b,$c instead of there values.
Thanks,

The qw operator creates a list of strings. It quotes words, so it's "qw".
my #array = qw( x y z );
is exactly the same as
my #array = ( 'x', 'y', 'z' );
Your line:
my #array = qw($a,$b,$c);
is saying
my #array = ( '$a,$b,$c' );
What you want is to drop the qw.
my #array = ( $a, $b, $c );

Related

Perl to sort words by user-defined alphabet sequence

I have an array of "words" (strings), which consist of letters from an "alphabet" with user-defined sequence. E.g my "alphabet" starts with "ʔ ʕ b g d", so a list of "words" (bʔd ʔbg ʕʔb bʕd) after sort by_my_alphabet should be ʔbd ʕʔb bʔd bʕd.
sort by_my_alphabet (bʔd ʔbg ʕʔb bʕd) # gives ʔbd ʕʔb bʔd bʕd
Is there a way to make a simple subroutine by_my_alphabet with $a and $b to solve this problem?
Simple, and very fast because it doesn't use a compare callback, but it needs to scan the entire string:
use utf8;
my #my_chr = split //, "ʔʕbgd";
my %my_ord = map { $my_chr[$_] => $_ } 0..$#my_chr;
my #sorted =
map { join '', #my_chr[ unpack 'W*', $_ ] } # "\x00\x01\x02\x03\x04" ⇒ "ʔʕbgd"
sort
map { pack 'W*', #my_ord{ split //, $_ } } # "ʔʕbgd" ⇒ "\x00\x01\x02\x03\x04"
#unsorted;
Optimized for long strings since it only scans a string up until a difference is found:
use utf8;
use List::Util qw( min );
my #my_chr = split //, "ʔʕbgd";
my %my_ord = map { $my_chr[$_] => $_ } 0..$#my_chr;
sub my_cmp($$) {
for ( 0 .. ( min map length($_), #_ ) - 1 ) {
my $cmp = $my_ord{substr($_[0], $_, 1)} <=> $my_ord{substr($_[1], $_, 1)};
return $cmp if $cmp;
}
return length($_[0]) <=> length($_[1]);
}
my #sorted = sort my_cmp #unsorted;
Both should be faster than Sobrique's. Theirs uses a compare callback, and it scans the entire strings being compared.
Yes.
sort can take any function that returns a relative sort position. All you need is a function that correctly looks up the 'sort value' of a string for comparing.
So all you need to do here is define a 'relative weight' of your extra letters, and then compare the two.
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my #sort_order = qw ( B C A D );
my #array_to_sort = qw ( A B C D A B C D AB BB CCC ABC );
my $count = 0;
my %position_of;
$position_of{$_} = $count++ for #sort_order;
print Dumper \%position_of;
sub sort_by_pos {
my #a = split //, $a;
my #b = split //, $b;
#iterate one letter at a time, using 'shift' to take it off the front
#of the array.
while ( #a and #b ) {
my $result = $position_of{shift #a} <=> $position_of{shift #b};
#result is 'true' if it's "-1" or "1" which indicates relative position.
# 0 is false, and that'll cause the next loop iteration to test the next
#letter-pair
return $result if $result;
}
#return a value based on remaining length - longest 'string' will sort last;
#That's so "AAA" comparing with "AA" comparison actually work,
return scalar #a <=> scalar #b;
}
my #new = sort { sort_by_pos } #array_to_sort;
print Dumper \#new;
Bit of a simple case, but it sorts our array into:
$VAR1 = [
'B',
'B',
'BB',
'C',
'C',
'CCC',
'A',
'A',
'AB',
'ABC',
'D',
'D'
];

Perl Passing arguments to subroutine not working

I'm trying to pass parameters to a perl subroutine and for whatever reason inside the subroutine the parameters are coming out empty.
...
...
...
print "Passing arguments $a, $b, $c, $d \n";
beforeEnd($a, %b, $c, $d);
sub beforeEnd() {
my ($a, %b, $c, $d) = #_;
print "a is $a, b is $b, c is $c, d is $d \n";
}
The output of the print statements give me an idea that something is wrong. The weird part? The first 2 parameters are passing properly.
> Passing arguments 1, (1,2,3), 2, 3
> a is 1, b is (1,2,3), c is , d is
Any help would be greatly appreciated.
Because when you pass arguments into or out of a subroutine, any hashes and arrays are smashed flat.
You are assigning into %b which will gobble up any arguments.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
sub test1 {
my ( $first, #rest, $last ) = #_;
print Dumper \#rest;
print "First = $first, last = $last, rest = #rest\n";
}
sub test2 {
my ( $first, $second ) = #_;
print "#$first ; #$second";
}
test1 ( 1, 2, 3, 4 );
test2 ( [1,2], [ 3,4] );
my #list1 = ( 1,2,3,4 );
my #list2 = ( 5,6,7,8 );
test1 ( #list1, #list2 );
test2 ( \#list1, \#list2 );
If you want to keep arrays or hashes intact, you need to either pass them by reference or as the last argument.
You would also probably get a warning if you turned on strict and warnings here - which is one of the reasons it's strongly recommended - because $b and %b are not the same. You'd also get a warning about an odd number of assignments:
Odd number of elements in hash assignment at line 5.
Use of uninitialized value $b in print
When arguments are passed to a Perl subroutine, they are flattened into a single List represented by #_. Conceptually, this means that if you are not passing references to arrays or hashes, you will "lose" some data. "Lose" is not exactly correct, because all of the data is still there; it is just not in the variable you expect. An example of this could be:
sub f {
my (#a, #b) = #_;
say 'a: ' . join(', ', #a);
say 'b: ' . join(', ', #b);
}
f( qw(1 2 3), qw(a b c) );
You will get the following output:
a: 1, 2, 3, a, b, c
b:
This is happening because the first array #a consumes all of the values from #_ and there a no more left to be stored in #b. The same thing is happening with the hash in your beforeEnd subroutine. The values of $c and $d are getting stored inside of %b. As an example since I can't see the variables values, if you passed
beforeEnd(1, ( a => 1, b => 2 ), 'c', 3);
inside your sub, you get something like this:
$a = 1
%b = ( a => 1, b => 2, c => 3 )
$c = undef
$d = undef
You can solve this by passing a reference to your hash %b:
beforeEnd($a, \%b, $c, $d);
Subroutines accept a list of scalar as arguments. If you pass an array or a hash, the contents of the array or hash is passed instead. That means that
f($a, %b, $c, $d)
is the same as
f($a, $b_key_1, $b_val_1, $b_key_2, $b_val_2, $b_key_3, $b_val_3, $c, $d);
How many of the scalars in #_ should be assigned to %b? Perl keeps it simple and assigns all remaining scalars, so
my ($a, %b, $c, $d) = #_;
is really no different than
my $a = $_[0]; # The first argument
my %b = #_[1..$#_]; # All but the first argument
my $c;
my $d;
It's best if you pass a reference to the hash. That avoids the problem, and it's far more efficient.
use Data::Dumper qw( Dumper );
sub beforeEnd {
my ($a, $b, $c, $d) = #_;
local $Data::Dumper::Terse = 1;
print "a is $a, b is ".Dumper($b).", c is $c, d is $d \n";
}
beforeEnd($a, \%b, $c, $d);
Off-topic comments about your code:
You had a prototype indicating no arguments are expected (), but you expect four. Rid yourself of that prototype.
You should avoid using $a and $b as variables as it can issues with sort.
The arguments can be passed in subroutine only as list of scalar variables.
We need to pass the reference of the hash (or an array, object) whenever passing the arguments to subroutine.
...
...
...
print "Passing arguments $a, $b, $c, $d \n";
beforeEnd($a, \%b, $c, $d);
sub beforeEnd() {
my ($a, $b, $c, $d) = #_;
print "a is $a, b is %$b, c is $c, d is $d \n";
}

Split hash in hash of array

I have three hashes that I would like to explode each one into a Hash of Arrays. Each one could be passed into a subroutine with a split function, but this requires the same subroutine to be called 3 times. I have tried to iterate through each hash and them split it but without success. Is it possible to do this without calling a subroutine? The code I have tried is:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my %hash1 = (
A => "AAAAAAAAAA",
B => "AAAAANAAAA",
C => "AAAAAXAAAA",
D => "AAXAAAAAAA",
E => "AAAAAAAAAX",
);
my %hash2 = (
F => "BBBBBBBBBB",
G => "BBBBBBBBBB",
H => "BBXBBBBBBB",
I => "BBBBBBBBBB",
J => "AAAAANAAAA",
);
foreach my $ref ( \%hash1, \%hash2 ) {
while ( my ($key, $value) = each %$ref) {
#{ $ref->{$key} } = split (//, $value );
}
}
print Dumper %hash1;
but gives the warning:
Can't use string ("AAAAAAAAAA") as an ARRAY ref while "strict refs" in use
Thanks in advance.
Your mistake is that you are taking the value of the key $ref->{$key} which is AAAAAAA etc, and using it as a reference with the #{ ... } braces. You need to assign to $ref->{$key} directly.
Since you are splitting the original value into a list, you need an array to store it. You can do this either by using a named array, lexically scoped, or an anonymous array. My choice would be to use an anonymous array, but a named array might appear more readable:
foreach my $ref ( \%hash1, \%hash2 ) {
while ( my ($key, $value) = each %$ref) {
$ref->{$key} = [ split (//, $value ) ]; # using anonymous array
# my #list = split (//, $value );
# $ref->{$key} = \#list; # using named array
}
}
This process would overwrite the original values, but I assume that is what you want.
A more direct way to achieve the same thing would be this:
$_ = [ split // ] for values %hash1, values %hash2;
Here we are (ab)using the fact that the elements in a for loop are aliased to the original variables, and simply overwriting the values the same way we did above.
If the compressed format is unwanted, a more verbose alternative would be:
for my $value (values %hash1, values %hash2) {
$value = [ split //, $value ];
}
Assuming I correctly understood what you want to do, you can use hash slicing and do something like this:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my %hash1 = (
A => "AAAAAAAAAA",
B => "AAAAANAAAA",
C => "AAAAAXAAAA",
D => "AAXAAAAAAA",
E => "AAAAAAAAAX",
);
my %hash2 = (
F => "BBBBBBBBBB",
G => "BBBBBBBBBB",
H => "BBXBBBBBBB",
I => "BBBBBBBBBB",
J => "AAAAANAAAA",
);
foreach my $ref ( \%hash1, \%hash2 ) {
my #keys = keys %$ref;
#$ref{#keys} = map { [split //, $ref->{$_}] } #keys;
}
print Dumper \%hash1;
print Dumper \%hash2;
This is like writing:
foreach my $ref ( \%hash1, \%hash2 ) {
my #keys = keys %$ref;
foreach my $key (#keys) {
my #arr = split //, $ref->{$key};
$ref->{$key} = \#arr;
}
}
just simpler.

Assignment of multiple array subroutine parameters in Perl doesn't work

I'm confused about perl subroutine parameters in this example
when i use references in subroutine parameters it works:
#a = ( 1, 2 );
#b = ( 5, 8 );
#c = add_vecpair( \#a, \#b );
print "#c\n";
print $a[0];
sub add_vecpair { # assumes both vectors the same length
my ( $x, $y ) = #_; # copy in the array references
my #result;
#$x[0] = 2;
for ( my $i = 0; $i < #$x; $i++ ) {
$result[$i] = $x->[$i] + $y->[$i];
}
return #result;
}
but when i don't use references as parameters like this:
#a = ( 1, 2 );
#b = ( 5, 8 );
#c = add_vecpair( #a, #b );
print "#c\n";
print $a[0];
sub add_vecpair { # assumes both vectors the same length
my ( #x, #y ) = #_; # copy in the array references
my #result;
print #y;
for ( my $i = 0; $i < #x; $i++ ) {
$result[$i] = $x[$i] + $y[$i];
}
return #result;
}
..it doesn't work. When do i need to use references as subroutine parameters?
Short version: The issue is this line:
my (#x, #y) = #_;
Assignments are greedy. #x is treated first, and is given as many values from #_ as it can handle. And as it can handle all of them, it ends up getting all of the contents of #_, and #y get none.
The result is the same as this:
my #x = #_; # Gets all of the arguements
my #y; # Gets nothing, and is therefore declared but uninitialized.
This is why using references is recommended when subroutines take more than one value as arguement, and at least one of those values are arrays or hashes.
Longer version:
#_ is a composite of all of the arguements passed to the subroutine, so the original container doesn't matter. Consider the code snippets below. The first one is yours, the second one does the exact same thing, but more clearly displays what is happening.
#a = (1, 2);
#b = (5, 8);
add_vecpair(#a,#b);
....is the same as:
add_vecpair(1, 2, 5, 8);
To further hilight the problem, hashes get really messy if treated this way:
%a = ('a' => 1,
'b' => 2);
%b = ('c' => 3,
'd' => 4);
somefunction(%a, %b);
...is the same as:
somefunction('a', 1, 'b', 2, 'c', 3, 'd', 4);
When you call Perl subroutines with array or hash parameters, they are flattened out to a single list. Therefore in the second case your two array parameters loose their identities and #_ becomes a single array with the elements of both #a and #b.

Programatic access of a hash element

Can anyone make this print "4" by replacing the PFM block??
my %hash;
$hash{1}{2}{3}=4;
my #key=qw(1 2 3);
my $key;
for(#key){PFM}
print $hash{$key}
my %hash;
$hash{1}{2}{3}=4;
my #key=qw(1 2 3);
my $data = \%hash;
for(#key){
$data = $data->{$_}
}
print $data
my $val = \%hash;
$val //= $val->{$_} for #key;
say $val;
or you could use Data::Diver
use Data::Diver qw( Dive );
say Dive(\%hash, #key);
Neither version will vivify anything if any part of the key doesn't exist.
If you want to set a value using such a key:
my $p = \\%hash;
$p = \( $$p->{$_} ) for #key;
$$p = 5;
or
use Data::Diver qw( DiveRef );
my $ref = DiveRef(\%hash, map \$_, #key);
$$ref = 5;
or
use Data::Diver qw( DiveVal );
DiveVal(\%hash, map \$_, #key) = 5;
(The map \$_, is required to make Data::Diver make hashes instead of arrays for numerical keys.)
Yes. But it's probably not what you wanted:
$key = "X";
$hash{X} = 4;
4 is not a value of the %hash originally:
my #fours = grep $_ == 4, values %hash;
print "[#fours]\n"; # prints '[]'