What does the `if` modifier in `my` statement? [duplicate] - perl

This question already has answers here:
What is wrong in "my $foo = $x if $y" syntax?
(1 answer)
What is happening when my() is conditional?
(3 answers)
Closed 3 months ago.
The following minimal code example
use strict;
use warnings;
sub f {
my ( $c ) = #_;
my $n = 1 if 0;
$n or $n = $c if $c;
printf "%s\n", $n;
}
f( 3 );
f( 4 );
for me calculates
3
3
However, if I replace the if-modified my statement in the code and run
use strict;
use warnings;
sub f {
my ( $c ) = #_;
my $n;
$n or $n = $c if $c;
printf "%s\n", $n;
}
f( 3 );
f( 4 );
then I get
3
4
My first thought was that the conditional modifier would dupe strict and then $n would sometimes refer to a global. But the behaviour of the first snippet does not change if I drop use strict and define $n = 6; or my $n = 6; or our $n = 6; upfront before sub f.
What is happening here?

Related

How to grep only defined values from perl array [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have a lot of arrays that have some defined array elements, and then some undefined elements.
This problem does not show in simple cases like this:
my #x = sort {$a <=> $b} grep {defined} #{ $args{data} };
but this is allowing undefined values to get through in complex cases like this:
sub boxplot_series {
my %args = (
output_type => 'eps', #_ # defaults
);
my #labels = sort keys %{ $args{data} };# $args{data} should be a hash of arrays
my ($data_fh, $data_filename) = tempfile(UNLINK => 1);
my $x = 1;
foreach my $set (#labels) {
my $n = scalar #{ $args{data}{$set} };
if ($n == 0) {
confess "$set has no values.\n";
}
my #x = sort {$a <=> $b} grep {defined $_} #{ $args{data}{$set} };
if (grep undef #x) {
confess "\#x has undef.";
}
if (scalar #x == 0) { next }
my $n4 = floor(($n+3)/2)/2;
my #d = (1, $n4, ($n +1)/2, $n+1-$n4, $n);#d <- c(1, n4, (n + 1)/2, n + 1 - n4, n)
my #sum_array = $x;
$x++;
foreach my $e (0..4) {
my $floor = floor($d[$e]-1);
my $ceil = ceil($d[$e]-1);
# undef values can get through here too
push #sum_array, (0.5 * ($x[$floor] + $x[$ceil]));
}
....
How can I grep only defined elements from Perl arrays?
It works correctly.
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
my %args = (data => [4, 2, undef, 3, undef, 1]);
my #x = sort {$a <=> $b} grep {defined} #{ $args{data} };
say for #x;
But it breaks if you specify
my $a;
before the sort in the same scope. That's because $a (and $b) are special global variables and you shouldn't redeclare them. Now you know why.
With warnings on, you also get
"my $a" used in sort comparison at ...

Use bigint and float perl

I am extremely new to Perl, so I hope you will excuse my inexperience.
I have the following lines of code:
use warnings;
use strict;
use POSIX 'ceil';
use bigint;
my($g, $y, $n) = ($ARGV[0], $ARGV[1], $ARGV[2]);
my $z = ceil(sqrt($n-1));
my $entry;
print "list1: \n";
for my $v (0 .. $z) {
$entry = ($g ** $v) % $n;
$entry = ($entry ** ($n - 2)) % $n;
$entry = ($entry * $y) % $n;
print "$entry : $v\n";
}
print "list2: \n";
for my $u (0 .. $z) {
$entry = ($g ** ($u * $z)) % $n;
print "$entry: $u\n";
}
I need to use the bigint environment because of some following statements. Whenever I look into $z it evaluates to 6 instead of 7, when i call my program with $n = 41. It looks as if the bigint environment rounds the value of the sqrt method. I also tried to use BigFloat instead of bigint, but then the result of $entry = ($g ** ($u * $z)) % $n; gets calculated wrong (with ($g, $y, $n) = (15, 38, 41) the result is 3, when $u reached 3 in the for loop, but should be 26 instead).
Is there any option to avoid this rounding, so I can use float while calculating the square root and bigint in all following statements, so the pow operation works properly?
My call is perl program.pl 15 38 41. I try to implement the baby-step-giant-step algorithm.
you need to use bignum, not bigint:
$ cat bauer.pl
#!/usr/bin/perl
use warnings;
use strict;
use POSIX;
use bigint;
my($g, $y, $n) = ($ARGV[0], $ARGV[1], $ARGV[2]);
my $z = ceil(sqrt($n-1));
$ perl r.pl
1.41421356237309504880168872420969807857
With your program signature:
$ cat bauer.pl
#!/usr/bin/perl
use warnings;
use strict;
use POSIX;
use bignum;
my($g, $y, $n) = ($ARGV[0], $ARGV[1], $ARGV[2]);
my $z = ceil(sqrt($n-1));
print STDOUT "$z\n";
$ perl bauer.pl 1 2 48
7
I'd recommend Math::BigFloat and Math::BigInt over the bigint pragma, practically always.
The pragma is "just a thin wrapper around various modules of the Math::BigInt family" says the (linked) doc, but has very non-trivial actions (the "Description" conveys it). Instead, use the classes to set up what you want burdened with infinite precision support, itself non-trivial and costly.
use warnings;
use strict;
use feature 'say';
use Math::BigFloat;
my($g, $y, $n) = ($ARGV[0], $ARGV[1], $ARGV[2]);
my $z = sqrt($n-1);
say $z;
my $num = Math::BigFloat->new( $z );
say $num;
my $num_ceil = $num->bceil();
say $num_ceil;
Update The shown calculation has no need for a big-number exponent
use warnings;
use strict;
use feature 'say';
use POSIX 'ceil';
use Math::BigInt;
my ($g, $y, $n) = #ARGV;
my $z = ceil(sqrt($n-1));
my $bg = Math::BigInt->new($g);
my $e;
for my $u (0 .. $z) {
$e = $bg->copy->bmodpow($u*$rnd, $n);
say "$u: $e";
}
There happens to be a bmodpow method, doing exactly what's needed and being
"far superior" at it. Most arithmetic methods modify their operands, thus copy() is chained in to preserve $bg for the next iteration. See "Modifying and =" bullet under Caveats in docs.
I declare $e outside of the loop to avoid the (copy) constructor running every time in the loop as the variable becomes (is assigned) a BigInt object, returned by the method. (I am not certain that this is needed or that it helps though.)
Whenever I look into $z it evaluates to 6 instead of 7,
use bigint; causes numeric literals to be replaced with Math::BigInt objects. For example,
1
gets replaced with
Math::BigInt->new(1)
Math::BigInt in turn overrides a number of operators when a Math::BigInt object is used as an operand.
As such,
use bigint;
my $z = ceil(sqrt($n-1));
is equivalent to
use Math::BigInt;
my $z = ceil(sqrt($n-Math::BigInt->new(1)));
which is equivalent to
use Math::BigInt;
my $temp = Math::BigInt->new(1); # 1 [BigInt]
$temp->bneg; # -1 [BigInt]
$temp->badd($n); # 40 [BigInt]
$temp->bsqrt(); # 6 [BigInt] <--- XXX
$temp = $temp->numify; # 6 [Primitive]
my $z = ceil($temp); # 6 [Primitive]
So, you are using a Math::BigInt when you don't want to. Don't do that!!! Simply use
# No "use bigint;"!!!
my $z = ceil(sqrt($n-1));
Of course, the algorithm to which you linked actually calls for
# No "use bigint;"!!!
my $z = ceil(sqrt($n));
Because use bigint; can have large effects at a distance, I personally find use bigint; far too magical. I'd much rather use Math::BigInt->new(...) where appropriate rather than having use bigint; convert all my numerical constants into Math::BigInt objects. I'd also rather use Math::BigInt's methods instead of overloaded operators. Far less surprises that way (e.g. such as the loss of big number support when using ceil).
use warnings;
use strict;
use feature qw( say );
use Config qw( %Config );
use Math::BigInt qw( );
use POSIX qw( ceil );
# Each of the arguments is expected to be in [0, 2^32).
# Should use exponentiation by squaring instead of larger number support.
sub pow_m {
my ($base, $exp, $mod) = #_;
my $n = Math::BigInt->new($base);
$n->bpow($exp);
$n->bmod($mod);
return $n->numify();
}
# Each of the arguments is expected to be in [0, 2^32).
# Requires a 64-bit integers or $e might overflow.
sub babystep_giantstep {
my ($g, $h, $mod) = #_;
my $m = ceil(sqrt($mod));
my %table;
my $e = 1;
for my $i (0..$m-1) {
$table{$e} = $i;
$e = ($e * $g) % $mod;
}
my $factor = pow_m($g, $mod-$m-1, $mod);
$e = $h;
for my $i (0..$m-1) {
if (exists($table{$e})) {
return $i*$m + $table{$e};
}
$e = ($e * $factor) % $mod;
}
return undef;
}
{
$Config{uvsize} >= 8
or warn("Results may overflow\n");
my ($g, $h, $mod) = #ARGV;
my $log = babystep_giantstep($g, $h, $mod);
say $log;
my $test = Math::BigInt->new($g);
$test->bpow($log);
$test->bmod($mod);
$test = $test->numify;
say $test == $h ? "ok" : "not ok";
}

In Perl, how can I loop through variable names? [duplicate]

This question already has answers here:
How can I use a variable as a variable name in Perl?
(3 answers)
Closed 8 years ago.
I have N array and i want to print 1st elements of them in single for loop
my code:
#arr1=qw(1..5);
#arr2=qw(6..10);
...
#arrN=qw(...);
for($i=1;$i<=$N;$i++)
{
print "$arr[$i][0]";
}
When you find that you need to know the names of 0 .. N different variables. Its time to consider that you might be doing it wrong.
Arrays = list of 0 .. N values, can be sequential
Hash = list of 0 .. N named values
For your arrays, unless you actually want to be converting to strings, don't use qw() just use the bare ()
See solution below, you need an array of arrays:
#!/usr/bin/perl
use strict;
use warnings;
my $n = 10;
my #nArrays;
#fills the nArrays list with array_refs
for my $num(0 .. $n){
push #nArrays, [($num .. $num+5)];
}
#Print out the arrays, one per row, long way
for my $array (#nArrays){
print $array->[0], "\n";
}
If, contrary to all normal recommendations, you leave use strict; out of your script, you could use:
$N = 3;
#arr1 = (1..5);
#arr2 = (6..10);
#arr3 = (7..12);
#arrs = ("arr1", "arr2", "arr3");
for ($i = 0; $i < $N; $i++)
{
print "$arrs[$i][0]\n";
}
Output:
1
6
7
This is absolutely not recommended, but it does work still (mainly for reasons of backwards compatibility).
With use strict; etc, you can use explicit references:
use strict;
use warnings;
my $N = 3;
my #arr1 = (1..5);
my #arr2 = (6..10);
my #arr3 = (7..12);
my #arrs = (\#arr1, \#arr2, \#arr3);
for (my $i = 0; $i < $N; $i++)
{
print "$arrs[$i][0]\n";
}
Output:
1
6
7
On the whole, though, you would do better with a single array of arrays, perhaps like this:
use strict;
use warnings;
my $N = 3;
my #arr = ( [1..5], [6..10], [7..12] );
for (my $i = 0; $i < $N; $i++)
{
print "$arr[$i][0]\n";
}
Output:
1
6
7
What you have in mind is called a symbolic reference, and generally not a good idea.
If the values of these variables belong together, there is already a data structure that is indexed by an integer: Use an array to hold them:
use strict;
use warnings;
my #arr = (
[ 1 .. 5 ],
[ 6 .. 10 ],
# ...
[ 1_000_000 .. 1_000_005 ],
);
for my $i (0 .. $#arr) {
print $arr[$i][0], "\n";
}

How to produce range with step in Perl

In Bash, seq 5 5 20 produces 5 10 15 20.
In Perl, 1..5 produces 1 2 3 4 5; does it support step?
How do I produce a range with step in Perl?
perldoc -f map is one way:
use warnings;
use strict;
use Data::Dumper;
my #ns = map { 5 * $_ } 1 .. 4;
print Dumper(\#ns);
__END__
$VAR1 = [
5,
10,
15,
20
];
See also: perldoc perlop
The range operator in Perl doesn't support steps. You could use a for loop instead:
for (my $i = 5; $i <= 20; $i += 5) {
print "$i\n";
}
The List::Gen range function does this:
use strict;
use warnings;
use feature 'say';
use List::Gen;
my $range = range 5, 20, 5;
say for #$range; # 5
# 10
# 15
# 20
say while <$range>; # TIMT1WTDI
$range->say; # TAMT2WTDI, v.0.974
say $range->str; # TAMT3WTDI, v.0.974
my $by_fives = <5 .. 20 by 5>;
say while <$by_fives>; #TAMT4WTDI
<5 .. * by 5>->say( 4 ); #TAMT5WTDI
Not as good as toolic's answer:
use warnings;
use strict;
my #ns;
for my $n (1..4) {
push(#ns, $n*5);
}
I wrote Acme::Range::Module a while back as a gag module - hence the Acme:: namespace - but it does do what you want and has tests and is supported. Here's the example code:
use Acme::Globule qw( Range );
foreach (<10..1>) {
print "$_... ";
}
print "Lift-off!\n";
# put down that crack pipe...
sub my_keys(\%) {
my #hash = %{ $_[0] };
return #hash[ glob("0,2..$#hash") ];
}
sub my_values(\%) {
my #hash = %{ $_[0] };
return #hash[ glob("1,3..$#hash") ];
}
Here is an easy solution that utilizes map and the built-in range operator:
sub range {
my ($start, $end, $step) = #_;
$step ||= 1;
return map { $_ * $step } ($start / $step .. $end / $step);
}
Notice the key point here is the map {} block. We simply divide the end
by the given step (works for negative and positive) then map each value
to the multiple of the given step.
Like the map solution :)
Here it is used to look for files starting with even numbers in a range 116 to 648:
perl -e 'foreach (map { 2 * $_ } (116/2) .. (648/2)) { system("ls -l $_*"); } '
Perl is just wonderful for some jobs and making funny one-liners :)

In Perl, how can I iterate over multiple elements of an array?

I have a CSV file that I use split to parse into an array of N items, where N is a multiple of 3.
Is there a way i can do this
foreach my ( $a, $b, $c ) ( #d ) {}
similar to Python?
I addressed this issue in my module List::Gen on CPAN.
use List::Gen qw/by/;
for my $items (by 3 => #list) {
# do something with #$items which will contain 3 element slices of #list
# unlike natatime or other common solutions, the elements in #$items are
# aliased to #list, just like in a normal foreach loop
}
You could also import the mapn function, which is used by List::Gen to implement by:
use List::Gen qw/mapn/;
mapn {
# do something with the slices in #_
} 3 => #list;
You can use List::MoreUtils::natatime. From the docs:
my #x = ('a' .. 'g');
my $it = natatime 3, #x;
while (my #vals = $it->()) {
print "#vals\n";
}
natatime is implemented in XS so you should prefer it for efficiency. Just for illustration purposes, here is how one might implement a three element iterator generator in Perl:
#!/usr/bin/perl
use strict; use warnings;
my #v = ('a' .. 'z' );
my $it = make_3it(\#v);
while ( my #tuple = $it->() ) {
print "#tuple\n";
}
sub make_3it {
my ($arr) = #_;
{
my $lower = 0;
return sub {
return unless $lower < #$arr;
my $upper = $lower + 2;
#$arr > $upper or $upper = $#$arr;
my #ret = #$arr[$lower .. $upper];
$lower = $upper + 1;
return #ret;
}
}
}
my #list = (qw(one two three four five six seven eight nine));
while (my ($m, $n, $o) = splice (#list,0,3)) {
print "$m $n $o\n";
}
this outputs:
one two three
four five six
seven eight nine
#z=(1,2,3,4,5,6,7,8,9,0);
for( #tuple=splice(#z,0,3); #tuple; #tuple=splice(#z,0,3) )
{
print "$tuple[0] $tuple[1] $tuple[2]\n";
}
produces:
1 2 3
4 5 6
7 8 9
0
Not easily. You'd be better off making #d an array of three-element tuples, by pushing the elements onto the array as an array reference:
foreach my $line (<>)
push #d, [ split /,/, $line ];
(Except that you really ought to use one of the CSV modules from CPAN.
As of Perl v5.36 you can do exactly that:
foreach my ( $a, $b, $c ) ( #d ) { ... }
It's implemented as for_list experimental feature, so you can ignore the warning the usual way with use experimental qw(for_list);
For versions before v5.36 we'll rely on while/splice as mentioned above.