Accessing Array Elements of Referenced Array - perl

I am new to Perl. I wrote a snippet to access array elements and print it to the console:
use strict;
use warnings;
my #array1 = ('20020701 00000', 'Sending Mail in Perl', 'Philip Yuson');
my #array2 = ('20020601', 'Manipulating Dates in Perl', 'Philip Yuson');
my #array3 = ('20020501', 'GUI Application for CVS', 'Philip Yuson');
my #main = (\#array1, \#array2, \#array3);
my $a = $main[0];
print #$a;
print #$a . "pdf";
First print:
20020701 00000Sending Mail in PerlPhilip Yuson
But why second print outputs this?
3pdf
I need to get the output like
20020701 00000Sending Mail in PerlPhilip Yusonpdf
I don't know why it is giving 3pdf i am pressed get out of this. Any Help is greatly appreciated.

The 3 is the number of elements in the array. The . is forcing the array into scalar context, and then you get the number of elements instead of the array contents. You could use
print "#$a pdf";
or
print #$a , "pdf";
depending on what kind of output you want.

Arrays are one of the parts of Perl that act differently according to the “context”, which is a very important concept in Perl programming. Consider this:
my #fruits = qw/apples pears bananas/;
my $items = #fruits;
On the second line you are assigning to a scalar (⇒ here we have some context), but on the right side you have an array. We say that the array here is used in scalar context, and in scalar context the value of an array is the number of its items.
Now to your problem: When you are simply printing the array, there is not much magic involved. But when you try to append a string onto the array using the . operator, you are using the array in scalar context. Which means the array evaluates to the number of its items (3), to which you append the pdf.
Is that clear? You should Google up something on “Perl context”, that will make Perl programming much easier for you.

This is a matter of contexts. In Perl, the data type of a value is only part of what evaluates to; the other half is the context that value is used in.
As you may know, there are three built-in data types: scalars, arrays, and hashes. There is also some degree of implicit casting that can be done between these data types.
There are also two major contexts: list and scalar. Arrays and hashes both work without casting in list context; scalar values work without change in scalar contexts.
The behavior of an operator can depend on the context it is run in. If an operator requires a particular context, and Perl is able to implicitly cast the value into something matching that context, it will. In the case of arrays and associative arrays being cast to integers, what you get is the ''cardinality'' of the array, the number of elements it contains.
In your example above, #$a evaluates to data typed as an array. The other half of that story, though, is the context in which the operator . runs in. Reading perldoc perlop, it says the following:
Binary . concatenates two strings.
Well, strings are scalar values, and so we need to cast the array #$a to be valid in a scalar context, and in doing so get the back the cardinality of the array. #$a contains 3 things, so this evaluates to the scalar value 3, which is then turned into a string so the . operator can work its magic.
Hope this makes some sense.

print #$a . "pdf" evaluates the array in scalar context, this outputting the number of elements in the array, which is why you get 3.
You're probably looking for something like this:
print #$a, "pdf";
The comma operator instead of dot forces it into list context.

I have a feeling what you really would like is:
print "#$a.pdf", "\n";
That is:
my #array1 = ('20020701 00000', 'Sending Mail in Perl', 'Philip Yuson');
my #array2 = ('20020601', 'Manipulating Dates in Perl', 'Philip Yuson');
my #array3 = ('20020501', 'GUI Application for CVS', 'Philip Yuson');
my #main = (\#array1, \#array2, \#array3);
for my $x ( #main ) {
print "#$x.pdf", "\n";
}
Output:
20020701 00000 Sending Mail in Perl Philip Yuson.pdf
20020601 Manipulating Dates in Perl Philip Yuson.pdf
20020501 GUI Application for CVS Philip Yuson.pdf

Related

Perl assigning #ARGV array to a variable

When I assign the Perl #ARGV array to a variable, if I don't use the quotes, it gives me the number of strings in the array, and not the strings in the array.
What is this called - I thought it was dereferencing, but it is not. Right now I am calling it one more thing I need to memorize in Perl.
#!/usr/bin/perl
use strict ;
use warnings;
my $str = "#ARGV" ;
#my $str = #ARGV ;
#my $str = 'geeks, for, geeks';
my #spl = split(', ' , $str);
foreach my $i (#spl) {
print "$i\n" ;
}
If you assign an array to a scalar in Perl, you get the number of elements in the array.
my #array = (1, 1, 2, 3, 5, 8, 13);
my $scalar = #array; # $scalar contains 7
This is known as "evaluating an array in scalar context".
If you expand an array in a double-quoted string in Perl, you get the elements of the array separated by spaces.
my #array = (1, 1, 2, 3, 5, 8, 13);
my $scalar = "#array"; $scalar contains '1 1 2 3 5 8 13'
This is known as "interpolating an array in a double-quoted string".
Actually, in that second example, the elements are separated by the current contents of the $" variable. And the default value of that variable is a space. But you can change it.
my #array = (1, 1, 2, 3, 5, 8, 13);
$" = '+';
my $scalar = "#array"; $scalar contains '1+1+2+3+5+8+13'
To store a reference to the array, you either take a reference to the array.
my $scalar = \#array;
Or create a new, anonymous array using the elements of of the original array.
my $scalar = [ #array ];
Because we don't know what you are actually trying to do, we can't recommend which of these is the best approach.
Perl works by context. The one that you see here is scalar versus array context. In scalar context, you want one thing, so Perl gives you the one thing the probably makes sense. Recognize the context and you can probably suss out what's going on.
When you have a scalar on the left side of an assignment, you have scalar context because you want to end up with one thing:
my $one_thing = ...
Put an array on the right side, and you have an array in scalar context. The design of Perl decided that the most common thing people probably want in that case is the number of elements:
my $one_thing = #array;
This works with some other builtins too. The localtime builtin returns a single string in scalar context (a timestamp):
my $uid = localtime; # Tue Mar 17 11:39:47 2020
But, in array context, you want possibly multiple things (where that could be two, or one, or zero, or ten thousand, or...). In that case, localtime returns a list of things:
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime();
You already know some of this though, probably. The + operator uses its operands as numbers, but the . operator uses them as strings:
my $sum = '123' + 14;
my $string = '123' . 14;
Perl's philosophy is that it is going to try to do what the verbs (operators, builtins, functions) are trying to do, not what the nouns (variable or value type) might imply. Many languages tell the verbs what to do based on the nouns, so fitting Perl into one of those mental modules usually doesn't work out. You don't have to memorize a lot; I've been doing this quite awhile and I still refer to the docs often.
We go through a lot of this philosophical explanation in Learning Perl.
The idiom you are looking for is one of
my $str = \#ARGV;
my $str = [ #ARGV ];
These both assign an array reference to the scalar variable $str. You can then get back the elements of #ARGV when you dereference $str. For example,
for my $i (#$str) {
print "$i\n";
}
(Some people prefer #{$str}, which does the same thing)
\ is the reference operator, which returns a reference to whatever is on its right hand side.
[...] creates a new array reference out of whatever is contained between the brackets.
"#array" is a stringify operation on an array, and equivalent to join($", #array)
And finally, a scalar assignment from an array, like
$n = #array
returns the number of elements in the array.
The total number of elements in the Array can sometimes be required. Since such situations are often encountered, we also have to learn how to get the total number.
#array = ("a".."z");
$re = $#array;
print ("$re\n");
We may need to add one to the number we get to reach the total number.
$ay = ("a", "b", "c");
$re = $#ay;
$re = $re +1;
print ("$re\n");
result: 3

why perl doesn't recognize i want to create an arrayRef with "split"?

I want to split a scalar by whitespaces and save the result in an ArrayReference.
use strict;
use warnings;
use Data::Dumper;
my $name = 'hans georg mustermann';
my $array = split ' ', $name;
print Dumper($array); #$VAR1 = 3;
So it seems $array is now a scalar with the size resulted by the split operation.
When i change the code to my $array = [split ' ', $name]; the variable $array is now a ArrayReference and contains all 3 strings.
I just don't understand this behavior. Would be really great if someone could explain it to me or post a good documentation about these things, as i don't know how to search for this topic.
Thank you in advance
What you see here is called "context". The documentation about this is rather scattered. You also want to take a look at this tutorial about "scalar vs list context" https://perlmaven.com/scalar-and-list-context-in-perl
If you assign the result of split (or any subroutine calls) to an array, it's list context:
my #arr = split ' ', $name;
#=> #arr = ('hans', 'georg', 'mustermann');
What your example code shows is assigning them to a scalar -- and therefore it's under "scalar context".
Since, naturally, multiple things cannot fit into one position, some sort of summarization needs to be done. In the case of split function, perl5 has defined that the number of elements in the result of split shall be the best.
Check the documentation of the split function: https://perldoc.pl/functions/split -- which actually defines the behaviour under scalar context as well as list context.
Also take a glance at the documentation of all builtin functions at https://perldoc.pl/functions -- you'll find the behaviour definition under "list context" and "scalar context" for most of them -- although many of them are not returning "the size of lists" but rather something else.
That's called context.
The partial expression split ' ', $name evaluates to a list. The partial expression $array = LIST coerces the list to a scalar value, namely counting the number of elements in the list. That's the default behaviour of lists in scalar context.
You should write #array = LIST instead, using an array variable, not a scalar variable, in order to preserve the list values.
If you read the documentation for split(), you'll find the bit that explains what the function returns.
Splits the string EXPR into a list of strings and returns the list in list context, or the size of the list in scalar context.
You're calling the function in scalar context (because you're assigning the result of the call to a scalar variable) so you're getting the size of the list.
If you want to get the list, then you need to store it either in a list of variables:
my ($forename, $middlename, $surname) = split ' ', $name;
Or (more usually) in an array:
my #name_parts = split ' ', $name;
But actually, you say that you want an array reference. You can do that by calling split() inside an anonymous array constructor ([ ... ]) and assigning the result of that call to a scalar variable.
my $name_parts = [ split ' ', $name ];

How do I determine the number of elements in an array reference?

Here is the situation I am facing...
$perl_scalar = decode_json( encode ('utf8',$line));
decode_json returns a reference. I am sure this is an array. How do I find the size of $perl_scalar?? As per Perl documentation, arrays are referenced using #name. Is there a workaround?
This reference consist of an array of hashes. I would like to get the number of hashes.
If I do length($perl_scalar), I get some number which does not match the number of elements in array.
That would be:
scalar(#{$perl_scalar});
You can get more information from perlreftut.
You can copy your referenced array to a normal one like this:
my #array = #{$perl_scalar};
But before that you should check whether the $perl_scalar is really referencing an array, with ref:
if (ref($perl_scalar) eq "ARRAY") {
my #array = #{$perl_scalar};
# ...
}
The length method cannot be used to calculate length of arrays. It's for getting the length of the strings.
You can also use the last index of the array to calculate the number of elements in the array.
my $length = $#{$perl_scalar} + 1;
$num_of_hashes = #{$perl_scalar};
Since you're assigning to a scalar, the dereferenced array is evaluated in a scalar context to the number of elements.
If you need to force scalar context then do as KARASZI says and use the scalar function.
You can see the entire structure with Data::Dumper:
use Data::Dumper;
print Dumper $perl_scalar;
Data::Dumper is a standard module that is installed with Perl. For a complete list of all the standard pragmatics and modules, see perldoc perlmodlib.

= and , operators in Perl

Please explain this apparently inconsistent behaviour:
$a = b, c;
print $a; # this prints: b
$a = (b, c);
print $a; # this prints: c
The = operator has higher precedence than ,.
And the comma operator throws away its left argument and returns the right one.
Note that the comma operator behaves differently depending on context. From perldoc perlop:
Binary "," is the comma operator. In
scalar context it evaluates its left
argument, throws that value away, then
evaluates its right argument and
returns that value. This is just like
C's comma operator.
In list context, it's just the list
argument separator, and inserts both
its arguments into the list. These
arguments are also evaluated from left
to right.
As eugene's answer seems to leave some questions by OP i try to explain based on that:
$a = "b", "c";
print $a;
Here the left argument is $a = "b" because = has a higher precedence than , it will be evaluated first. After that $a contains "b".
The right argument is "c" and will be returned as i show soon.
At that point when you print $a it is obviously printing b to your screen.
$a = ("b", "c");
print $a;
Here the term ("b","c") will be evaluated first because of the higher precedence of parentheses. It returns "c" and this will be assigned to $a.
So here you print "c".
$var = ($a = "b","c");
print $var;
print $a;
Here $a contains "b" and $var contains "c".
Once you get the precedence rules this is perfectly consistent
Since eugene and mugen have answered this question nicely with good examples already, I am going to setup some concepts then ask some conceptual questions of the OP to see if it helps to illuminate some Perl concepts.
The first concept is what the sigils $ and # mean (we wont descuss % here). # means multiple items (said "these things"). $ means one item (said "this thing"). To get first element of an array #a you can do $first = $a[0], get the last element: $last = $a[-1]. N.B. not #a[0] or #a[-1]. You can slice by doing #shorter = #longer[1,2].
The second concept is the difference between void, scalar and list context. Perl has the concept of the context in which your containers (scalars, arrays etc.) are used. An easy way to see this is that if you store a list (we will get to this) as an array #array = ("cow", "sheep", "llama") then we store the array as a scalar $size = #array we get the length of the array. We can also force this behavior by using the scalar operator such as print scalar #array. I will say it one more time for clarity: An array (not a list) in scalar context will return, not an element (as a list does) but rather the length of the array.
Remember from before you use the $ sigil when you only expect one item, i.e. $first = $a[0]. In this way you know you are in scalar context. Now when you call $length = #array you can see clearly that you are calling the array in scalar context, and thus you trigger the special property of an array in list context, you get its length.
This has another nice feature for testing if there are element in the array. print '#array contains items' if #array; print '#array is empty' unless #array. The if/unless tests force scalar context on the array, thus the if sees the length of the array not elements of it. Since all numerical values are 'truthy' except zero, if the array has non-zero length, the statement if #array evaluates to true and you get the print statement.
Void context means that the return value of some operation is ignored. A useful operation in void context could be something like incrementing. $n = 1; $n++; print $n; In this example $n++ (increment after returning) was in void context in that its return value "1" wasn't used (stored, printed etc).
The third concept is the difference between a list and an array. A list is an ordered set of values, an array is a container that holds an ordered set of values. You can see the difference for example in the gymnastics one must do to get particular element after using sort without storing the result first (try pop sort { $a cmp $b } #array for example, which doesn't work because pop does not act on a list, only an array).
Now we can ask, when you attempt your examples, what would you want Perl to do in these cases? As others have said, this depends on precedence.
In your first example, since the = operator has higher precedence than the ,, you haven't actually assigned a list to the variable, you have done something more like ($a = "b"), ("c") which effectively does nothing with the string "c". In fact it was called in void context. With warnings enabled, since this operation does not accomplish anything, Perl attempts to warn you that you probably didn't mean to do that with the message: Useless use of a constant in void context.
Now, what would you want Perl to do when you attempt to store a list to a scalar (or use a list in a scalar context)? It will not store the length of the list, this is only a behavior of an array. Therefore it must store one of the values in the list. While I know it is not canonically true, this example is very close to what happens.
my #animals = ("cow", "sheep", "llama");
my $return;
foreach my $animal (#animals) {
$return = $animal;
}
print $return;
And therefore you get the last element of the list (the canonical difference is that the preceding values were never stored then overwritten, however the logic is similar).
There are ways to store a something that looks like a list in a scalar, but this involves references. Read more about that in perldoc perlreftut.
Hopefully this makes things a little more clear. Finally I will say, until you get the hang of Perl's precedence rules, it never hurts to put in explicit parentheses for lists and function's arguments.
There is an easy way to see how Perl handles both of the examples, just run them through with:
perl -MO=Deparse,-p -e'...'
As you can see, the difference is because the order of operations is slightly different than you might suspect.
perl -MO=Deparse,-p -e'$a = a, b;print $a'
(($a = 'a'), '???');
print($a);
perl -MO=Deparse,-p -e'$a = (a, b);print $a'
($a = ('???', 'b'));
print($a);
Note: you see '???', because the original value got optimized away.

Indexing directly into returned array in Perl

This question has been asked about PHP both here and here, and I have the same question for Perl. Given a function that returns a list, is there any way (or what is the best way) to immediately index into it without using a temporary variable?
For example:
my $comma_separated = "a,b,c";
my $a = split (/,/, $comma_separated)[0]; #not valid syntax
I see why the syntax in the second line is invalid, so I'm wondering if there's a way to get the same effect without first assigning the return value to a list and indexing from that.
Just use parentheses to define your list and then index it to pull your desired element(s):
my $a = (split /,/, $comma_separated)[0];
Just like you can do this:
($a, $b, $c) = #array;
You can do this:
my($a) = split /,/, $comma_separated;
my $a on the LHS (left hand side) is treated as scalar context. my($a) is list context. Its a single element list so it gets just the first element returned from split.
It has the added benefit of auto-limiting the split, so there's no wasted work if $comma_separated is large.