In Perl, what is the difference between #array[1] and $array[1]? - arrays

I have been studying array slices and frankly do not see the difference between choosing #array[1] and $array[1]. Is there a difference?
#!/usr/bin/perl
#array = (1,3);
print "\nPrinting out full array..\#array\n";
print "#array\n";
print "\n";
print "Printing out \#array[1]\n";
print "#array[1]\n";
print "Printing out \$array[1]\n";
print "$array[1]\n";
print "\n\n";

This is a FAQ.
The two forms are likely to work the same way in many contexts (but not all, as you can see from the example in the FAQ), but $array[1] expresses the intent more clearly.

$ and # are called sigils: hear what the sigils tell you
When you see $ you are dealing with a single thing.
When you see # you have a list of things.
#array[ 1 ] is a slice, a list with selected elements from the #array list. You have put only an element in this slice, the second element of #array, but it's a list anyway.
$array[ 1 ] is the second element of the list, a single value, a scalar.
#hash{ 'one', 'two' } is another kind of slice: this time we sliced an hash ( %hash ), obtaining a list with values corresponding to keys one and two.
Where's the difference? Although the difference is thin, you should avoid single element slices when you want a single value. Remember that on the right hand side of an expression, single element slices behave like scalars, but when they are on the left hand side of an expression they become a list assignment: they give list context to the expression.
If still you aren't feeling comfortable with the difference between scalar and list context, please don't use single element slices in order to avoid unexpected results in certain situations ( for instance when using the line input operator < > ).
Check the docs and happy Perl.

You already have your answer from Keith and Marco: # means list, $ means scalar (single value). I have more to add that does not fit in a comment.
You do not notice the difference because you are using a slice of one element. However, try this:
use warnings;
use strict;
use v5.10; # to enable say
my #array = (1 .. 10);
say "#array[2 .. 4]"; # [2,3,4] == "3 4 5"
say "#array[1,4,7]"; # == "2 5 8"
Here's some pointers to your code.
use strict;
use warnings;
There really is no excuse for not using these two. There is hardly any circumstance where it is better -- or easier -- to not use them. This holds especially true when you are experimenting and trying to learn perl.
If you had been using warnings, you would have gotten this additional information:
Scalar value #array[1] better written as $array[1]
Which basically is exactly the answer you have been given here, albeit said in the perl compiler's terse language.
Also, using print the way you do it is hard on the eyes. Make use of perl's flexibility:
#!/usr/bin/perl
use strict;
use warnings;
my #array = (1 .. 10);
print "
Printing out full array..\#array
#array
Printing out \#array[1]
#array[1]
Printing out \$array[1]
$array[1]
";
As you can see, this will print out newlines in a WYSIWYG fashion.

There are two differences:
The «1» is evaluated in list context in the case of the array slice (#foo[1]), but in scalar context in the case of the array indexing ($foo[1]). This has no functional effect.
#foo[1] gives a warning.

Good question - here's some differences:
$a[0] = localtime; print #a; # this prints: Tue Nov 1 18:51:13 2011
#a[0] = localtime; print #a; # this prints the amount of seconds, e.g. 13
$a[0] = grep{}, qw(6 2 8); print #a; # this prints: 3
#a[0] = grep{}, qw(6 2 8); print #a; # this prints: 6
$a[0] = reverse ("a", "b"); print #a; # this prints: ba
#a[0] = reverse ("a", "b"); print #a; # this prints: b
$a[0] = ("a", "b"); print #a; # this prints: b
#a[0] = ("a", "b"); print #a; # this prints: a
It doesn't matter if the slice is [1]. You can create more examples by finding functions that give different results when evaluated in different contexts.
Explanation of the latter (4th) example:
The $ in $a[0] creates a scalar context. The context affects the way the comma operator works, creating different results:
"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."
http://perldoc.perl.org/perlop.html#Comma-Operator
Hence: b
To understand the line beginning with: #a[0] consider:
($a, $b, $c) = ("a", "b", "c", "d"); # here the "d" is discarded
print $a, $b, $c; # this prints: abc
($a, $b) = ("a", "b", "c"); # here the "c" is discarded
($a) = ("a", "b"); # here the "b" is discarded
($a[0]) = ("a", "b"); # here the "b" is discarded
It would appear that parentheses at the beginning of the line create a list context. This is pretty much what it says at: http://docstore.mik.ua/orelly/perl4/prog/ch02_07.htm
"Assignment to a list of scalars also provides a list context to the righthand side, even if there's only one element in the list"
# at the beginnning of a line also creates a list context:
#a[0] = ("a", "b") means evaluate the RHS in the same way as above i.e. discard the "b"

Related

Changing element's positions in Perl

So I have a problem and I can't solve it. If I read some words from a file in Perl, in that file the words aren't in order, but have a number (as a first character) that should be the element's position to form a sentence.The 0 means that position is correct, 1 means that the word should be in position [1] etc.
The file looks like: 0This 3a 4sentence 2be 1should, and the solution should look like 0This 1should 2be 3a 4sentence.
In a for loop I get through the words array that i get from the file, and this is how i get the first character(the number) $firstCharacter = substr $words[$i], 0, 1;, but i don't know how to properly change the array.
Here's the code that I use
#!/usr/bin/perl -w
$arg = $ARGV[0];
open FILE, "< $arg" or die "Can't open file: $!\n";
$/ = ".\n";
while($row = <FILE>)
{
chomp $row;
#words = split(' ',$row);
}
for($i = 0; $i < scalar #words; $i++)
{
$firstCharacter = substr $words[$i], 0, 1;
if($firstCharacter != 0)
{
}
}
Just use sort. You can use a match in list context to extract the numbers, using \d+ will work even for numbers > 9:
#! /usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
my #words = qw( 0This 3a 4sentence 2be 1should );
say join ' ', sort { ($a =~ /\d+/g)[0] <=> ($b =~ /\d+/g)[0] } #words;
If you don't mind the warnings, or you are willing to turn them off, you can use numeric comparison directly on the words, Perl will extract the numeric prefixes itself:
no warnings 'numeric';
say join ' ', sort { $a <=> $b } #words;
Assuming you have an array like this:
my #words = ('0This', '3a', '4sentence', '2be', '1should');
And you want it sorted like so:
('0This', '1should', '2be', '3a', '4sentence');
There's two steps to this. First is extracting the leading number. Then sorting by that number.
You can't use substr, because you don't know how long the number might be. For example, ('9Second', '12345First'). If you only looked at the first character you'd get 9 and 1 and sort them incorrectly.
Instead, you'd use a regex to capture the number.
my($num) = $word =~ /^(\d+)/;
See perlretut for more on how that works, particularly Extracting Matches.
Now that you can capture the numbers, you can sort by them. Rather than doing it in loop yourself, sort handles the sorting for you. All you have to do is supply the criterion for the sorting. In this case we capture the number from each word (assigned to $a and $b by sort) and compare them as numbers.
#words = sort {
# Capture the number from each word.
my($anum) = $a =~ /^(\d+)/;
my($bnum) = $b =~ /^(\d+)/;
# Compare the numbers.
$anum <=> $bnum
} #words;
There are various ways to make this more efficient, in particular the Schwartzian Transform.
You can also cheat a bit.
If you ask Perl to treat something as a number, it will do its damnedest to comply. If the string starts with a number, it will use that and ignore the rest, though it will complain.
$ perl -wle 'print "23foo" + "42bar"'
Argument "42bar" isn't numeric in addition (+) at -e line 1.
Argument "23foo" isn't numeric in addition (+) at -e line 1.
65
We can take advantage of that to simplify the sort by just comparing the words as numbers directly.
{
no warnings 'numeric';
#words = sort { $a <=> $b } #words;
}
Note that I turned off the warning about using a word as a number. use warnings and no warnings only has effect within the current block, so by putting the no warnings 'numeric' and the sort in their own block I've only turned off the warning for that one sort statement.
Finally, if the words are in a file you can use the Unix sort utility from the command line. Use -n for "numeric sorting" and it will do the same trick as above.
$ cat test.data
00This
3a
123sentence
2be
1should
$ sort -n test.data
00This
1should
2be
3a
123sentence
You should be able to split on the spaces, which will make the numbers the first character of the word. With that assumption, you can simply compare using the numerical comparison operator (<=>) as opposed to the string comparison (cmp).
The operators are important because if you compare strings, the first character is used, meaning 10, 11, and 12 would be out of order, and listed near the 1 (1,10,11,12,2,3,4… instead of 1,2,3,4…10,11,12).
Split, Then Sort
Note: #schwern commented an important point. If you use warnings -- and you should -- you will receive warnings. This is because the values of the internal comparison variables, $a and $b, aren't numbers, but strings (e.g., `"0this", "3a"). I've update the following Codepad and provided more suitable alternatives to avoid this issue.
http://codepad.org/xs2GH9xT
use strict;
use warnings;
my $line = q{0This 3a 4sentence 2be 1should};
my #words = split /\s/,$line;
my #sorted = sort {$a <=> $b} #words;
print qq{
Line: $line
Words: #words
Sorted: #sorted
};
Alternatives
One method is to ignore the warning using no warnings 'numeric' as in Schwern's answer. As he has shown, turning off the warnings in a block will re-enable it afterwards, which may be a little foolproof compared to Choroba's answer, which applies it to the broader scope.
Choroba's solution works by parsing the digits from the those values internally. This is much fewer lines of code, but I would generally advise against that for performance reasons. The regex isn't only run once per word, but multiple times over the sorting process.
Another method is to strip the numbers out and use them for the sort comparison. I attempt to do this below by creating a hash, where the key will be the number and the value will be the word.
Hash Mapping / Key Sort
Once you have an array where the values are the words prefixed by the numbers, you could just as easily split those number/word combo into a hash that has the key as the number and value as the word. This is accomplished by using split.
The important thing to note about the split statement is that a limit is passed (in this case 2), which limits the maximum number of fields the string is split into.
The two values are then used in the map to build the key/value assignment. Thus "0This" is split into "0" and "This" to be used in the hash as "0"=>"This"
http://codepad.org/kY8wwajc
use strict;
use warnings;
my $line = q{0This 3a 4sentence 2be 1should};
my #words = split /\s/, $line; # [ '0This', '3a', ... ]
my %mapped = map { split /(?=\D)/, $_, 2 } #words; # { '0'=>'This, '3'=>'a', ... }
my #sorted = #mapped{ sort { $a <=> $b } keys %mapped }; # [ 'This', 'should', 'be', ... ]
print qq{
Line: $line
Words: #words
Sorted: #sorted
};
This also can be further optimized, but uses multiple variables to illustrate the steps in the process.

How can I create a variable array name in Perl?

Array #p is a multiline array, e.g. $p[1] is the second line.
This code will explain what I want:
$size=#p; # line number of array #p
for($i=0; $i<$size; $i++)
{
#p{$i}= split(/ +/,$p[$i]);
}
I want the result should be like this:
#p0 = $p[0] first line of array #p goes to array #p0;
#p1 = $p[1] second line of array #p goes to array #p1;
...
...
and so on.
But above code does not work, how can I do it?
It is a bad idea to dynamically generate variable names.
I suggest the best solution here is to convert each line in your #p array to an array of fields.
Lets suppose you have a better name for #p, say #lines. Then you can write
my #lines = map [ split ], <$fh>;
to read in all the lines from the file handle $fh and split them on whitespace. The first field of the first line is then $lines[0][0]. The third field of the first line is $lines[0][2] etc.
First, the syntax #p{$i} accesses the entry with the key $i in a hash %p, and returns it in list context. I don't think you meant that. use strict; use warnings; to get warned about undeclared variables.
You can declare variables with my, e.g. my #p; or my $size = #p;
Creating variable names on the fly is possible, but a bad practice. The good thing is that we don't need to: Perl has references. A reference to an array allows us to nest arrays, e.g.
my #AoA = (
[1, 2, 3],
["a", "b"],
);
say $AoA[0][1]; # 2
say $AoA[1][0]; # a
We can create an array reference by using brackets, e.g. [ #array ], or via the reference operator \:
my #inner_array = (1 .. 3);
my #other_inner = ("a", "b");
my #AoA = (\#inner_array, \#other_array);
But careful: the array references still point to the same array as the original names, thus
push #other_inner, "c";
also updates the entry in #AoA:
say $AoA[1][2]; # c
Translated to your problem this means that you want:
my #pn;
for (#p) {
push #pn, [ split /[ ]+/ ];
}
There are many other ways to express this, e.g.
my #pn = map [ split /[ ]+/ ], #p;
or
my #pn;
for my $i ( 0 .. $#p ) {
$pn[$i] = [ split /[ ]+/, $p[$i] ];
}
To learn more about references, read
perlreftut,
perldsc, and
perlref.

What exactly mean the ${...} in the perl?

Thinking about the next. Have a LIST
qw(a b c);
now, assign the LIST into nameless (anonymous) ARRAY
[ qw(a b c) ]
so the next
use 5.016;
use warnings;
use diagnostics;
my $x = [ qw(a b c) ];
say ref $x; #ARRAY - the $x is an ARRAY reference, and
say $x->[1]; #prints "b", and
say [ qw(a b c) ]->[1]; #works too
but what happens now?
use 5.016;
use warnings 'all';
use diagnostics;
say ${[ qw(a b c) ]}[1];
it prints b, but
my $y = ${[ qw(a b c) ]};
is an error,
Not a SCALAR reference at pepe line 6 (#1)
(F) Perl was trying to evaluate a reference to a scalar value, but found
a reference to something else instead. You can use the ref() function
to find out what kind of ref it really was. See perlref.
Uncaught exception from user code:
Not a SCALAR reference at pepe line 17.
So, whats mean the contruction ${.... }
It "works" in the say (prints the second element of the anonymous array), but don't understand why
but can't assign it into variable
And the hint from the diagnostics is not very helpful, because how I should use the ref when I can't assign? What I missed from the perlref?
${ EXPR1 }[ EXPR2 ] is an array index dereference. It returns the element at the index returned by EXPR2 of the array referenced by the reference returned by EXPR1.
${ $array_ref }[ ... ] is to array references as $array[...] is to arrays.
${ EXPR } that's not followed by [ or { is a scalar dereference. It returns the scalar referenced by the reference returned by EXPR.
${ $scalar_ref } is to scalar references as $scalar is to scalars.
As you can see, when dealing with a reference, you can use the same syntax as you normally would, except that you replace the name of the variables with {$ref} (keeping the leading sigil).
As such, #{ $array_ref } is to array references as #array is to arrays.
say #{[ qw(a b c) ]};
This is the essence of the chart in my earlier post Mini-Tutorial: Dereferencing Syntax. See also:
References quick reference
perlref
perlreftut
perldsc
perllol
Oops, I thought you had
say ${[ qw(a b c) ]}; # Want to print a b c
You have
my $y = ${[ qw(a b c) ]};
You want
my $y = [ qw(a b c) ];
[] creates an array and a reference to that array, and returns the latter, kinda like
my $y = do { my #anon = qw(a b c); \#a };
Given an array #arr and an arrayref $aref = \#arr, then inside the following groups of expressions all lines are equivalent:
Accessing the whole array:
# arr
#{$aref}
Accessing a single scalar in the array:
$ arr [$i]
${$aref}[$i]
$ aref->[$i]
Accessing a slice of entries:
# arr [$i .. $j]
#{$aref}[$i .. $j]
(the spaces are included for alignment and are not recommended for actual code).
The ${}, #{}, … are circumfix dereference operators. However, accessing a single scalar changes the sigil from % or # to $. Without references, this makes total sense. With them, it's just slightly complicated, until you read perlreftut (esp. the two reference usage rules).
With the
${$ar}[0]
you said to perl: take $ar as arrayref and return me 1st element from the array to what the reference $ar points.
With the construction
${$sr}
you saying to perl: take the $sr as SCALAR REF and return the value of the scalar to what the reference $sr points.
Therefore, the answer to your question from the comment: When the $ar is an array-ref, WHAT IS the ${$ar}? is:
When the $ar is an $array-ref, the ${$ar} is an ERROR,
because you said to perl - take the scalar-ref, but the $ar is NOT scalar-ref (it is arrayref).
The next example show your constructions clearly:
use 5.012;
use warnings;
my #arr = qw(a b c);
my $aref = \#arr;
my $myval = "VALUE";
my $sref = \$myval;
say $aref->[0];
say ${$aref}[0];
say ${$sref}; #your construction - here CORRECTLY points to an scalar
#say ${$aref} #ERROR because the ${$xxx} mean: take scalar ref, but the $aref is array ref.

Perl: Dereferencing Array

Why does the following code not work in getting into an anonymous array?
my #d = [3,5,7];
print $(#{$d[0]}[0]);
# but print $d[0][0] works.
Script 1 (original)
Because it is invalid Perl code?
#!/usr/bin/env perl
use strict;
use warnings;
my #d = [3,5,7];
print $(#{$d[0]}[0]);
When compiled (perl -c) with Perl 5.14.1, it yields:
Array found where operator expected at xx.pl line 6, at end of line
(Missing operator before ?)
syntax error at xx.pl line 6, near "])"
xx.pl had compilation errors.
Frankly, I'm not sure why you expected it to work. I can't make head or tail of what you were trying to do.
The alternative:
print $d[0][0];
works fine because d is an array containing a single array ref. Thus $d[0] is the array (3, 5, 7) (note parentheses instead of square brackets), so $d[0][0] is the zeroth element of the array, which is the 3.
Script 2
This modification of your code prints 3 and 6:
#!/usr/bin/env perl
use strict;
use warnings;
my #d = ( [3,5,7], [4,6,8] );
print $d[0][0], "\n";
print $d[1][1], "\n";
Question
So the $ in $d[0] indicates that [3,5,7] is dereferenced to the array (3,5,7), or what does the $ do here? I thought the $ was to indicate that a scalar was getting printed out?
Roughly speaking, a reference is a scalar, but a special sort of scalar.
If you do print "$d[0]\n"; you get output something like ARRAY(0x100802eb8), indicating it is a reference to an array. The second subscript could also be written as $d[0]->[0] to indicate that there's another level of dereferencing going on. You could also write print #{$d[0]}, "\n"; to print out all the elements in the array.
Script 3
#!/usr/bin/env perl
use strict;
use warnings;
$, = ", ";
my #d = ( [3,5,7], [4,6,8] );
#print $(#{$d[0]}[0]);
print #d, "\n";
print $d[0], "\n";
print #{$d[0]}, "\n";
print #{$d[1]}, "\n";
print $d[0][0], "\n";
print $d[1][1], "\n";
print $d[0]->[0], "\n";
print $d[1]->[1], "\n";
Output
ARRAY(0x100802eb8), ARRAY(0x100826d18),
ARRAY(0x100802eb8),
3, 5, 7,
4, 6, 8,
3,
6,
3,
6,
I think you are trying for this:
${$d[0]}[0]
Though of course there's always the syntactic sugar way, too:
$d[0]->[0]
The square bracket constructor creates an anonymous array, but you are storing it in another array. This means that you are storing the three element array inside the first element of a one element array. This is why $d[0][0] will return the value 3. To do a single level array use the list constructor:
my #d = (3,5,7);
print $d[0];
If you really mean to create the array inside the outer array then you should dereference the single (scalar) value as
print ${$d[0]}[0].
For more read perldoc perlreftut.

Ways to Flatten A Perl Array in Scalar Context

I recently started learning perl and have a question that I'm not finding a clear answer to on the Internet. say I have something like this,
#arr = (1, 2, 3);
$scal = "#arr"
# $scal is now 123.
Is the use of quotes the only way to flatten the array so that each element is stored in the scalar value? It seems improbable but I haven't found any other ways of doing this. Thanks in advance.
The join function is commonly used to "flatten" lists. Lets you specify what you want between each element in the resulting string.
$scal = join(",", #arr);
# $scal is no "1,2,3"
In your example, you're interpolating an array in a double-quoted string. What happens in those circumstances is is controlled by Perl's $" variable. From perldoc perlvar:
$LIST_SEPARATOR
$"
When an array or an array slice is interpolated into a double-quoted string or a similar context such as /.../ , its elements are separated by this value. Default is a space. For example, this:
print "The array is: #array\n";
is equivalent to this:
print "The array is: " . join($", #array) . "\n";
Mnemonic: works in double-quoted context.
The default value for $" is a space. You can obviously change the value of $".
{
local $" = ':',
my #arr = (1, 2, 3);
my $scalar = "#arr"; # $scalar contains '1:2:3'
}
As with any of Perl's special variables, it's always best to localise any changes within a code block.
You could also use join without any seperator
my $scalar = join( '' , #array ) ;
There is more than one way to do it.
in the spirit of TIMTOWTDI:
my $scal;
$scal .= $_ foreach #arr;
Read section Context in perldata. Perl has two major contexts: scalar and list.
For example:
#a = (1, 1, 1); # list context
print #a; # list context
$count = #a; # scalar context, returns the number of elements in #a
etc.

Resources