How to use $a and $b in Perl subroutine
Asked Answered
I

2

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?

Incredulity answered 2/1, 2012 at 18:19 Comment(0)
P
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";
    }
}    
Pulling answered 2/1, 2012 at 18:42 Comment(8)
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.Pulling
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).Heredity
Thank you! I think I will use $_[i] =) And $_ is special variable?Incredulity
$_ is special but it's got extra magic on it to make it global everywhere. $a and $b are slightly less magical.Pulling
$_ often is implicitly localized though.Stockroom
Look at the source for List::MoreUtils for some examples in the wildTertias
In this case, it's actually @_ that you're using. $_[0] is saying "I want scalar element (hence $) number 0 of the array @_".Treasurehouse
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?.Lalittah
S
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.

Shanel answered 25/12, 2017 at 9:22 Comment(1)
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?.Lalittah

© 2022 - 2024 — McMap. All rights reserved.