10

I would like to use $a and $b variables in my anonimous binary functions like it is done in sort {$a <=> $b} (1, 2, 3) but I can not figure out why code like

#!/usr/bin/env perl
use strict;
use warnings;

Foo::Bar(sub { $a + $b });

package Foo;
sub Bar {
    my ($function) = @_; 

    for my $i (1, 2, 3) {
        local ($a, $b) = ($i, $i);
        print $function->() . "\n";
    }
}    

does not work. While

#!/usr/bin/env perl
use strict;
use warnings;

Foo::Bar(sub { $_ });

package Foo;
sub Bar {
    my ($function) = @_; 

    for my $i (1, 2, 3) {
        local $_ = $i;
        print $function->() . "\n";
    }
}

works fine.

What am I doing wrong?

Egga Hartung
  • 1,061
  • 11
  • 23
alexanderkuk
  • 1,551
  • 2
  • 12
  • 29

2 Answers2

14

$a and $b are special package variables. You're calling Foo::Bar from within your main package, so you need to set $main::a and $main::b to get it to work. You can use caller to get the name of the calling package. This should work:

#!/usr/bin/env perl
use strict;
use warnings;

Foo::Bar(sub { $a + $b });

package Foo;
sub Bar {
    my ($function) = @_; 
    my $pkg = caller;

    for my $i (1, 2, 3) {
        no strict 'refs';
        local *{ $pkg . '::a' } = \$i;
        local *{ $pkg . '::b' } = \$i;
        print $function->() . "\n";
    }
}    
friedo
  • 65,762
  • 16
  • 114
  • 184
  • 1
    IMHO, it's OK if you want to create a `sort`-like function that takes a block; but that function should only return values and not `print` stuff, obviously. This is fine as a demonstration though. – friedo Jan 02 '12 at 18:59
  • 5
    That's true. I suspect the OP doesn't really know what he's doing though, and would be better off with `sub {$_[0] + $_[1]}` and `$function->($i, $i)`. – flesk Jan 02 '12 at 19:05
  • Thank you! I think I will use `$_[i]` =) And `$_` is special variable? – alexanderkuk Jan 02 '12 at 19:57
  • `$_` is special but it's got extra magic on it to make it global everywhere. `$a` and `$b` are slightly less magical. – friedo Jan 02 '12 at 19:59
  • `$_` often is implicitly localized though. – matthias krull Jan 02 '12 at 20:04
  • 3
    Look at the source for [List::MoreUtils](http://p3rl.org/List::MoreUtils) for some examples in the wild – Joel Berger Jan 03 '12 at 03:03
  • In this case, it's actually @_ that you're using. $_[0] is saying "I want scalar element (hence $) number 0 of the array @_". – Joe McMahon Jan 05 '12 at 01:20
  • I would recommend against using the package of `caller` and instead use the package of the actual function instead or you can get into some trouble down the road. See [How to make a function that takes a block which requires $a and $b but is in different package than caller in perl?](https://stackoverflow.com/questions/73515700). – Adrian Aug 29 '22 at 06:17
0

Just in case anyone is interested, a copy-and-paste from List::MoreUtils::PP v.0.428 (as of December 2017):

# begin copyrighted content
sub reduce_u(&@)
{
    my $code = shift;

    # Localise $a, $b
    my ($caller_a, $caller_b) = do
    {
        my $pkg = caller();
        no strict 'refs';
        \*{$pkg . '::a'}, \*{$pkg . '::b'};
    };

    local (*$caller_a, *$caller_b);
    *$caller_a = \();
    for (0 .. $#_)
    {
        *$caller_b = \$_[$_];
        *$caller_a = \($code->());
    }

    ${*$caller_a};
}
# end copyrighted content

It only differs from the sample above in the area affected by no strict 'refs';. Probably can't be done without no strict at all.

Dallaylaen
  • 5,268
  • 20
  • 34
  • I would recommend against using the package of `caller` and instead use the package of the actual function instead or you can get into some trouble down the road. See [How to make a function that takes a block which requires $a and $b but is in different package than caller in perl?](https://stackoverflow.com/questions/73515700). – Adrian Aug 29 '22 at 06:19