【问题标题】:Dynamically choose equality operator in Perl在 Perl 中动态选择相等运算符
【发布时间】:2017-02-11 18:47:19
【问题描述】:

我有一个混合类型(字符串和数字)的值列表:

my @list = (123, 'foo', 34.5, 'bar', '67baz');

对于该列表中的每个项目,我需要与同样随机类型(字符串或数字)的输入进行比较以执行某些操作:

my $input = 345;
foreach my $elem (@list) {
    if ($input == $elem) {
        # do something
    } else {
        # do something else
    }
}

比较 (==) 将在两个操作数的类型相同(本例中为数字)时正常工作,但当类型不匹配时会引发警告和/或产生错误结果(对于此代码sn-p,它可以是字符串与数字,或者两者都可以是字符串,因此需要使用eq

我想就您解决此比较问题的首选方法获得一些意见。或者,如果有一个模块可以根据操作数的类型动态选择(并执行)正确的比较类型。

我知道使用 Scalar::Util 中的 looks_like_number() 等方法来检查您正在使用的值类型,但我认为也许有更好的方法来确定如何进行比较。提前致谢。

【问题讨论】:

  • 您是否需要345 来匹配345.03.45e2?如果没有,那么您应该一直使用eq

标签: perl comparison operators


【解决方案1】:

在许多语言中,操作数的类型决定了操作。在 Perl 中,操作符确定操作,操作数将被强制转换为正确的类型。因此,Perl 不会考虑将四个存储为字符串,而不是将四个存储为整数。他们都是四个。而且,如果您的代码试图确定标量是数字还是字符串,则代码有问题[1]

如果你坚持,那么你需要想出一个认为是数字而不是字符串的定义。如果您选择“如果 Perl 可以将其用作数字,则它是一个数字,否则为字符串”,那么 looks_like_number 确实会成功。您也可以选择根据值在标量中的存储方式来决定,但是当标量在[2] 中同时包含整数和字符串时,您必须决定要做什么。 p>

另一方面,如果您正在尝试编写通用代码,那么您可以让您的函数接受比较函数(就像 sort 一样)。

sub find(&@) {
   my $cb = shift;
   for (@_) {
      return 1 if $cb->();
   }

   return 0;
}


if (find { $_ == $num_to_find } @nums) {
   print("Found $num_to_find\n");
} else {
   print("Didn't find $num_to_find\n");
}

find { $_ eq $string_to_find } @strings

find { $_->year == $year_to_find } @date_time_objects;

  1. 这就是为什么 smartmatch 运算符被认为是有问题的,需要在它停止实验之前进行更改。

  2. perl -le'open(my $fh, "<", "non-existent"); print 0+$!; print "".$!;'

【讨论】:

    【解决方案2】:

    您可以使用Sort::Naturally,它导入一个进行“自然”比较的ncmp 运算符。

    要检查是否相等,您可以这样做

    use Sort::Naturally;
    if ( ncmp( $lval, $rval ) == 0 ) { ... }
    

    以下是ncmp 如何处理某些配对的一些示例。

    ncmp(1, '1')   ==  0
    ncmp('a', 'a') ==  0
    ncmp('a', 'b') == -1
    ncmp('a', 'A') == -1
    ncmp(2, 1)     ==  1
    ncmp(1, 'a')   == -1
    ncmp('a', 1)   ==  1
    

    基本上...

    0 相等
    1 表示lval 小于rval
    -1 表示lval 大于rval

    【讨论】:

      【解决方案3】:

      使用 perl eval 字符串,它是你程序中的一种迷你 perl 程序(你可以在脚本语言中轻松地做到这一点,Perl eval 比周围的所有东西都强大得多,但强大的力量是伟大的责任...):

          # get the var guiding the dynamic operator
          my $issues_order_by_attribute= $ENV{ 'issues_order_by_attribute' } || 'prio' ;
          my $operator = '<=>' ;   # initialize a default operator
          # use Scalar::Util looks_like_number method to decide for operator 
          $operator = 'cmp'  unless ( looks_like_number( $issues_order_by_attribute ) ) ;
          # use eval string within the code to interpolate the operator
          # to sort the hash ref of hash refs by the set attribute to order by
          foreach my $issue_id (
              eval 'sort {      ' .
                  '$hsr2->{$a}->{ $issues_order_by_attribute }' . $operator . '$hsr2->{$b}->{ $issues_order_by_attribute }' .
                  '} keys (%$hsr2)' )  {
              # Action !!! 
              my $row = $hsr2->{ $issue_id } ;
          }
      

      【讨论】:

        【解决方案4】:

        smartmatch operator 会让你写信

        no if $] >= 5.018, warnings => qw( experimental::smartmatch );
        
        my $input = 123;  # also works with 34.5, 'foo', etc.
        foreach my $elem (@list) {
            if ($elem ~~ $input) {
                # do something
            } else {
                # do something else
            }
        }
        

        这个想法取自 Perl 6,并在 5.10 中实现。自 Perl 5.18 以来,一直在讨论如何改进该功能以更好地适应 Perl 5,因此界面已降级为“实验”状态等待更改。

        【讨论】:

        • 那相当具有误导性。他们不是想把它进化成更好的东西。这是错误的,他们正在尝试修复它。更糟糕的是,您专门将它用于需要修复的行为。如果不首先破坏您的代码,它将不会离开实验状态。
        猜你喜欢
        • 2014-03-12
        • 1970-01-01
        • 2015-06-05
        • 1970-01-01
        • 1970-01-01
        • 2016-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多