参考Perl运算符

时间:2016-11-23 17:30:26

标签: perl reference

在Perl中,是否可以对运算符(例如eqgt)进行子引用?或者它是不可能的,因为它们不是潜艇?我希望避免将运算符重新定义为需要比较函数的subs。

我应用检查功能来配置值,其中检查器取决于配置键。一个这样的检查器是"与另一个密钥的值进行比较"。比较函数可以通过匿名子定义,或者命名子可以为每种比较类型实现,但我认为能够说compare_to("OTHER_KEY", \&eq)更好。

编辑:由于一位评论员想要上下文而其他人投票结束,因为太过宽泛",我绝对清楚问题不是&#34 ;如何编写配置检查器"。这只是关于参考运营商:是否可能,有什么警告适用?

2 个答案:

答案 0 :(得分:4)

使用"abc"创建字符串。

使用sub { $_[0] eq $_[1] }或类似eval('$_[0] eq $_[1]')创建运算符。

这些不会复制运营商;他们创造了它。

在评论中,您一直坚持要求问题是否可以在不创建操作符的情况下获取对操作符的引用("在代码中包装它)。显然,答案是否定的。

另一方面,问题是询问是否有可能获得对运营商的引用,或者更确切地说是eq。{/ p>

可以使用\&CORE::op_name [1] 来引用某些运算符,但不能使用eq。使用该语法自动生成子语言仅支持其语法可以通过带有原型 [2] 的子近似的运算符。

当然,你可以自己轻松地创建一个可调用的运算符实例,而不是让Perl为你创建它:

my $eq = sub { $_[0] eq $_[1] };

compare("OTHER_KEY", $eq)    

sub eq { $_[0] eq $_[1] }

compare("OTHER_KEY", \&eq)

如果你想避免代码重复(有一堆类似的子声明),你可以使用eval

eval("sub $_ { \$_[0] $_ \$_[1] }")
   for qw( eq ne gt lt );

compare("OTHER_KEY", \&eq)

你可以简单地使用以下内容,但这将是非常浪费和危险的:

sub compare {
   ref($_[1])
      ? $_[1]->($val, $_[0])
      : eval("\$val $_[1] \$_[0]")
}

compare("OTHER_KEY", "eq")`

如果您不想涉及任何潜艇,唯一的选择是:

sub compare {
   ref($_[1]) ? $_[1]->($val, $_[0]) :
   $_[1] eq "eq" ? $val eq $_[0] :
   $_[1] eq "ne" ? $val ne $_[0] :
   $_[1] eq "lt" ? $val lt $_[0] :
   $_[1] eq "gt" ? $val gt $_[0] :
   die "Bad argument";
}
  1. 自5.16起。

  2. prototype("CORE::op_name")返回定义值的那些。对于某些命名列表运算符(例如length)和一些命名的一元运算符(例如time)都是如此,但对于任何已命名的二元运算符(例如eq)都不是这样。

答案 1 :(得分:4)

eqgt之类的比较属于CORE函数的特殊类别,您无法在其中创建对它们的引用。这在http://perldoc.perl.org/CORE.html

的文档中有详细说明

所以看起来你不能做你想做的事。我认为你的选择包括:

  • 使用匿名subs或命名subs来包装要使用的比较器
  • 传入字符串'eq'并在if中执行compare_to并在那里调用比较器。
  • 您可以执行其他语言所做的操作,并将第二个参数设为1,0或-1,并将其设为gteqlt,然后如果value是一个coderef,调用它。
  • 如果您有兴趣沿着这条路走下去,可能会有一种伪造的神奇方法来使用AUTOLOAD来解决这个问题。

<强>更新

AUTOLOAD魔法的例子:

use feature qw( say );

my %bin_ops = map { $_ => 1 } qw( eq ne gt lt );
sub AUTOLOAD {
    $AUTOLOAD =~ s/^.*:://;
    die "..." if !$bin_ops{$AUTOLOAD};
    return eval "\$_[0] $AUTOLOAD \$_[1]";
}

sub compare_to {
    my ($sub, $a, $b) = @_;
    return $sub->($a, $b);
}

say compare_to(\&eq, 1, 1) ? "yep" : "nope";
say compare_to(\&eq, 1, 0) ? "yep" : "nope";
say compare_to(\&gt, 1, 0) ? "yep" : "nope";
say compare_to(\&lt, 1, 0) ? "yep" : "nope";

运行它:

> perl tmp/test_coreref.pl
yep
nope
yep
nope

可能有一种方法可以在不使用eval的情况下做到这一点,但我暂时还不打算继续这样做。