Routine as argument -- generic variables not working - perl

I am working on writing a gaming system (wargames, etc.) and am creating the system for creating and displaying hex maps. I realized quickly that I am repeatedly doing a nested loop of x=(0..maxx) and y=(0..maxy). So I attempted to adapt some code I found somewhere (one of the advanced perl books, I forget where) to create an easier way to do this sort of looping thing. This is what I came up with:
sub fillmap (&#) {
my $code = shift;
no strict 'refs';
use vars qw($x $y);
my $caller = caller;
local(*{$caller."::x"}) = \my $x;
local(*{$caller."::y"}) = \my $y;
foreach $x (0..5) {
foreach $y (0..3) {
warn "fillmap $x,$y\n";
&{$code}($x,$y);
}
}
}
It's suppose to work like sort, but using $x and $y instead of $a and $b.
Note: the warn statement is for debugging. I also simplified the x and y ranges (the array passed in determines the maxx and maxy values, but I didn't want to muddy this discussion with the routines for calculating them... I just hard-coded them to maxx=5 and maxy=3)
So, this execution of this routine like so:
fillmap {warn "$x,$y\n";} #map;
should yield a list of the x,y pairs. But instead, it gives me this:
fillmap 0,0
,
fillmap 0,1
,
fillmap 0,2
,
fillmap 0,3
,
fillmap 1,0
,
...
Note, the "fillmap" lines are from the subroutine for debugging. But instead of each x,y pair, I just get the comma ($x and $y are undefined).
What am I doing wrong?

The problem is that for $x does its own localisation. The $x inside the loop isn't the $x that's aliased to $caller::x.
You need to do one of the following:
Copy $x into $caller::x inside the loop.
Alias $caller::x to $x inside the loop.
The following does the latter:
use strict;
use warnings;
sub fillmap(&#) {
my $code = shift;
my $caller = caller();
my $xp = do { no strict 'refs'; \*{$caller.'::x'} }; local *$xp;
my $yp = do { no strict 'refs'; \*{$caller.'::y'} }; local *$yp;
for my $x (0..1) {
*$xp = \$x;
for my $y (0..2) {
*$yp = \$y;
$code->();
}
}
}
our ($x, $y);
fillmap { warn "$x,$y\n"; } '...';
You could avoid the need for our ($x, $y); by using $a and $b instead of $x and $y. You can't solve the problem by moving it (or use vars qw( $x $y );) into fillmap because you obviously intend fillmap to be used in a different package and lexical scope than the caller.

Related

Confusion about using perl #_ variable with regex capture

When I using following code, subroutine f can't print #_ correctly for the substitution of $tmp before it, which is explainable.
use strict;
use warnings;
sub f {
# print("args = ", #_);
my $tmp = "111";
$tmp =~ s/\d+//;
print("args = ", #_);
}
"dd11ddd" =~ /(?<var>\d+)/;
f($+{"var"});
But when I uncomment the first print statement, then both print could give the correct #_, which makes me confused, why the capture group hasn't been overwrite. Or just some underlay mechanism of perl I don't know? Please help, thanks.
When I pass capture group into perl subroutine, the capture group hasn't been overwritten as expected.
I want to know why this could happen and how to explain it correctly.
Perl arguments are passed by reference.
sub f {
$_[0] = "def";
}
my $x = "abc;
say $x; # abc
f( $x );
say $x; # def
%+ is affected by $tmp =~ s/\d+//, and thus so is $_[0]. We don't usually run into problems because we usually make an explicit copy of the arguments.
sub f {
my $y = shift;
$y = "def";
}
my $x = "abc";
say $x; # abc
f( $x );
say $x; # abc
Passing a copy of the scalar would also avoid the problems.
sub f {
$_[0] = "def";
}
my $x = "abc";
say $x; # abc
f( "$x" );
say $x; # abc
The above explains why you get weird behaviour and how to avoid it, but not why accessing $_[0] before the substitution seems to fix it. Honestly, it doesn't really matter. It's some weird interaction between the magical nature of %+, the implicit localization of %+, the optimizations to avoid needless implication localizations of %+, and the way localization works.

What do dollar, at-sign and semicolon characters in Perl parameter lists mean?

I have encountered a number of Perl scripts in the codebase at my job. Some of them contain subroutines with the following syntax oddity:
sub sum($$$) {
my($a,$b,$m)=#_;
for my $i (0..$m) {
$$a[$i] += $$b[$i] if $$b[$i] > 0;
}
}
sub gNode($$;$$) {
my($n,$l,$s,$d) = #_;
return (
"Node name='$n' label='$l' descr='$d'" ,
$s ? ("Shape type='$s' /") : (),
'/Node'
);
}
sub gOut($$#) {
my $h = shift;
my $i = shift;
if ($i > 0) {
print $h (('')x$i, map '<'.$_.'>', #_);
} else {
print $h map '<'.$_.'>', #_;
}
}
Leaving aside the question of what these subroutines are meant to do (I'm not entirely sure myself...), what do the sequences of characters in the 'parameter list' position mean? Viz. the $$$, $$;$$ and $$# sequences in these examples.
I have a very limited understanding of Perl, but I believe that the my($a,$b,$m)=#_; line in the first example (sum) unpacks the parameters passed to the subroutine into the $a, $b and $m local variables. This suggests that the $$$ indicates the arity and type signature of sum (it expects three scalars, in this case). This would potentially suggest that gOut expects two scalars and an array. Is this the correct interpretation?
Even if the above interpretation is correct, I'm lost as to the meaning of the semicolon in the second routine (gNode).
See perldoc perlsub entry on Prototypes.
# Declared as Called as
sub mylink ($$) mylink $old, $new
sub myvec ($$$) myvec $var, $offset, 1
sub myindex ($$;$) myindex &getstring, "substr"
sub mysyswrite ($$$;$) mysyswrite $buf, 0, length($buf) - $off, $off
sub myreverse (#) myreverse $a, $b, $c
sub myjoin ($#) myjoin ":", $a, $b, $c
sub mypop (+) mypop #array
sub mysplice (+$$#) mysplice #array, 0, 2, #pushme
sub mykeys (+) mykeys %{$hashref}
sub myopen (*;$) myopen HANDLE, $name
sub mypipe (**) mypipe READHANDLE, WRITEHANDLE
sub mygrep (&#) mygrep { /foo/ } $a, $b, $c
sub myrand (;$) myrand 42
sub mytime () mytime
Don't forget: This is all very powerful, of course, and should be used only in moderation to make the world a better place.
I agree with the rest: don't use sub prototypes unless you know what you're doing. "With great power comes great responsibility." Those look like they were created by someone used to C prototypes. For example, the sub sum really should have this prototype:
sub sum (\$\$\$) {

Why is my for loop an illegal declaration

I created two subs one to do Fibonacci and the other to test even numbers. When I call it though it is saying my for loop in line 7 the sub Fibonacci is illegal why?
#!/usr/bin/perl
use strict;
use warnings;
my ($x,$y);
my $num = 0;
sub Fibs($start,$stop){
for ($start..$stop){
($x, $y) = ($y, $x+$y);
my $total += $y;
}
print "$total \n"
}
sub even($num){
if ($num % 2 == 0){
return $num;}
}
my $big_total = Fibs(even($num), 3999999)
Edited from suggestions below.
Clearly I am missing something. From feedback updated to new version.
#!/usr/bin/perl
use strict;
use warnings;
my ($x,$y);
my $num = 0;
sub Fibs{
my ($start, $stop) = #_ ;
for ($start..$stop){
my ($x, $y) = (0,2);
if ($x % 2 == 0){
($x, $y) = ($y, $x+$y);
my $total += $y;
}
}
my $big_total = Fibs(0, 3999999)
In addition to the missing opening braces, Perl doesn't support that kind of declaration for subroutine parameters.
Rather than
sub Fibs($start, $stop) {
...
}
you need to write something like:
sub Fibs {
my($start, $stop) = #_;
...
}
(Perl does have prototypes, but they're not really intended for declaring the types of parameters, and they don't provide names. See this article for a discussion.)
Other problems:
You should add
use strict;
use warnings;
You never use the $x and $y that you declare in the outer scope.
Your even function appears to be incomplete. It doesn't (explicitly) return a value if its argument is an odd number. What exactly is it intended to do?

How is the map function in Perl implemented?

Is map function in Perl written in Perl? I just can not figure out how to implement it. Here is my attempt:
use Data::Dumper;
sub Map {
my ($function, $sequence) = #_;
my #result;
foreach my $item (#$sequence) {
my $_ = $item;
push #result, $function->($item);
}
return #result
}
my #sample = qw(1 2 3 4 5);
print Dumper Map(sub { $_ * $_ }, \#sample);
print Dumper map({ $_ * $_ } #sample);
$_ in $function is undefined as it should be, but how map overcomes this?
map has some special syntax, so you can't entirely implement it in pure-perl, but this would come pretty close to it (as long as you're using the block form of map):
sub Map(&#) {
my ($function, #sequence) = #_;
my #result;
foreach my $item (#sequence) {
local $_ = $item;
push #result, $function->($item);
}
return #result
}
use Data::Dumper;
my #sample = qw(1 2 3 4 5);
print Dumper Map { $_ * $_ } #sample;
print Dumper map { $_ * $_ } #sample;
$_ being undefined is overcome by using local $_ instead of my $_. Actually you almost never want to use my $_ (even though you do want to use it on almost all other variables).
Adding the (&#) prototype allows you not to specify sub in front of the block. Again, you almost never want to use prototypes but this is a valid use of them.
While the accepted answer implements a map-like function, it does NOT do it in the way perl would. An important part of for, foreach, map, and grep is that the $_ they provide to you is always an alias to the values in the argument list. This means that calling something like s/a/b/ in any of those constructs will modify the elements they were called with. This allows you to write things like:
my ($x, $y) = qw(foo bar);
$_ .= '!' for $x, $y;
say "$x $y"; # foo! bar!
map {s/$/!!!/} $x, $y;
say "$x $y"; # foo!!!! bar!!!!
Since in your question, you have asked for Map to use array references rather than arrays, here is a version that works on array refs that is as close to the builtin map as you can get in pure Perl.
use 5.010;
use warnings;
use strict;
sub Map (&\#) {
my ($code, $array) = splice #_;
my #return;
push #return, &$code for #$array;
#return
}
my #sample = qw(1 2 3 4 5);
say join ', ' => Map { $_ * $_ } #sample; # 1, 4, 9, 16, 25
say join ', ' => map { $_ * $_ } #sample; # 1, 4, 9, 16, 25
In Map, the (&\#) prototype tells perl that the Map bareword will be parsed with different rules than a usual subroutine. The & indicates that the first argument will either be a bare block Map {...} NEXT or it will be a literal code reference Map \&somesub, NEXT. Note the comma between the arguments in the latter version. The \# prototype indicates that the next argument will start with # and will be passed in as an array reference.
Finally, the splice #_ line empties #_ rather than just copying the values out. This is so that the &$code line will see an empty #_ rather than the args Map received. The reason for &$code is that it is the fastest way to call a subroutine, and is as close to the multicall calling style that map uses as you can get without using C. This calling style is perfectly suited for this usage, since the argument to the block is in $_, which does not require any stack manipulation.
In the code above, I cheat a little bit and let for do the work of localizing $_. This is good for performance, but to see how it works, here is that line rewritten:
for my $i (0 .. $#$array) { # for each index
local *_ = \$$array[$i]; # install alias into $_
push #return, &$code;
}
My Object::Iterate module is an example of what you are trying to do.

Is there some way to make variables like $a and $b in regard to strict?

In light of Michael Carman's comment, I have decided to rewrite the question. Note that 11 comments appear before this edit, and give credence to Michael's observation that I did not write the question in a way that made it clear what I was asking.
Question: What is the standard--or cleanest way--to fake the special status that $a and $b have in regard to strict by simply importing a module?
First of all some setup. The following works:
#!/bin/perl
use strict;
print "\$a=$a\n";
print "\$b=$b\n";
If I add one more line:
print "\$c=$c\n";
I get an error at compile time, which means that none of my dazzling print code gets to run.
If I comment out use strict; it runs fine. Outside of strictures, $a and $b are mainly special in that sort passes the two values to be compared with those names.
my #reverse_order = sort { $b <=> $a } #unsorted;
Thus the main functional difference about $a and $b--even though Perl "knows their names"--is that you'd better know this when you sort, or use some of the functions in List::Util.
It's only when you use strict, that $a and $b become special variables in a whole new way. They are the only variables that strict will pass over without complaining that they are not declared.
: Now, I like strict, but it strikes me that if TIMTOWTDI (There is more than one way to do it) is Rule #1 in Perl, this is not very TIMTOWDI. It says that $a and $b are special and that's it. If you want to use variables you don't have to declare $a and $b are your guys. If you want to have three variables by adding $c, suddenly there's a whole other way to do it.
Nevermind that in manipulating hashes $k and $v might make more sense:
my %starts_upper_1_to_25
= skim { $k =~ m/^\p{IsUpper}/ && ( 1 <= $v && $v <= 25 ) } %my_hash
;`
Now, I use and I like strict. But I just want $k and $v to be visible to skim for the most compact syntax. And I'd like it to be visible simply by
use Hash::Helper qw<skim>;
I'm not asking this question to know how to black-magic it. My "answer" below, should let you know that I know enough Perl to be dangerous. I'm asking if there is a way to make strict accept other variables, or what is the cleanest solution. The answer could well be no. If that's the case, it simply does not seem very TIMTOWTDI.
Others mentioned how to 'use vars' and 'our' - I just wanted to add that $a and $b are special cases, since they're used internally by the sort routines. Here's the note from the strict.pm docs:
Because of their special use by sort(), the variables $a and $b are
exempted from this check.
$a and $b are special because they're a part of the core language. While I can see why you might say that the inability to create similarly-special variables of your own is anti-TIMTOWTDI, I would say that it's no more so than the inability to create new basic commands on the order of 'print' or 'sort'. (You can define subs in modules, but that doesn't make them true keywords. It's the equivalent of using 'our $k', which you seem to be saying doesn't make $k enough like $a for you.)
For pushing names into someone else's namespace, this should be a working example of Exporter:
package SpecialK;
use strict;
use base 'Exporter';
BEGIN {
our #EXPORT = qw( $k );
}
our $k;
1;
Save this to SpecialK.pm and 'use SpecialK' should then make $k available to you. Note that only 'our' variables can be exported, not 'my'.
If I understand correctly, what you want is:
use vars qw($a $b); # Pre-5.6
or
our ($a, $b); # 5.6 +
You can read about it here.
If I'm understanding your question you want to write a module that declares variables in the user's namespace (so they don't have to) and which get localized automatically in callbacks. Is that right?
You can do this by declaring globals and exporting them. (Though do note that it's generally considered bad form to export things without being asked to.)
package Foo;
use strict;
use warnings;
require Exporter;
our #ISA = qw(Exporter);
our #EXPORT = qw(*k *v hashmap);
our ($k, $v);
sub hashmap(&\%) {
my $code = shift;
my $hash = shift;
while (local ($k, $v) = each %$hash) {
$code->();
}
}
Note: The export is of *k and *v, not $k and $v. If you don't export the entire typeglob the local in hashmap won't work correctly from the user's package. A side effect of this is that all of the various forms of k and v (%k, #v, etc.) get declared and aliased. For a full explanation of this, see Symbol Tables in perlmod.
Then in your script:
use Foo; # exports $k and $v
my %h = (a => 1, b => 2, c => 3);
hashmap { print "$k => $v\n" } %h;
__END__
c => 3
a => 1
b => 2
In Perl 5.6 and later, you can use our:
our ($k, $v);
Or you can stick with the older "use vars":
use vars qw($k $v);
Or you might just stick with "my", e.g.:
my %hash;
my ($k,$v);
while (<>) {
/^KEY=(.*)/ and $k = $1 and next;
/^VALUE=(.*)/ and $v = $1;
$hash{$k} = $v;
print "$k $v\n";
}
__END__
KEY=a
VALUE=1
KEY=b
VALUE=2
Making a global $v is not really necessary in the example above, but hopefully you get the idea ($k on the other hand needs to be scoped outside the while block).
Alternatively, you can use fully qualified variable names:
$main::k="foo";
$main::v="bar";
%main::hash{$k}=$v;
$a and $b are just global variables. You can achieve similar effects by simply declaring $k and $v:
use strict;
our ($k, $v);
(In this case $k and $v are not global variables, but lexically scoped aliases for package variables. But if you don't cross the boundaries it's similarly enough.)
It sounds like you want to do the sort of magic that List::MoreUtils does:
use strict;
my #a = (1, 2);
my #b = (3, 4);
my #x = pairwise { $a + $b } #a, #b;
I'd suggest just looking at the pairwise sub in the List::MoreUtils source. It uses some clever symbol table fiddling to inject $a and $b into the caller's namespace and then localize them to just within the sub body. I think.
This worked for me:
package Special;
use base qw<Exporter>;
# use staging; -> commented out, my module for development
our $c;
our #EXPORT = qw<manip_c>;
sub import {
*{caller().'::c'} = *c;
my $import_sub = Exporter->can( 'import' );
goto &$import_sub;
}
And it passes $c through strict, too.
package main;
use feature 'say';
use strict;
use Special;
use strict;
say "In main: \$c=$c";
manip_c( 'f', sub {
say "In anon sub: \$c=$c\n"; # In anon sub: $c=f
});
say "In main: \$c=$c";
Yeah, it's kind of dumb that I bracketed my modules with "use strict", but I don't know the internals, and that takes care of potential sequencing issues.
I'm not sure if anyone's clarified this, but strict does not whitelist $a and $b just because they are really convenient variable names for you to use in your own routines. $a and $b have special meaning for the sort operator. This is good from the point of view within such a sort routine, but kind of bad design from outside. :) You shouldn't be using $a and $b in other contexts, if you are.
$a and $b aren't normal variables, though, and can't be easily replicated by either lexical declarations or explicit exports or messing about with the symbol table. For instance, using the debugger as a shell:
DB<1> #foo = sort { $b cmp $a } qw(foo bar baz wibble);
DB<2> x #foo
0 'wibble'
1 'foo'
2 'baz'
3 'bar'
DB<3> x $a
0 undef
DB<4> x $b
0 undef
$a and $b only exist within the block passed to sort(), don't exist afterwards, and have scope in such a way that any further calls to sort don't tread on them.
To replicate that, you probably need to start messing about with source filters, to turn your preferred notation
my %starts_upper_1_to_25
= skim { $k =~ m/^\p{IsUpper}/ && ( 1 <= $v && $v <= 25 ) } %my_hash
;
into effectively
my %starts_upper_1_to_25
= map { my $k = $_; my $v = $my_hash{$v};
$k =~ m/^\p{IsUpper}/ && ( 1 <= $v && $v <=> 25 ) } keys %my_hash
;
$a and $b are as special as $_ and #_, and while there's no easy way to change those names in Perl 5, Perl 6 does indeed fix this, with the given keyword. "given" is a rubbish term to search on, but http://dev.perl.org/perl6/doc/design/syn/S03.html may be a good place to start.
Is this what your after?.....
use strict;
use warnings;
use feature qw/say/;
sub hash_baz (&#) {
my $code = shift;
my $caller = caller;
my %hash = ();
use vars qw($k $v);
no strict 'refs';
local *{ $caller . '::k' } = \my $k;
local *{ $caller . '::v' } = \my $v;
while ( #_ ) {
$k = shift;
$v = shift;
$hash{ $k } = $code->() || $v;
}
return %hash;
}
my %hash = (
blue_cat => 'blue',
purple_dog => 'purple',
ginger_cat => 'ginger',
purple_cat => 'purple' );
my %new_hash = hash_baz { uc $v if $k =~ m/purple/ } %hash;
say "#{[ %new_hash ]}";
# => purple_dog PURPLE ginger_cat ginger purple_cat PURPLE blue_cat blue
The modules suggested that use export are really no different from use vars.
But the use vars would need to be done in each package that used the $a-like variable.
And our() would need to be done in each outer scope.
Note that you can avoid using $a and $b even for sort by using a $$-prototyped sub:
sub lccmp($$) { lc($_[0]) cmp lc($_[1]) }
print join ' ', sort lccmp
qw/I met this guy and he looked like he might have been a hat-check clerk/;
This is essential when using a compare routine in a different package than the sort call.
EDIT - this is actually incorrect, see the comments. Leaving it here to give other people a chance to learn from my mistake :)
Oh, you're asking if there's a way for a module to declare $k and $v in the CALLER's namespace? You can use Exporter to push up your variables to the caller:
use strict;
package Test;
use Exporter;
my #ISA = qw/Exporter/;
my $c = 3;
my #EXPORT = qw/$c/;
package main;
print $c;