Method invocation does not supply scalar context... seems strange - perl

This behavior isn't Math::BigInt specific, but the following code breaks on the last line.
use strict;
use warnings;
use Math::BigInt;
my $a = Math::BigInt->bone;
my $b = Math::BigInt->bone;
print ($a+$b)->bfac;
This code, however, works fine:
use strict;
use warnings;
use Math::BigInt;
my $a = Math::BigInt->bone;
my $b = Math::BigInt->bone;
print scalar($a+$b)->bfac;
My question is this... why isn't scalar context imposed automatically on the left argument of "->"? AFAIK, "->" only works on scalars and (exceptionally) on typeglobs.

You need one more set of parens,
print (($a+$b)->bfac);
as your code is interpreted as,
(print ($a+$b))->bfac;
and warnings also gave you print (...) interpreted as function ..

Need a + so it's not interpreted as arguments to print.
print +($a+$b)->bfac;

Related

Perl syntax error printing list index

I am trying to print an index of a list returned from a function call. I am wrapping the function call in parenthesis in an attempt to convert the list to an array. The program fails to compile saying 'syntax error at file.pl line 4, near ")["'. If I create a temporary variable $a or use printf indexing is fine. Why does print brake, is there a better alternative?
sub get{
return (1,2);
}
#print (get())[0]; #fails
printf("%d",(get())[0]);
my $a = (get())[0];
print $a;
I assume that you are foolishly not using
use warnings;
Which is quite a bad thing to not do. If you had used it, you would get the warning:
print (...) interpreted as function
Which is to say, what you wrote
print (get())[0]
Is something Perl interprets as you trying to put a subscript on the print() function. This does not work.
For the code to do what you expect, you need to be explicit about the parentheses:
print ( (get())[0] );
You should always use
use strict;
use warnings;
There is a slight learning curve to using these, but they only show you your errors. Not using them only hides your errors, and your code does not work better.
Try this :
sub get{
return (1,2);
}
print((get)[0]);
or
my $a = (get)[0];
print $a;
OUTPUT
1

Difference between a BLOCK and a function in terms of scoping in Perl

Guys I'm a little bit confused, I was playing with scoping in Perl, when i encountered this one:
#! usr/bin/perl
use warnings;
use strict;
sub nested {
our $x = "nested!";
}
print $x; # Error "Variable "$x" is not imported at nested line 10."
print our $x; # Doesn't print "nested!"
print our($x) # Doesn't print "nested!"
But when i do this:
{
our $x = "nested";
}
print our($x); # Prints "nested"
print our $x; # Prints "nested"
print $x; # Prints "nested"
So guys can you explain to me why those works and not?
To restate DVK's answer, our is just a handy aliasing tool. Every variable you use in these examples is actually named $main::x. Within any lexical scope you can use our to make an alias to that variable, with a shortened name, in that same scope; the variable doesn't reset or get removed outside, only the alias. This is unlike the my keyword which makes a new variable bound to that lexical scope.
To explain why the block example works the way it does, let's look at our explanation from "Modern Perl" book, chapter 5
Our Scope
Within given scope, declare an alias to a package variable with the our builtin.
The fully-qualified name is available everywhere, but the lexical alias is visible only within its scope.
This explains why the first two prints of your second example work (our is re-declared in print's scope), whereas the third one does not (as our only aliases $x to the package variable within the block's scope). Please note that printing $main::x will work correctly - it's only the alias that is scoped to the block, not the package variable itself.
As far as with the function:
print our $x; and print our($x) "don't work" - namely, correctly claim the value is uninitialized - since you never called the function which would initialize the variable. Observe the difference:
c:\>perl -e "use strict; use warnings; sub x { our $x = 1;} print our $x"
Use of uninitialized value $x in print at -e line 1.
c:\>perl -e "use strict; use warnings; sub x { our $x = 1;} x(); print our $x"
1
print $x; won't work for the same reason as with the block - our only scopes the alias to the block (i.e. in this case body of the sub) therefore you MUST either re-alias it in the main block's scope (as per print our $x example), OR use fully qualified package global outside the sub, in which case it will behave as expected:
c:\>perl -e "use strict; use warnings; sub x { our $x = 1;} print $main::x"
Use of uninitialized value $x in print at -e line 1.
c:\>perl -e "sub x { our $x = 1;} x(); print $main::x"
1

Using a sorting subroutine from another package

I have a script and a package like so:
# file: sortscript.pl
use strict;
use warnings;
use SortPackage;
my #arrays = ([1,"array1"],[10,"array3"],[4,"array2"]);
print "Using sort outside package\n";
foreach (sort SortPackage::simplesort #arrays){
print $_->[1],"\n";
}
print "\nUsing sort in same package\n";
SortPackage::sort_from_same_package(#arrays);
--
# file: SortPackage.pm
use strict;
use warnings;
package SortPackage;
sub simplesort{
return ($a->[0] <=> $b->[0]);
}
sub sort_from_same_package{
my #arrs = #_;
foreach (sort simplesort #arrs){
print $_->[1],"\n";
}
}
1;
Running the script produces the output:
$ perl sortscript.pl
Using sort outside package
Use of uninitialized value in numeric comparison (<=>) at SortPackage.pm line 15.
Use of uninitialized value in numeric comparison (<=>) at SortPackage.pm line 15.
Use of uninitialized value in numeric comparison (<=>) at SortPackage.pm line 15.
Use of uninitialized value in numeric comparison (<=>) at SortPackage.pm line 15.
Use of uninitialized value in numeric comparison (<=>) at SortPackage.pm line 15.
Use of uninitialized value in numeric comparison (<=>) at SortPackage.pm line 15.
array1
array3
array2
Using sort in same package
array1
array2
array3
Why am I not able to correctly use the subroutine to sort with when it is in another package?
As has been mentioned, $a and $b are package globals, so another solution is to temporarily alias the globals at the call site to the ones in package SortPackage:
{
local (*a, *b) = (*SortPackage::a, *SortPackage::b);
foreach (sort SortPackage::simplesort #arrays){
print $_->[1],"\n";
}
}
But this is pretty ugly, of course. I would just have SortPackage export a complete sorting routine, not just a comparator:
package SortPackage;
use strict;
sub _sort_by_first_element_comparator {
return $a->[0] <=> $b->[0];
}
sub sort_by_first_element {
return sort _sort_by_first_element_comparator #_;
}
$a and $b are special "package global" variables.
To use the main scope's $a and $b your comparator function would have to refer to $::a or $main::a (and likewise for $b).
However that comparator function then wouldn't work when called from any other package, or even from within its own package.
See the perlvars help and the perldoc for the sort function. The solution is also in the latter help text:
If the subroutine’s prototype is "($$)", the elements to be
compared are passed by reference in #_, as for a normal
subroutine. This is slower than unprototyped subroutines,
where the elements to be compared are passed into the
subroutine as the package global variables $a and $b (see
example below). Note that in the latter case, it is usually
counter‐productive to declare $a and $b as lexicals.
The special variables $a and $b are package globals. Your subroutine expects $SortPackage::a and $SortPackage::b. When you call it from sortscript.pl, the variables $main::a and $main::b are being set by sort.
The solution is to use a prototyped subroutine:
package SortPackage;
sub simplesort ($$) {
return ($_[0]->[0] <=> $_[1]->[0]);
}
It's a little slower (since you have actual parameters being passed, instead of reading pre-set globals), but it allows you to use subroutines from other packages by name, as you are attempting.

help with perl script converting use of argv to using getopts

I am trying to convert the use of #ARGV with using Getopt::Std instead in my perl script.
I am getting some substr errors and need some help figuring this out.
Errors:
Use of uninitialized value in substr at ./h.pl line 33.
Use of uninitialized value in substr at ./h.pl line 33.
substr outside of string at ./h.pl line 33.
Use of uninitialized value in substr at ./h.pl line 33.
substr outside of string at ./h.pl line 33.
The 'month' parameter (undef) to DateTime::new was an 'undef', which is not one of the allowed types: scalar
at /usr/lib64/perl5/vendor_perl/5.8.8/x86_64-linux-thread-multi/DateTime.pm line 176
DateTime::new('undef', 'HASH(0xb6932d0)') called at ./h.pl line 33
Here is my code. (commented out code was working code using #ARGV)
use strict;
use warnings;
use Getopt::Std;
use DateTime;
# Getopt usage
my %opt;
getopts ('fd:ld:h', \%opt);
$opt{h} and &Usage;
my $first_date = $opt{fd};
my $last_date = $opt{ld};
#unless(#ARGV==2)
#{
# print "Usage: myperlscript first_date last_date\n";
# exit(1);
#}
#
#my ($first_date,$last_date)=#ARGV;
# Convert using Getopts
my $date=DateTime->new(
{
year=>substr($first_date,0,4),
month=>substr($first_date,4,2),
day=>substr($first_date,6,2)
});
while($date->ymd('') le $last_date)
{
print $date->ymd('') . "\n";
$date->add(days=>1);
}
Even if you think Getopt::Std will do what you want, use Getopt::Long. For pretty much the same reasons you'd not just hand-roll an #ARGV handler.
To quote (in part) tchrist in http://www.nntp.perl.org/group/perl.perl5.porters/2008/05/msg136952.html:
I really like Getopt::Long...I cannot say enough good things about it to do it the justice it deserves... The only problem is that I just don't use it enough. I bet I'm not alone. What seems to happen is that at first we just want to add--oh say for example JUST ONE, SINGLE LITTLE -v flag. Well, that's so easy enough to hand-hack, that of course we do so... But just like any other piece of software, these things all seem to have a way of overgrowing their original expectations... Getopt::Long is just wonderful, up--I believe--to any job you can come up with for it. Too often its absence means that I've in the long run made more work for myself--or others--by not having used it originally.
"getopt, getopts - Process single-character switches with switch clustering"
As only single character switches are allowed $opt{fd} and $opt{ld} are undef.
Getopt::Long does what you want.
use strict;
use warnings;
use Getopt::Long;
my $fd;
my $ld;
my $result = GetOptions(
'fd=s' => \$fd,
'ld=s' => \$ld,
);
die unless $result;
print "fd: $fd\n";
print "ld: $ld\n";

replace variable value and store it in new variable

I have below code to replace the variable value and store it in new variable and leave the original variable intact.
#!/usr/bin/perl
$hdisk="hdisk361";
($newdisk) = ($hdisk =~ s/(hdisk\D*)(\d+)/(($1 eq "hdiskpower"?"prw":"dsk").$2)/ei);
print "hdisk: $hdisk"."\n";
print "newdisk: $newdisk"."\n";
It gives this output:
hdisk: dsk361
newdisk: 1
I want the output like this:
hdisk: hdisk361
newdisk: dsk361
Please help me to fix this code?
Or a bit shorter:
#!/usr/bin/perl
$hdisk="hdisk361";
($newdisk = $hdisk) =~ s/(hdisk\D*)(\d+)/(($1 eq "hdiskpower"?"prw":"dsk").$2)/ei;
Otherwise, as you saw, you get 1, meaning a successful operation. In the code you provided you captured the return value instead of the result.
But don't forget to use use strict and use warnings ;)
Use
$hdisk = "hdisk361"
$newdisk = $hdisk;
$newdisk =~ s/(hdisk\D*)(\d+)/(($1 eq "hdiskpower"?"prw":"dsk").$2/
The substitution s/// works by side-effect. Its return value is hardly ever what you want. You especially don't want to use s here, when you seem not to want $hdisk to change.
Capture the pieces of $hdisk with m instead of s.
use strict;
use warnings;
my $hdisk="hdisk361";
my ($word, $number) = $hdisk =~ m/(hdisk\D*)(\d+)/i;
my $newdisk = ($word eq "hdiskpower"?"prw":"dsk").$number;
print "hdisk: $hdisk"."\n";
print "newdisk: $newdisk"."\n";
This answer is a bit superflous, but anyway, if you are using a fairly modern version of Perl (5.13+), you could have the original code working by just adding the r flag:
use 5.013;
($newdisk) = ($hdisk =~ s/(hdisk\D*)(\d+)/(($1 eq "hdiskpower"?"prw":"dsk").$2)/rei);
You could even let go of the parens:
use 5.013;
my $newdisk = $hdisk =~ s/(hdisk\D*)(\d+)/(($1 eq "hdiskpower"?"prw":"dsk").$2)/rei;
You can read more on the /r flag at Use the /r substitution flag to work on a copy.