代码之家  ›  专栏  ›  技术社区  ›  Chris Lutz

Perl变量范围问题

  •  7
  • Chris Lutz  · 技术社区  · 15 年前

    所以我有一个Perl类。它有一个 sort() 方法,我希望它或多或少与内置的 分类() 功能:

    $object->sort(sub ($$) { $_[0] <=> $_[1] });
    

    但我做不到:

    $object->sort(sub { $a <=> $b });
    

    因为范围界定。但是清单::util模块使用 reduce() . 我查看了清单::util模块,它们使用 no strict 'vars' 为了实现这一点。我试过了,但没用。

    这是我的理解 约化() 因为它被导出到适当的名称空间,所以我的类不能这样做,因为函数在另一个名称空间中非常牢固。这是正确的吗,还是在我的情况下有一些(毫无疑问是更可怕和不明智的)方法可以做到这一点?

    3 回复  |  直到 15 年前
        1
  •  9
  •   Eevee    15 年前

    另外两个答案都是对的。下面是一个实际排序的工作解决方案:

    package Foo;
    
    use strict;
    use warnings;
    
    sub sort {
        my ($self, $sub) = @_;
    
        my ($pkg) = caller;
        my @x = qw(1 6 39 2 5);
        print "@x\n";
        {
            no strict 'refs';
            @x = sort {
                local (${$pkg.'::a'}, ${$pkg.'::b'}) = ($a, $b);
                $sub->();
            } @x;
        }
        print "@x\n";
    
        return;
    }
    
    
    package main;
    
    use strict;
    use warnings;
    
    my $foo = {};
    bless $foo, 'Foo';
    
    $foo->sort(sub { $a <=> $b });
    # 1 6 39 2 5
    # 1 2 5 6 39
    

    大概你会对一些数据进行排序,这些数据实际上是对象的一部分。

    你需要 caller 魔法,所以你要定位 $a $b 在调用者的包中,Perl将在包中查找它们。它正在创建只在调用该Sub时存在的全局变量。

    请注意,您将获得“仅使用一次的名称”和 warnings 我敢肯定,不知怎么的,为了避免这种情况,你可以跳过一些圈。

        2
  •  4
  •   cjm    15 年前

    你可以使用 Sub::Identify 找出包(它调用的 stash_name )与coderef关联。然后根据需要在该包中设置$A和$B。您可能需要使用 no strict 'refs' 用你的方法让它发挥作用。

    下面是Evee的答案,在一般情况下可以使用:

    use strict;
    use warnings;
    
    package Foo;
    
    use Sub::Identify 'stash_name';
    
    sub sort {
        my ($self, $sub) = @_;
    
        my $pkg = stash_name($sub);
        my @x = qw(1 6 39 2 5);
        print "@x\n";
        {
            no strict 'refs';
            @x = sort {
                local (${$pkg.'::a'}, ${$pkg.'::b'}) = ($a, $b);
                $sub->();
            } @x;
        }
        print "@x\n";
    
        return;
    }
    
    
    package Sorter;
    
    sub compare { $a <=> $b }
    
    package main;
    
    use strict;
    use warnings;
    
    my $foo = {};
    bless $foo, 'Foo';
    
    $foo->sort(\&Sorter::compare );
    
    $foo->sort(sub { $b <=> $a });
    
        3
  •  1
  •   Rob Kennedy    15 年前

    你可以使用 the local operator 为设置值 $a $b 在子程序调用期间:

    sub sample
    {
        my $callback = shift;
        for (my $i = 0; $i < @_; $i += 2) {
            local ($a, $b) = @_[$i, $i + 1];
            $callback->();
        }
    }       
    
    sample sub { print "$a $b\n" }, qw(a b c d e f g h i j);
    

    如果您有一个普通的子程序,而不是一个方法,那么您可以使它更像 sort ,所以您不需要使用 sub 在回调函数之前。在函数上使用原型:

    sub sample (&@)
    

    然后你这样称呼它:

    sample { print "$a $b\n" } qw(a b c d e f g h i j);
    

    但是,方法不受原型的影响。