Perl adding array with another array - arrays

I cam across the code below online where it's trying to add two array. Can anyone explain what it is calculating to get 14?
my #a = (1,2,5)+(8,9);
print "#a";
output: 14

Output is 14 as $a[0] is 14 => 5+9
+ operator imposes scalar context on both lists so last elements are taken and added,
# in scalar context $x is assigned with last element
my $x = (1,2,5);
print "\$x is $x\n";
outputs $x is 5
warnings pragma would also complain, giving you a hint that something fishy is going on,
Useless use of a constant (8) in void context

Starting with:
my #a = (1,2,5)+(8,9);
When using a list in a scalar context, the last element is returned. Consult What is the difference between a list and an array? for details.
Therefore the above two lists reduce to:
my #a = 5 + 9;
Which mathematically equals:
my #a = (14);

Related

Declare and initialize a typed array from a range

I recently tried my Array #a = 'a'..'z'; and my Array #a = #('a'..'z');.
Both will produce the following error:
Type check failed in assignment to #a; expected Array but got Str ("a")
in block <unit> at <unknown file> line 1
However initializing without the type works and seems to ultimately produce an Array:
> my #a = 'a'..'z';
> #a.^name
Array
Why is this the case?
TL;DR I provide a relatively simple answer in Why is this the case? However, that explanation may be inadequate1 so I review some alternatives in Declare and initialize a typed array from a range.
Why is this the case?
my #a; declares a new Array (initialized to be empty) and "binds" it to the symbol #a. Thus my #a; say #a.^name returns Array. There is no need to use the word Array in a declaration or initialization of an array -- the # is enough.2
my #a = 'a'..'z' attempts to copy each value in the range 'a' thru 'z', one at a time, into #a[0], #a[1], etc. The new array bound to #a has a type constraint for each of its elements (explained in the next section); it will be checked for each value (and will succeed).
my Array #a declares an Array with an Array type constraint on its elements (so it's an array of arrays). my Array #a; say #a.^name returns Array[Array] to indicate this. my Array #a = 'a'..'z'; fails when copying the first value ("a") because it is a Str value not an Array.
Declare and initialize a typed array from a range
my #a = 'a'..'z';
The my #a part of this statement declares a variable that's bound to (refers to) a new array of type Array. Because no element type constraint was specified, the new array's elements are constrained to be consistent with Mu, the Most unassuming type in P6. In other words it's an empty array ready to contain whatever values you want to put in it. (One could say that say #a.^name displays Array rather than Array[Mu] because the [Mu] is considered Most uninteresting.)
... = 'a'..'z' initializes the new array. The initialization has no impact on the array's already established type constraints. It just delivers copies of the strings 'a', 'b' etc. into the array (which auto-expands to receive them into #a[0], #a[1] etc.).
I recommend devs avoid adding explicit type constraints on variables and explicit coercions of values unless they're confident they're desirable. (cf my parenthetical remarks at the end of an earlier SO answer.) That said, you can choose to do so:
my Str #a = 'a'..'z'; # `Array` elements constrained to `Str`
my Str #a = (0..99)>>.Str; # Coerce value to match constraint
Alternatively, P6 supports explicit binding, rather than assignment, of a value or list of values. The most common way to do this is to use := instead of =:
my #a := 'a'..'z'; say #a.WHAT; say #a[25]; # (Range)␤z
Note how the explicit binding of #a means #a has not been bound to a new Array but instead to the Range value. And because a Range can behave as a Positional, positional subscripting still works.
The following statements would imo be grossly redundant explicit typing but would both work and yield exactly the same outcome as each other, though the first one would be faster:
my Str #a := Array[Str].new('a'..'z');
my Str #a = Array[Str].new('a'..'z');
There's more to discuss about this topic but perhaps the foregoing provides enough for this question/answer. If not, please ask further questions in comments under your original question and/or below.
Footnotes
1 An earlier version of this answer began with:
my Array #a ...
# My array of thoughts raised by this declaration
# and your questing "why?" in this SO question
# began with wry thoughts about complicated answers
# about reasons your array is awry and pedances
(I made up the word "pedances" to mean something that appears to be pedantic but flows nicely when used correctly -- which will happen naturally once you've become familiar with its apparently idiosyncratic but actually helpful nature. More importantly, I needed something that rhymes with "answers".)
2 Here are a couple of mnemonics for the meaning of # in P6:
It looks like a zero digit (0) with an 𝑎 (Mathematical Italic Small A) inside it -- and a #foo variable is by default a 0 indexed 𝑎𝑟𝑟𝑎𝑦 (or #𝑟𝑟𝑎𝑦).
It sounds like the word "at". An array has elements at indices.
Set the type of the element in the array:
my Str #a = 'a'..'z';
say #a; #[a b c d e f g
To see, what type it is, you can use .WHAT
my Str #a = 'a'..'z';
say #a.WHAT #(Array[Str])
To test if it is an array, you can smartmatch
my Str #a = 'a'..'z';
say 'is array' if #a ~~ Array; #is array
say 'is str array' if #a ~~ Array[Str]; #is str array
say 'is str array' if #a ~~ Array[Int]; #
The #('a'..'z') will make a List not an Array. Changing it to
['a'..'z'] will give an Array.
But that will still give you a type error when assigning it to my Array #a as you have done.
If you want to assign an entire Array to an element of another array, you have to itemise it somehow eg:
$[1,2,3]; #Still an array but treated as single item
[1,2,3], ; #Note the list operator (comma), this give you a list of lists
So in your case:
'a'..'z'; is a range, needs to be converted to an array, so
['a'..'z']; range is now evaluated into an array
$['a'..'z']; range is now evaluated into an array and taken as a single item
my Array #a=$['a'..'z'];
say #a;
#OUTPUT:
#[[a b c d e f g h i j k l m n o p q r s t u v w x y z]]
# Which is an array of arrays;
Not sure if it is what you are after but it removes the type error.
When you declare an array, you can easily specify what type the elements of the array are.
my Str #a = 'a'..'z';
You can specify the exact class that is used for the storage as well.
The following line is exactly the same as the above line; since Array is the default storage class.
my #a is Array[Str] = 'a'..'z';
You can even combine the two.
This following is also exactly the same.
my Str #a is Array = 'a'..'z';
When you wrote the following line.
my Array #a = 'a'..'z';
What you were actually saying was:
my #a is Array[Array] = 'a'..'z';
I assume you thought you were writing this.
my #a is Array = 'a'..'z';
This can be useful if you don't want the elements to change.
my #a is List = 'a'..'z';
Or if you have specific needs that aren't satisfied by the default Array class.
use Array::Unique; # doesn't really exist yet (afaik)
my #a is Array::Unique[Str] = 'a'..'z';
my Str #b is Array::Unique = 'a'..'z';

How to add each element of array to another array's each element?

I have these sets of arrays which has two elements for each.
#a = ("a", "b");
#i = (1, 2);
#s = ( "\\!", "\?");
How do I make the result such that it'll return
a1!, b2?
And I need them to be a new set of an array like
#new =(a1!,b2?)
I wrote the code for output of the answer
$i = length(#a);
for (0..$1) {
#array = push(#array, #a[$i], #s[$i];
}
print #array;
However, it only returned
syntax error at pra.pl line 10, near "];"
Thank you in advance.
use 5.008;
use List::AllUtils qw(zip_by);
⋮
my #new = zip_by { join '', #_ } \#a, \#i, \#s;
zip_by is a subroutine from the List::AllUtils module on CPAN. So it's not built-in.
use v6;
⋮
my #new = map { .join }, zip #a, #i, #s;
In Perl 6, zip is already part of the standard library. This additionaly solution is here for flavour, it's an opportunity to show off strengths: does the same job, but with less syntax in comparison, and works out of the box.
v6 is not strictly necessary, here I just used it for contrast to indicate the version. But at the beginning of a file it also has the nice property that if you accidentally run Perl 6 code in Perl 5, you'll get a nice error message instead of a cryptic syntax error. Try it! From the use VERSION documentation:
An exception is raised if VERSION is greater than the version of the current Perl
The basic idea you have is good, to iterate simultaneously using index of an array. But the code has many elementary errors and it also doesn't do what the examples show. I suggest to first make a thorough pass through a modern and reputable Perl tutorial.
The examples indicate that you want to concatenate (see . operator) elements at each index
use warnings;
use strict;
use feature 'say';
my #a1 = ('a', 'b');
my #a2 = (1, 2);
my #a3 = ('!', '?');
my #res;
foreach my $i (0..$#a1) {
push #res, $a1[$i] . $a2[$i] . $a3[$i];
}
say for #res;
where $#a1 is the index of the last element of array #a1. This assumes that all arrays are of the same size and that all their elements are defined.
This exact work can be done using map in one statement
my #res = map { $a1[$_] . $a2[$_] . $a3[$_] } 0..$#a1;
with the same, serious, assumptions. Even if you knew they held, do you know for sure, in every run on any data? For a robust approach see the answer by mwp.
There is also each_array from List::MoreUtils, providing a "simultaneous iterator" for all arrays
my $ea = each_array(#a1, #a2, #a3);
my #res;
while ( my ($e1, $e2, $e3) = $ea->() ) {
push #res, $e1 . $e2 . $e3
}
which is really useful for more complex processing.
A quick run through basics
Always have use warnings; and use strict; at the beginning of your programs. They will catch many errors that would otherwise take a lot of time and nerves.
Don't use single-letter variable names. We quickly forget what they meant and the code gets hard to follow, and they make it way too easy to make silly mistakes.
Array's size is not given by length. It is normally obtained using context -- when an array is assigned to a scalar the number of its elements is returned. For iteration over indices there is $#ary, the index of the last element of #ary. Then the list of indices is 0 .. $#ary, using the range (..) operator
The sigil ($, #, %) at the beginning of an identifier (variable name) indicates the type of the variable (scalar, array, hash). An array element is a scalar so it needs $ -- $ary[0]
The push doesn't return array elements but it rather adds to the array in its first argument the scalars in the list that follows.
The print #array; prints array elements without anything between them. When you quote it spaces are added, print "#array\n";. Note the handy feature say though, which adds the new line.
Always use strict; and use warnings; (and use my to declare variables).
You don't need to escape ? and ! in Perl strings, and you can use the qw// quote-like operator to easily build lists of terms.
You are using length(#a) to determine the last index, but Perl array indexes are zero-based, so the last index would actually be length(#a) - 1. (But that's still not right. See the next point...)
To get an array's length in Perl, you want to evaluate it in scalar context. The length function is for strings.
You have not accounted for the situation when the arrays are not all the same length.
You assign the last index to a variable $i, but then you reference the variable $1 on the next line. Those are two different variables.
You are iterating from zero to the last index, but you aren't explicitly assigning the iterator to a variable, and you aren't using the implicit iterator variable ($_).
To get a single array element by index in Perl, the syntax is $a[$i], not #a[$i]. Because you only want a single, scalar value, the expression has to start with the scalar sigil $. (If instead you wanted a list of values from an expression, you would start the expression with the array sigil #.)
push modifies the array given by the first argument, so there's no need to assign the result back to the array in the expression.
You are missing a closing parenthesis in your push expression.
In your same code, you have #new and #array, and you are only adding elements from #a and #s (i.e. you forgot about #i).
You are pushing elements onto the array, but you aren't concatenating them into the desired string format.
Here is a working version of your implementation:
use strict;
use warnings;
use List::Util qw{max};
my #a = ("a", "b");
my #i = ("1", "2");
my #s = ("!", "?");
my #array;
my $length = max scalar #a, scalar #i, scalar #s;
foreach my $i (0 .. $length - 1) {
push #array, ($a[$i] // '') . ($i[$i] // '') . ($s[$i] // '');
}
print #array;
(The // means "defined-or".)
Here's how I might write it:
use strict;
use warnings;
use List::Util qw{max};
my #a = qw/a b/;
my #i = qw/1 2/;
my #s = qw/! ?/;
my #array = map {
join '', grep defined, $a[$_], $i[$_], $s[$_]
} 0 .. max $#a, $#i, $#s;
print "#array\n";
(The $#a means "give me the index of the last element of the array #a.")
use warnings;
use strict;
use Data::Dumper;
my $result = [];
my #a = ("a", "b");
my #i = (1, 2);
my #s = ( "\!", "\?");
my $index = 0;
for my $a ( #a ) {
push( #$result, ($a[$index], $i[$index], $s[$index]) );
$index = $index + 1;
}
print Dumper(#$result);

size of array in perl script loop

I am using below commands to find the size of array
$size_x = #x;
or
$size_x = $#x+1 ;
Both work well when I use in simple statement. But when I use them in the loop, the size becomes one number bigger. Why it is happening in that way. Below is example:
for ($i=1;$i<=10;$i++){
if (1**2+2**2>=1){
#x[$i] =2+3;
$size_x = #x;
}
print "my size is $size_x\n";
}
here is results:
my size is 2
my size is 3
my size is 4
my size is 5
my size is 6
my size is 7
my size is 8
my size is 9
my size is 10
my size is 11
The answer should be from 1 to 10 instead of 2 to 11, i think. What is a better way to get size correctly? Thanks.
After reading your code, I honestly can't figure out what you're trying to do here, but if you're trying to create an array with 11 elements and assign all of them to 5 except for the first one, then you've done an excellent job. Perhaps it would help to see a visualization of the array you've created:
[undef, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
If that's really what you were hoping for, there are faster/simpler/better ways to do the same thing, but I have to believe you were trying to accomplish something else. At a very basic level, arrays in Perl are 0-indexed:
Normal arrays are ordered lists of scalars indexed by number, starting
with 0.
Having said that, you very rarely see the C-style for loop in Perl code, and that's because it's very rarely necessary. More often, you would see something like this:
for (0 .. 9) {
# do something...
}
That code uses a foreach-style loop and the range operator (..) to generate a sequence (technically, a list of values from left to right, counting up by one). Enough about loops. Let's talk about your strange logic.
12 + 22 will always be 5, which will always be greater than 1. The same goes for 2 + 3. There's nothing dynamic about this code, which is why I have to think that you meant to use the loop iterator in your calculations somehow. Otherwise, you could easily fill a 10-element array with a bunch of 5's in one line:
my #x = (5) x 10;
Now, one last point that applies to this code and all Perl code you write from this point forward. Every single file must include the following two lines at the top:
use strict;
use warnings;
I believe that Perl arrays are zero-based, meaning that the first element is stored and accessed at index 0. Here is your for loop:
for ($i=1; $i<=10; $i++) {
if (1**2+2**2>=1) {
#x[$i] =2+3;
$size_x = #x;
}
print "my size is $size_x\n";
}
When you make the first assignment #x[1] = 2 + 3, the array is considered to already have an (empty) element in position zero, so the size is returned as being 2.
The easiest way to avoid this problem from happening would be to refactor your for loop to start at index 0:
for ($i=0; $i<10; $i++) {
...
}
When you start the index from 1 the size automatically increases by 1. Try starting the index from 0 or
#x[$i-1] =2+3;
Just as a side note:
Array elements in Perl are denoted with a starting $, not an #.
So #x is the whole array, while $x[$i] is the $i'th element of the array #x.
use warnings; should warn about this.
(I know this is more a comment than an answer, but as a SO newbie I'm not yet allowed to comment. Sorry.)

Why is $#a one less than the actual number of elements in an array?

Does anyone knows why $#a one less than the actually number of elements in an array?
$ perl -we '#a=(1,2);print $#a'
1
That's the index of the last item and since arrays start at zero (unless you're messing around with things that are better left alone), $#a is one less than the length of the array.
I would imagine that is because it is the index of the last element in the array. Since the array indexing begins at 0, you need to add one to get the total number of elements in an array.
NB: You can also do this to find the count of an array:
#arr = ("one", "two");
$count = #arr;
print $count;
Array #a = ("a","b","c");
Value of $#a = index of last element(=2).
$a[0] = "a";
$a[1] = "b";
$a[2] = "c";
if you want to get number of elements in the array, you can assign the array to a scalar
like
$arrayLength = #a; #(=3)
Hope this helps you
$#array is used to find the index of the last element in the array.
In the example above it is position 1 - as indexes in arrays start at 0 in perl.
Everyone is telling you what it returns (which you already know), but you asked why it returns what it does.
Unfortunately, it's not really possible to answer why it returns what it does. Maybe because it makes the following easier?
for (0..$#a) {
...
}

Array Operations in perl

Why does the code below return 11 with this :- #myarray = ("Rohan");
Explaination i got was :- The expression $scalar x $num_times, on the other hand, returns a string containing $num_times copies of $scalar concatenated together string-wise.
So it should give 10 not 11 ...
code is as below :-
print "test:\n";
#myarray = ("Rohan"); # this returns 11
###myarray = ("Rohan","G"); this returns 22
#myarray2 = (#myarray x 2);
#myarray3 = ((#myarray) x 2); #returns Rohan,Rohan and is correct
print join(",",#myarray2,"\n\n");
print join(",",#myarray3,"\n\n");
What’s happening is that the x operator supplies scalar context not just to its right‐hand operand, but also to its left‐and operand as well — unless the LHO is surrounded by literal parens.
This rule is due to backwards compatibility with super‐ancient Perl code from back when Perl didn’t understand having a list as the LHO at all. This might be a v1 vs v2 thing, a v2 vs v3 thing, or maybe v3 vs v4. Can’t quite remember; it was a very long time ago, though. Ancient legacy.
Since an array of N elements in scalar context in N, so in your scenario that makes N == 1 and "1" x 2 eq "11".
Perl is doing exactly what you asked. In the first example, the array is in scalar context and returns its length. this is then concatenated with itself twice. In the second example, you have the array in list context and the x operator repeats the list.

Resources