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

625 Views Asked by At

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.

13

There are 13 best solutions below

0
Michael Carman On BEST ANSWER

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
0
moritz On

$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.)

4
dsm On

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.

4
zigdon On

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.
0
runrig On

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;
4
zigdon On

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;
1
Dave Sherohman On

$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'.

2
Eevee On

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.

1
Axeman On

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.

0
draegtun On

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
0
skiphoppy On

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.

1
Sam Kington On

$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.

0
ysth On

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.