How do dollar and number sign together work in perl? - perl

Today I have encountered a problem that required me to determine the maximum index of an array in perl. I used to do it this way:
my #array = (1, 2, 3);
print $array[#array - 1];
But today I have stumbled upon this code:
my #array = (1, 2, 3);
print $array[$#array];
I couldn't find anything on that matter in the docs. What exactly is that $# construct? Is that an operator? And how does it work, is it faster than the first piece of code? Does it always return the maximum array index? Is it deprecated or not?
I know that's a lot of questions, but they all can be summed up by one, and that's what I really want to know: How does it work?

This is documented in perldoc perldata, section "Scalar Values". In short, $#array is the last index of #array. As for how it works — it's sort of like an operator, but only as much as $ and # are operators. Think of it as special syntax. The last index of an array just happens to "have a name". It's a variable that you can read from and assign to.

The use is mentioned in first example in perldata. It denotes index of last item in the array.
Btw, you can also use
$array[-1]
to get last item.

That gives you the last index. It's documented in perldata - http://perldoc.perl.org/perldata.html

Related

Why does this line return sum of integers 1-10?

I'd like to understand how unpack is returning the sum in the given perl one-liner.
I've looked at pack man page and mostly understood that it is simply formatting the given array into a scalar of ten doubles.
However, I couldn't find proper documentation for unpack with %123. Looking for help here.
print unpack "%123d*" , pack( "d*", (1..10));
This line correctly outputs 55 which is 1+2+3+...+10.
From perldoc -f unpack:
In addition to fields allowed in pack(), you may prefix a field with a % to indicate that you want a <number>-bit checksum of the items instead of the items themselves.
Thus %123d* means to add all the input integers 1..10 and then take the first 123 bit of this result in order to construct the "<number>-bit checksum". Note that %8d* or just %d* (which is equivalent to %16d*) would suffice too given that the sum is small enough.

Declaring a Perl array and assigning it values by an array-slice

I was trying to split a string and rearrange the results, all in a single statement:
my $date_str = '15/5/2015';
my #directly_assigned_date_array[2,1,0] = split ('/', $date_str);
This resulted in:
syntax error at Array_slice_test.pl line 16, near "#directly_assigned_date_array["
Why is that an error?
The following works well though:
my #date_array;
#date_array[2,1,0] = split ('/', $date_str);
#vol7ron offered a different way to do it:
my #rvalue_array = (split '/', $date_str)[2,1,0];
And it indeed does the job, but it looks unintuitive, to me at least.
As you are just reversing the splitted array you can accomplish the same using this single statement: #date_array = reverse(split('/',$date_str));
Others here know much more about Perl internals than myself, but I assume it cannot perform the operation because an array slice is referencing an element of an array, which does not yet exist. Because the array has not yet been declared, it wouldn't know what address to reference.
my #array = ( split '/', $date_str )[2,1,0];
This works because split returns values in list context. Lists and arrays are very similar in Perl. You could think of an array as a super list, with extra abilities. However you choose to think of it, you can perform a list slice just like an array slice.
In the above code, you're taking the list, then reordering it using the slice and then assigning that to array. It may feel different to think about at first, but it shouldn't be too hard. Generally, you want your data operations (modifications and ordering) to be performed on the rhs of the assignment and your lhs to be the receiving end.
Keep in mind that I've also dropped some parentheses and used Perl's smart order of operation interpreting to reduce the syntax. The same code might otherwise look like the following (same operations, just more fluff):
my #array = ( split( '/', $date_str ) )[2,1,0];
As #luminos mentioned, since you only have 3 elements you're manually reversing it, you could use a reverse function; again we can make use of Perl's magic order of operation and drop the parentheses here:
my #array = reverse split '/', $date_str;
But in this case it might be too magical, so depending on your coding practice guidelines, you may want to include a set of parentheses for the split or reverse, if it increases readability and comprehension.

In Perl, what is the difference between accessing an array element using #a[$i] as opposed to using $a[$i]?

Basic syntax tutorials I followed do not make this clear:
Is there any practical/philosophical/context-dependent/tricky difference between accessing an array using the former or latter subscript notation?
$ perl -le 'my #a = qw(io tu egli); print $a[1], #a[1]'
The output seems to be the same in both cases.
$a[...] # array element
returns the one element identified by the index expression, and
#a[...] # array slice
returns all the elements identified by the index expression.
As such,
You should use $a[EXPR] when you mean to access a single element in order to convey this information to the reader. In fact, you can get a warning if you don't.
You should use #a[LIST] when you mean to access many elements or a variable number of elements.
But that's not the end of the story. You asked for practical and tricky (subtle?) differences, and there's one noone mentioned yet: The index expression for an array element is evaluated in scalar context, while the index expression for an array slice is evaluated in list context.
sub f { return #_; }
$a[ f(4,5,6) ] # Same as $a[3]
#a[ f(4,5,6) ] # Same as $a[4],$a[5],$a[6]
If you turn on warnings (which you always should) you would see this:
Scalar value #a[0] better written as $a[0]
when you use #a[1].
The # sigil means "give me a list of something." When used with an array subscript, it retrieves a slice of the array. For example, #foo[0..3] retrieves the first four items in the array #foo.
When you write #a[1], you're asking for a one-element slice from #a. That's perfectly OK, but it's much clearer to ask for a single value, $a[1], instead. So much so that Perl will warn you if you do it the first way.
The first yields a scalar variable while the second gives you an array slice .... Very different animals!!

Why does `$v = () = split` return 1?

perldoc says "a list assignment in scalar context returns the number of elements on the right-hand side of the list assignment" but when I try this code:
perl -e '$_="aaaaa";print $v=(()=split //)'
The output is 1 which makes me confused. (The answer I expect is 5.)
Can anybody explain this?
According to split documentation:
When assigning to a list, if LIMIT is omitted, or zero, Perl supplies
a LIMIT one larger than the number of variables in the list <...>
Since you specify empty list, split only returns 1 result and this number of results is exactly what ends in your variable.
split has some kind of crazy ultra-magic in it that allows it to know when it is on the right hand side of an assignment that has a list on the left hand side, and adjusts its behavior according to the number of items in that list.
This is described in perlfunc as being done "to avoid unnecessary work", but you've found an observable difference in behavior caused by that optimization.
To see some evidence of what happened, run your script through Deparse like this:
perl -MO=Deparse -e '$_="aaaaa";print $v=(()=split //)'
Update: I went looking for the code that implements this, and it's not where I expected it to be. Actually the optimization is performed by the assignment operator (op.c:Perl_newASSIGNOP) . split doesn't know that much about its context.
Why are you assigning to an empty array? the ()=(split //) bit. That's going to end up with - um, well, a mess. Or, in your case, an array with a size of one with not much in it.
Also, that's excessively obscure. perl has a sad reputation for being write-only, and all that modifying $_ and using it doesn't help others - or you - understand what is going on.
Try something like
perl -e '$v = (split //, "aaaaa"); print "$v\n"'
or, if you wish to replicate the behavior of your test:
perl -e '$v = () = (split //, "aaaaa"); print "$v\n"'
Yes, but :
perl -e '$_="aaaaa";print $v=(split //)'
gives 5, as well as
perl -e '$_="aaaaa";print $v=(#x=split //)'
Maybe your left-value () is dropping additional array elements ?
edit : by the way :
perl -e '$_="aaaaa";print $v=(($x,$y)=split //)'
returns 3, because the right sight of the $v=... command gives :
( $x , $y , ( a , a , a ) )
So in your original case, ()=split // returns ( ( a , a , a , a , a ) ) (which has only one element)
Edit : bad array notation, and result was wrong because of a last minute changed of my test-case

What does the special variable $#_ mean in Perl?

I encountered this special variable ($#_) while browsing. Tried finding out what it means, but couldn't find any. Please let me know what this special variable mean.
In Perl, you get the index of the last element of #array with the syntax $#array. So $#_ is the index of the last element in the array #_. This is not the same thing as the number of elements in the array (which you get with scalar #array), because Perl arrays are normally 0-based.