【问题标题】:When is the spaceship operator used outside a sort?宇宙飞船运算符何时在排序之外使用?
【发布时间】:2010-11-25 03:00:14
【问题描述】:

我只见过数字排序例程中使用的 Perl 宇宙飞船运算符 ()。但它在其他情况下似乎很有用。我只是想不出实际用途。

什么情况下可以在 Perl 排序之外使用它?

这是一个最佳实践问题。

【问题讨论】:

  • @ether:只有“操作员”标签?我以为你喜欢创建新的运算符标签!

标签: perl sorting operators spaceship-operator


【解决方案1】:

我正在为机器人 Joe 编写一个控制系统,它想去机器人 Mary 并给她充电。它们沿着直线上的整数点移动。 Joe 从 $j 开始,每个时间单位可以向任何方向走 1 米。玛丽站在 10 美元处,不能动弹——她需要好好充电!控制程序如下所示:

while ($m != $j) {
    $j += ($m <=> $j);
}

【讨论】:

    【解决方案2】:

    &lt;=&gt; 运算符对binary search algorithm 很有用。大多数编程语言没有进行三向比较的运算符,这使得每次迭代都需要进行两次比较。使用&lt;=&gt;,您可以只做一个。

    sub binary_search {
        my $value = shift;
        my $array = shift;
        my $low   = 0;
        my $high  = $#$array;
    
        while ($low <= $high) {
            my $mid = $low + int(($high - $low) / 2);
    
            given ($array->[$mid] <=> $value) {
                when (-1) { $low  = $mid + 1 }
                when ( 1) { $high = $mid - 1 }
                when ( 0) { return $mid      }
            }
        }
    
        return;
    }
    

    【讨论】:

    • 考虑到正确实现二分搜索是多么困难,我只能希望我还没有向世界贡献另一个破例。 :P
    • 将这两个比较从明确编写的语句委托给预处理器内部有什么性能优势?
    • 在字节码级别有一个运算符,而不是两个:perl -MO=Terse -e "$a &lt;=&gt; $b" 也就是说,由于 Perl 是用 C 实现的(它没有三向比较运算符),所以 &lt;=&gt; 必须是内部实现了两个比较。任何性能优势都在于扩展发生在 C 而不是 Perl 中。当然,最终限制是处理器的指令集支持什么,不管 C 有什么操作符。
    【解决方案3】:

    在任何一种比较方法中。例如,你可能有一个复杂的对象,但它仍然有一个定义的“顺序”,所以你可以为它定义一个比较函数(你没有必须在排序方法中使用它,虽然它会很方便):

    package Foo;
    
    # ... other stuff...
    
    # Note: this is a class function, not a method
    sub cmp
    {
        my $object1 = shift;
        my $object2 = shift;
    
        my $compare1 = sprintf("%04d%04d%04d", $object1->{field1}, $object1->{field2}, $object1->{field3});
        my $compare2 = sprintf("%04d%04d%04d", $object2->{field1}, $object2->{field2}, $object2->{field3});
        return $compare1 <=> $compare2;
    }
    

    这当然是一个完全人为的例子。但是,在我公司的源代码中,我发现几乎与上述内容完全相同,用于比较用于保存日期和时间信息的对象。

    我能想到的另一个用途是用于统计分析——如果一个值反复运行在一个值列表中,您可以判断该值是高于还是低于该集合的算术中位数:

    use List::Util qw(sum);
    # $result will be
    #   -1 if value is lower than the median of @setOfValues,
    #    1 if value is higher than the median of @setOfValues,
    #    0 if value is equal to the median
    my $result = sum(map { $value <=> $_ } @setOfValues);
    

    这里还有一个,来自Wikipedia:“如果两个参数不能比较(例如其中一个是NaN),则运算符返回undef。”,即您可以一次确定两个数字是否为一个数字,虽然我个人会选择不那么神秘的Scalar::Util::looks_like_number。

    【讨论】:

    • 实际上,undef 行为可能会导致问题,因为undef 在排序中会导致 Perl 很可能死掉。但是我们可以做一个处理 NaN 的数字排序:sub numeric { $a &lt;=&gt; $b // ($a == $a) - ($b == $b) } 这会将 NaN 排序到开头,这具有至少不会死的积极效果。
    猜你喜欢
    • 2011-03-04
    • 2010-10-24
    • 1970-01-01
    • 2021-08-07
    • 1970-01-01
    • 2021-07-27
    • 2011-01-30
    • 1970-01-01
    相关资源
    最近更新 更多