What is the difference between #{$value} and #$value in perl? - perl

Example:
for my $key ( keys %DNS_CSV_FILES){
for my $file (#$DNS_CSV_FILES{$key}){
print $file;
}
}
gives the error:
Global symbol "$DNS_CSV_FILES" requires explicit package name (did you forget to declare "my $DNS_CSV_FILES"?) at dns.pl line 41.
Execution of dns.pl aborted due to compilation errors.
But this code:
for my $key ( keys %DNS_CSV_FILES){
for my $file (#{$DNS_CSV_FILES{$key}}){
print $file;
}
}
gives the desired output:
file1.txtfile2.txt

#$x{ $key } is short for #{ $x }{ $key }, not #{ $x{ $key } }.
See Perl Dereferencing Syntax. Footnote [1] in particular. The curlies can only be omitted around a simple scalar variable.
There is no difference between #{ $x } and #$x. But that's not what the two snippets are using.
The first is using #$x{ $key }, which is short for #{ $x }{ $key }.
There is a difference between #{ $x }{ $key } and #{ $x{ $key } }.
#foo{ $key } is a slice of a named array, so #{ ... }{ $key } is a slice of a referenced array. #{ $DNS_CSV_FILES }{ $key } is therefore a slice of the array referenced by scalar $DNS_CSV_FILES.
#foo is a array provided by name, so #{ ... } is a referenced array. #{ $DNS_CSV_FILES{ $key } } is therefore an array referenced by hash element $DNS_CSV_FILES{ $key }.

In short, Perl's dereference syntax puts braces around the reference. However, you can leave off the braces if the reference is simple scalar, like $value. For anything else, including a hash key lookup, you keep the braces.
That's the old-style "circumfix" notation. Perl v5.24 stabilized the postfix dereference syntax.

This
#$DNS_CSV_FILES{$key}
Will from the left side see an array sigil # followed by a scalar $. This can only be the dereferencing of an array ref. Otherwise the # is a syntax error. Despite you putting the hash notation at the end. It is a race condition, of sorts. So it will assume that what follows is a scalar, and not a hash value.
When you clarify by adding extra brackets, it becomes clear what is intended
#{ $DNS_CSV_FILES{$key} }
Whatever is inside #{ } must be an array ref, and $....{key} must be a hash value.

Related

Perl unexpected result

Imagine I have this Perl script
my $name = " foo ";
my $sn = " foosu";
trim($name, \$sn);
print "name: [$name]\n";
print "sn: [$sn]\n";
exit 0;
sub trim{
my $fref_trim = sub{
my ($ref_input) = #_;
${$ref_input} =~ s/^\s+// ;
${$ref_input} =~ s/\s+$// ;
};
foreach my $input (#_){
if (ref($input) eq "SCALAR"){
$fref_trim->($input);
} else {
$fref_trim->(\$input);
}
}
}
Result:
name: [foo]
sn: [foosu]
I would expect $name to be "[ foo ]" when printing the value after calling trim, but the sub is setting $name as I would want it. Why is this working, when it really shouldn't?
I'm not passing $name by reference and the trim sub is not returning anything. I'd expect the trim sub to create a copy of the $name value, process the copy, but then the original $name would still have the leading and trailing white spaces when printed in the main code.
I assume it is because of the alias with #_, but shouldn't the foreach my $input (#_) force the sub to copy the value and only treat the value not the alias?
I know I can simplify this sub and I used it only as an example.
Elements of #_ are aliases to the original variables. What you are observing is the difference between:
sub ltrim {
$_[0] =~ s/^\s+//;
return $_[0];
}
and
sub ltrim {
my ($s) = #_;
$s =~ s/^\s+//;
return $s;
}
Compare your code to:
#!/usr/bin/env perl
my $name = " foo ";
my $sn = " foosu";
trim($name, \$sn);
print "name: [$name]\n";
print "sn: [$sn]\n";
sub trim {
my #args = #_;
my $fref_trim = sub{
my ($ref_input) = #_;
${$ref_input} =~ s/^\s+//;
${$ref_input} =~ s/\s+\z//;
};
for my $input (#args) {
if (ref($input) eq "SCALAR") {
$fref_trim->($input);
}
else {
$fref_trim->(\$input);
}
}
}
Output:
$ ./zz.pl
name: [ foo ]
sn: [foosu]
Note also that the loop variable in for my $input ( #array ) does not create a new copy for each element of the array. See perldoc perlsyn:
The foreach loop iterates over a normal list value and sets the scalar variable VAR to be each element of the list in turn. ...
...
the foreach loop index variable is an implicit alias for each item in the list that you're looping over.
In your case, this would mean that, at each iteration $input is an alias to the corresponding element of #_ which itself is an alias to the variable that was passed in as an argument to the subroutine.
Making a copy of #_ thus prevents the variables in the calling context from being modified. Of course, you could do something like:
sub trim {
my $fref_trim = sub{
my ($ref_input) = #_;
${$ref_input} =~ s/^\s+//;
${$ref_input} =~ s/\s+\z//;
};
for my $input (#_) {
my $input_copy = $input;
if (ref($input_copy) eq "SCALAR") {
$fref_trim->($input_copy);
}
else {
$fref_trim->(\$input_copy);
}
}
}
but I find making a wholesale copy of #_ once to be clearer and more efficient assuming you do not want to be selective.
I assume it is because of the alias with #_, but shouldn't the foreach my $input (#_) force the sub to copy the value and only treat the value not the alias?
You're right that #_ contains aliases. The part that's missing is that foreach also aliases the loop variable to the current list element. Quoting perldoc perlsyn:
If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. In other words, the foreach loop index variable is an implicit alias for each item in the list that you're looping over.
So ultimately $input is an alias for $_[0], which is an alias for $name, which is why you see the changes appearing in $name.

Perl: How to access an array thats inside of 3 hashes passed to subroutine by reference

I have a code that is something like this:
foreach $item (#total_data)
{
setinfo($item);
} # #total_data contains an array of references to hashes (\%hash1 ... \%hashN)
In the subrutine goes something like this:
sub setinfo
{
my ($argument) = #_;
my $i = 0;
#inside original hash $argument{"data"}{"fulldraw"} there is an [array]
#that contains numbers of the form XYYZ and I want to split them into
#the following pairs XY YY YZ but that code works ok#
foreach $item (${$argument{"data"}{"fulldraw"}})
{
my $match;
my $matchedstr;
if ($item =~ /^\d{4}$/)
{
...
}
else
{
print STDERR "DISCARDED: $item\n";
}
}
}
I know I am probably making the mistake in how I am dereferencing it, but couldn't figure it out with all the articles I've read on the internet.
Thanks!
#{ ... } # dereference
Maybe $argument is a hashref; you need to use
foreach $item (#{ $argument->{data}->{fulldraw} })
Just use dereference #{ ... }:
foreach $item (#{ $argument->{data}{fulldraw} })
You can use Data::Dumper to visualize complex structures:
use Data::Dumper;
print Dumper($argument);

what is the usage of \& and $expr->()

sub reduce(&#) {
my $expr = \&{shift #ARG};
my $result = shift #ARG;
while (scalar #ARG > 0) {
our $a = $result;
our $b = shift #ARG;
$result = $expr->();
}
return $result;
}
I cannot really understand some grammar in this code. Anyone can explain to me? like \& and $result = $expr->()
\&name returns a reference to the subroutine named name.
$code_ref->() calls the subroutine referenced by $code_ref.
$ perl -e'
sub f { CORE::say "Hi" }
my $code_ref = \&f;
$code_ref->();
'
Hi
In your case, shift #ARG returns a subroutine reference. \&{ $code_ref } simply returns the code ref. As such,
my $expr = \&{shift #ARG};
could have been written as
my $expr = shift #ARG;
Note that reduce's prototype allows it to be called as
reduce { ... } ...
but what is actually executed is
reduce( sub { ... }, ... )
Note that this version of reduce is buggy. You should use the one provided by List::Util.
local $a and local $b should have been used to avoid clobbering the values its caller might have in $a and $b.
This version of reduce expects its callback to have been compiled in the same package as reduce itself. Otherwise, the callback sub won't be able to simply use $a and $b.
Declaring the variables using our is actually completely useless in this version case since $a and $b are exempt from use strict; checks, and the undeclared use of $a and $b would access the very same package variables.
Having a look some List::Util::reduce() examples will probably help.
Let's take the first one:
$foo = reduce { $a > $b ? $a : $b } 1..10;
So reduce takes a BLOCK followed by a LIST, which the function signature declares: sub reduce(&#) {. The block in our case is the statement $a > $b ? $a : $b, while the list is 1..10. From the docs:
Reduces #list by calling "BLOCK" in a scalar context multiple times,
setting $a and $b each time. The first call will be with $a and $b set to
the first two elements of the list, subsequent calls will be done by
setting $a to the result of the previous call and $b to the next element
in the list.
Returns the result of the last call to the "BLOCK". If #list is empty then
"undef" is returned. If #list only contains one element then that element
is returned and "BLOCK" is not executed.
And now to an annotated version of the code:
$foo = reduce { $a > $b ? $a : $b } 1..10; # $foo will be set to 10
sub reduce(&#) {
# reduce() takes a BLOCK followed by a LIST
my $expr = \&{shift #ARG};
# $expr is now a subroutine reference, i.e.
# $expr = sub { $a > $b ? $a : $b };
# Start by setting $result to the first item in the list, 1
my $result = shift #ARG;
# While there are more items in the list...
while (scalar #ARG > 0) {
# Set $a to the current result
our $a = $result;
# Set $b to the next item in the list
our $b = shift #ARG;
# Set $result to the result of $a > $b ? $a : $b
$result = $expr->();
}
# List has now been reduced by the operation $a > $b ? $a : $b
return $result;
}

Can I pass arguments to the compare subroutine of sort in Perl?

I'm using sort with a customized comparison subroutine I've written:
sub special_compare {
# calc something using $a and $b
# return value
}
my #sorted = sort special_compare #list;
I know it's best use $a and $b which are automatically set, but sometimes I'd like my special_compare to get more arguments, i.e.:
sub special_compare {
my ($a, $b, #more) = #_; # or maybe 'my #more = #_;' ?
# calc something using $a, $b and #more
# return value
}
How can I do that?
Use the sort BLOCK LIST syntax, see perldoc -f sort.
If you have written the above special_compare sub, you can do, for instance:
my #sorted = sort { special_compare($a, $b, #more) } #list;
You can use closure in place of the sort subroutine:
my #more;
my $sub = sub {
# calc something using $a, $b and #more
};
my #sorted = sort $sub #list;
If you want to pass the elements to be compared in #_, set subroutine's prototype to ($$). Note: this is slower than unprototyped subroutine.

What's wrong with this statement in Perl?

print "$_", join(',',sort keys %$h),"\n";
It's giving me an error below:
Use of uninitialized value in string at missing_months.pl line 36.
1,10,11,12
this print statement is present in a for loop as below:
foreach my $num ( sort keys %hash )
{
my $h = $hash{$num};
print "$_", join(',',sort keys %$h),"\n";
}
No need for the "$_". That line should be:
print join (',' , sort {$a <=> $b} keys %$h),"\n";
While the $_ is treated as the default iterator in for and foreach loops (see perlvar), you've already assigned the iterator variable as $num.
Here is how to use the $_ correctly in a single line:
print join(',', sort { $a <=> $b } keys %{$hash{$_}}),"\n" foreach keys %hash;
On a Side Note...
sort uses string comparison by default, meaning that '10' is deemed to come before '2'. It seems that you're dealing with months (perhaps?), which is why I've used the numerical comparison block { $a <=> $b }.