Ok I realize my original question was really bad. Here goes take 2. Our goal is to replicate the behaviour the folowing commands in Perl using only "splice".
my #arr = qw (A B C D);
#arr = ();
#or
#arr;
#replace the commands above.
I used
splice(#arr)
to replace the command. But I was wondering if there's a difference between writing
#arr = ();
and
#arr;
my #arr = ();
is a cluttered, inefficient way of writing
my #arr;
What it does: It creates a new empty lexically-scopped array named #arr.
Lexically-scoped means #arr is only visible (i.e. can only be used) within the inner-most curlies (or file) that contains it.
Assigning an empty list to array empties it, but newly created arrays are guaranteed to be empty already.
my #arr = qw( A B C D );
is equivalent to
my #arr = split(' ', q( A B C D ));
It's a convenient (i.e. shorter) way of writing
my #arr = ( 'A', 'B', 'C', 'D' );
What it does: It creates a new lexically-scopped array named #arr, and assigns four strings (A, B, C and D) to it.
You'd get the same result from
my #arr;
$arr[0] = 'A';
$arr[1] = 'B';
$arr[2] = 'C';
$arr[3] = 'D';
(#arr, #arr = ( )) makes no sense. It amounts to #arr = ().
my #arr = qw (A B C D); my #arr = (); makes no sense. It amounts to my #arr;.
my #arr = (); declares a perl variable #arr as local to the scope of the enclosed block. The declaration in the title: #a, #a = () defines #a globally. These links can provide you some help:
How should I use the "my" keyword in Perl?
Why declare Perl variable with "my" at file scope?
and from perldoc:
https://perldoc.perl.org/functions/my.html
if you used modules like use strict, your program would fail if variable were not defined with my.
my #arr = qw(foo bar) is the Perl style. It is more or less convention of the Perl language (e.g. how Python code is Pythonic).
my #arr = declares an array and assigns a list to it.
In your first example, the list is one formed by qw, which "Evaluates to a list of the words extracted out of STRING, using embedded whitespace as the word delimiters." So qw( A B C D ) is the list 'A','B','C','D'. Other delimiters than () can be used, just like with other quote-type operators.
In your second example, you are assigning an empty list to the new array. To make it equivalent to the first example without using qw, it could be:
my #arr = ('A','B','C','D');
or even:
my #arr = 'A'..'D';
qw is there for convenience; most people like it, some do not. Use whatever works for you.
Related
I was wondering if it is possible to make a hash assigning its keys and values at once. Or in general use map and for at one line:
#!/usr/bin/perl
%h = map {$_, $i} qw[a b c] for $i (1..3)
But unfortunatelly not => Number found where operator expected, meant number in the parenthesis. So my question is why am I not able to make double loop by this way? And how otherwise would someone assign hash keys to values (and I dont concern something like $h = {a=>1,b=>2,c=>3} but rather assigning %h = (#keys = #values) ... in other words, how to assign hash by:
2 arrays only (#keys,#values), no scalars
At once (at one line - without block)
Is it even possible in perl?
Populating a hash is simply a matter of assigning a list with alternating keys and values, so you just have to construct the list using the two arrays in an alternating fashion.
use strict;
use warnings;
my #keys = qw(a b c);
my #values = 1..3;
my %h = map { ($keys[$_], $values[$_]) } 0..$#keys;
List::UtilsBy provides a useful abstraction for this in zip_by.
use List::UtilsBy 'zip_by';
my %h = zip_by { #_ } \#keys, \#values;
But actually it's even easier to use slice assignment. Though you technically can't do this in the same statement as the declaration, it's by far the neatest option:
my %h;
#h{#keys} = #values;
Use List::MoreUtils 'zip' or add your own since that module is not a core module:
sub zip(\##){map{($_[0][$_-1],$_[$_])}1..#{$_[0]}}
my %h = zip #keys, #values;
Well, the question is not very clear on 'why?' -- same can be achieved with following code
use strict;
use warnings;
use Data::Dumper;
my $debug = 1;
my %h;
#h{qw(a b c)} = (1..3);
print Dumper(\%h) if $debug;
I am trying to read the contents of a file into a hash
file contents look line,
A|A1
B|B1
C|C1
the code I have is
use strict;
use warnings;
use Data::Dumper;
my $instAttribFileName="DATABYIDENTIFIER_InstCommonAttrList.config";
open(IFH,$instAttribFileName) or die "cannot open file";
my %attribhash = ();
%attribhash = map {chomp; split /\|/} (<IFH>);
print Dumper %attribhash;
Dumper does not print the hash but reads A,A1 etc into seperate variables.
what am I doing wrong here?
According to perldoc perldata:
LISTs do automatic interpolation of sublists. That is, when a LIST is
evaluated, each element of the list is evaluated in list context, and
the resulting list value is interpolated into LIST just as if each
individual element were a member of LIST. Thus arrays and hashes lose
their identity in a LIST
So you need to pass the hash by reference to Dumper() or else it will be flattened into a list of separate arguments. For example, if you have:
my %foo = ( a => 'A', b => 'B');
print Dumper %foo;
Output:
$VAR1 = 'b';
$VAR2 = 'B';
$VAR3 = 'a';
$VAR4 = 'A';
but if you pass a reference to %foo instead (by putting a backslash in front):
print Dumper \%foo;
we get:
$VAR1 = {
'b' => 'B',
'a' => 'A'
};
References:
Data::Dumper
perldoc perlref
How can I print the contents of a hash in Perl?
It's always worth reading all of the documentation for the modules you're trying to use. The "BUGS" section in the Data::Dumper manual says:
Due to limitations of Perl subroutine call semantics, you cannot pass an array or hash. Prepend it with a \ to pass its reference instead.
I have a 2 dimensional array like this:
$map[0][0] = 'a';
$map[0][1] = 'b';
$map[1][0] = 'c';
$map[1][1] = 'd';
I want to pass only everything under $map[1] (by reference) to a subroutine. How to do that ?
Perl doesn't have multiple dimension arrays.
What you have is an array and each element of that array is a reference to another array. You might want to read up about Perl References since this is the way Perl allows you to build some very complex data structures.
Many people think of it as a multidimensional array, and you could treat it as such under certain circumstances. However, I prefer the -> syntax which reminds me that this is merely a reference to a reference.
$map[0]->[0] = 'a';
$map[0]->[1] = 'b';
$map[1]->[0] = 'c';
$map[1]->[1] = 'd';
Now, I can take the data structure apart:
#map: This is an array with two items in it, $map[0] and $map[1].
$map[0]->[]: This is a reference to another array. That array also has to items in it.
$map[1]->[]: This is another reference to yet another array. That array has two items in it.
Note that $map[1]->[] means that $map[1] contains an array reference. Thqt means you can pass $map[1] as your reference to that inner array.
mysub ($map[1]);
Here's a simple program:
#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);
my #map;
$map[0]->[0] = 'a';
$map[0]->[1] = 'b';
$map[1]->[0] = 'c';
$map[1]->[1] = 'd';
mysub( $map[1] );
sub mysub {
my $array_ref = shift;
my #array = #{ $array_ref }; # Dereference your reference
for my $index ( 0..$#array ) {
say "\$map[1]->[$index] = $array[$index]";
}
}
This prints:
$map[1]->[0] = c
$map[1]->[1] = d
Now, you see why I like that -> syntax although it's really completely unnecessary. It helps remind me what I am dealing with.
You can send array reference,
sub mysub {
my ($aref) = #_;
# work with #$aref ..
}
mysub($map[1]);
Simply pass the scalar $map[1].
fn($map[1]);
sub fn
{
my #loc_map_1 = #{ $_[0] };
Remember that perl doesn't have "real" 2 dimensional arrays. In your case map is an array that contains 2 references to arrays.
Here is my code named reverse.pl
#!usr/bin/perl -w
use 5.016;
use strict;
while(my $line=<>)
{
my #array=();
push (#array,$line);
#array=reverse#array;
say #array;
}
Test file named a.txt
A B C D
E F G H
I J K L
M N O P
Q R S T
My command is perl reverse.pl a.txt
Why it can't implement the reverse function?
I want to show the result is:
D C B A
H G F E
and so on.
Reverse in a scalar context reverses a scalar.
Reverse in a list context reverses the list, but not each scalar within the list.
You explicitly turn your scalar $line into a list with one item and then reverse the order of the items.
Try this:
#!/usr/bin/perl
use 5.016;
use strict;
while (my $line=<>) {
chomp($line);
say scalar reverse $line;
}
If you have an array and want to reverse each element (but not the elements), use map:
my #array = qw(Alpha Beta Gamma);
#array = map { scalar reverse $_ } #array;
print "#array\n";
If you want to do both (reverse each element and the elements themselves), do:
#array = map { scalar reverse $_ } reverse #array;
or:
#array = reverse map { scalar reverse $_ } #array;
When you say:
push #array, $line;
You're creating an array of one value that's equal to the line.
$array[0] = "A B C D";
When you say:
#array = reverse #array;
You are reversing that single member array. The first element becomes the last, and the last element becomes the first, etc.. However, you only have one element, so there's nothing to reverse.
What you want to do is create an array with your line:
my #array = split /\s+/, $line;
This will create an array with each character being a separate element of the array. For example, your first line:
$array[0] = "A";
$array[1] = "B";
$array[2] = "C";
$array[3] = "D";
Now, if you use reverse on this array, you'll get:
$array[0] = "D";
$array[1] = "C";
$array[2] = "B";
$array[3] = "A";
Here's the program:
use strict;
use warnings;
use feature qw(say);
while ( my $line = <> ) {
chomp $line;
my #array = split /\s+/, $line;
say join " ", reverse $line;
}
The join function takes an array, and joins each element into a single line -- thus rebuilding your line.
By the way, I could have done this:
#array = reverse #array;
say "#array"; #Quotes are important!
This is because Perl will automatically join an array with whatever character is in $". This is a Perl variable that is used for joining arrays when that array is placed in quotation marks, and the default value is a single space.
Personally, I rather prefer the say join " ", reverse $line;. It's more obvious what is going on, and doesn't depend upon the value of rarely used variables.
I am new to Perl, can anyone explain the following scripts for me please:
#!/usr/bin/env perl
use strict;
use warnings;
sub f1($) { my ($v) = #_; print "f1 $v\n"; }
sub f2(#) { my ($v) = #_; print "f2 $v\n"; }
my $s = "ww";
my #a = ("xx", "yy", "zz");
f1 $s; f1 #a; f2 $s; f2 #a;
The output in my computer is :
f1 ww
f1 3
f2 ww
f2 xx # why!!
Can anyone explain why the fourth output is xx? I thought it should be zz, since when array converts to scalar, it should be the last element of array.
No, with a statement such as:
my ($v, $foo, $bar) = #_;
$v will be assigned the first value in the #_ array, $foo the second, and so on. This is because the parentheses impose a list context. Any excess values will be ignored, unless one of your variables is an array, in which case it will slurp all remaining values.
my ($v, #foo, $bar) = #_; # wrong! $bar will never get any value
$v will get the first value, #foo all the rest. $bar will be undefined.
You may be thinking of assignment using a list:
my $v = qw(a b c);
But this is wrong, and will cause an error:
Useless use of a constant (a) in void context at -e line 1.
Useless use of a constant (b) in void context at -e line 1.
This is because the LHS using scalar context, it will (more or less) be similar to:
'a';
'b';
my $v = 'c';
You may notice that if we impose list context by putting $v inside parentheses, we get a different result:
my ($v) = qw(a b c); # $v is now 'a'
ETA: Regarding prototypes:
In f1, what you see is that the array is forced into scalar context because the subroutine expects a scalar argument. That is why f1 with an array prints 3 (the size). When the prototype looks for an array, the array remains in default list context, and the assignment is done as per normal (as described above).
As an extra note: prototypes have a very specific use, to make subroutines behave more like certain built-ins with regard to argument handling. Such as sort { code here } or push #array, $foo.
If this is not what you are after, you should skip prototypes all together and simply write:
sub f1 {
...
}
Documentation here
Perl array behaves in 2 differents flavours, regarding context:
Scalar context:
my $a = #tab; # get the array length
Array context:
my #newTab = #tab; # newTab is a copy of tab
Which can be reworded like this:
# 3 scalars in an array context (see parens) gets the contents of the tab
my ($a,$b,$c) = #tab;
Here, since #tab can be wider than the number of scalars, these scalars are populated from the beginning of the tab (and not from the end). So your code:
my ($a) = #tab;
will echo the first element of the tab
An array can't be converted to a scalar. An array can be evaluated in scalar context, though.
An array evaluated in scalar context does not return the last element of the array. As you saw with f1, it returns the number of elements in the array.
$ perl -E'my #a = qw( xx yy zz ); say #a; say scalar(#a);'
xxyyzz
3
Neither array is being evaluated in scalar context. An argument list expression is evaluated in list context.
f(#a);
is the same as
f($a[0], $a[1], $a[2]);
The RHS of a list assignment in evaluated in list context.
my ($v) = #_;
is the same as
my ($v) = ($_[0], $_[1], $_[2]);
which is the same as
my ($v, undef, undef) = ($_[0], $_[1], $_[2]);