如何本地化两个变量以便后续调用预定义的子例程?

时间:2017-05-24 17:10:44

标签: perl

我试图模仿reduce中的List::Util函数。

我遇到的问题是子例程引用已传递给我的reducer 无法访问词汇$a$b变量。

我试过宣布他们 在reduce子程序中包含mylocal,但都没有效果。

我当前的子程序是这样的,用于测试our $DEBUG = 1; 在文件的顶部:

sub reduce(&@) {
    my $code = shift;

    if ( @_ == 0 ) {
        print "No items passed to reduce, returning undef\n" if $DEBUG;
        return undef;
    }

    if ( @_ == 1 ) {
        print "One item passed to reduce, returning it\n" if $DEBUG;
        return shift;
    }

    my $a = shift;    # Also tried "local $a = shift;"
    my $b = shift;    # Also tried "local $b = shift;"

    print "Starting reduction loop with a = [$a] and b = [$b]\n" if $DEBUG;

    while ( @_ ) {
        $a = $code->();
        $b = shift;
        print "- Reductive iteration ended with a = [$a] and b = [$b]\n" if $DEBUG;
    }

    my $val = &{$code};

    print "- Finished reductive loop with value [$val]\n" if $DEBUG;

    return $val;
}

我称之为

print "" . (reduce { print "-- $a + $b\n"; $a + $b; } 1..10) . "\n";

我的输出是:

Starting reduction loop with a = [0] and b = [1]
--  + 
- Reductive iteration ended with a = [0] and b = [2]
--  + 
- Reductive iteration ended with a = [0] and b = [3]
--  + 
- Reductive iteration ended with a = [0] and b = [4]
--  + 
- Reductive iteration ended with a = [0] and b = [5]
--  + 
- Reductive iteration ended with a = [0] and b = [6]
--  + 
- Reductive iteration ended with a = [0] and b = [7]
--  + 
- Reductive iteration ended with a = [0] and b = [8]
--  + 
- Reductive iteration ended with a = [0] and b = [9]
--  + 
- Reductive iteration ended with a = [0] and b = [10]
--  + 
- Finished reductive loop with value [0]
0

如何将子例程引用传递给reduce以查看$a$b

2 个答案:

答案 0 :(得分:2)

看看another pure perl reduce implementation。关键的见解是,传递给$a的子中的$breduce是调用包命名空间中的全局变量,而在reduce实现中,您执行了一个小符号表巫术来获得它们。我在这里更改了词法变量名称(到$aa$bb),以区别于调用者中的$a$b

sub reduce (&@) {
  my $f = shift;
  ...
  my $pkg = caller;
  my $aa = shift;               # first element in list (after function spec)

  no strict 'refs';
  # makes '$a' in calling package an alias for local '$aa'
  local *{"${pkg}::a"} = \$aa;

  # $glob_b is a reference to stash for `b` in calling package   
  my $glob_b = \*{"${pkg}::b"};

  foreach my $bb (@_) {
    # $glob_b is reference to stash for caller's 'b'
    # *$glob_b is the stash for caller's 'b'
    # assigning scalar reference to *$glob_b updates '$b' in calling pkg
    local *$glob_b = \$bb;

    # $aa is aliased to caller's $a, so $a is updated within this loop
    $aa = $f->();
  }
  $aa;
}

答案 1 :(得分:1)

最好不要猜测如何传递值。如果您在阅读文档后真的不知道该怎么做,那么弄乱mylocal会让您更加困惑。这两个想法都将确保在当前代码块结束时丢弃这些变量,但由于local包变量一起使用,对传递的子例程引用的任何调用都将是能够看到那些临时值

你没有定义调用reduce的真正目的,所以我写了一些东西,它只是连接列表中的所有值并返回结果。请始终提供我们可以测试和修复的内容

这是一个有效的my_reduce。请注意,真实reduce必须检查调用代码的命名空间,并修改 包中的$a$b。在这里,我只是将my_reduce放在与调用代码相同的包中

我还并行使用List::Util::reduce以确保两个结果一致

use strict;
use warnings 'all';
use feature 'say';

use List::Util ();

my $DEBUG = 1;

sub my_reduce(&@);

my @abc = 'a' .. 'z';

say List::Util::reduce(
    sub { $a . $b },
    @abc
);

say my_reduce(
    sub { $a . $b },
    @abc
);

sub my_reduce(&@) {
    my $code = shift;

    if ( @_ == 0 ) {
        print "No items passed to reduce, returning undef\n" if $DEBUG;
        return undef;
    }
    elsif ( @_ == 1 ) {
        print "One item passed to reduce, returning it.\n" if $DEBUG;
        return shift;
    }
    else {
        while ( @_ > 1 ) {
            local ($a, $b) = splice @_, 0, 2;
            unshift @_, $code->();
        }
        return shift;
    }
}

输出

abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyz