微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Ruby <=>组合器的实现

不经常,人们想要实现< => (产品数据类型上的比较或“太空船”)运算符,即具有多个字段的类(所有这些(我们希望!)已经具有< =>实现),以特定顺序比较字段.
def <=>(o)
    f1 < o.f1 && (return -1)
    f1 > o.f1 && (return  1)
    f2 < o.f2 && (return -1)
    f2 > o.f2 && (return  1)
    return 0
end

这既繁琐又容易出错,尤其是在很多领域.它容易出错,我经常觉得我应该对这个功能进行单元测试,这只会增加乏味和冗长.

Haskell提供了一种特别好的方法

import Data.Monoid (mappend)
import Data.Ord (comparing)

-- From the standard library:
-- data Ordering = LT | EQ | GT

data D = D { f3 :: Int,f2 :: Double,f1 :: Char } deriving Show

compareD :: D -> D -> Ordering
compareD = foldl1 mappend [comparing f1,comparing f2,comparing f3]

(对于那些不熟悉折叠的人,上面扩展到了

comparing f1 `mappend` comparing f2 `mappend` comparing f3

它产生一个可以应用于两个D的函数,以产生一个Ordering.)

compareD的定义非常简单,显然是正确的,即使没有静态类型检查,我也觉得不需要进行单元测试.

实际上,问题甚至可能比这更有趣,因为我可能不想仅使用标准< =>运算符,但在不同的时间以不同的方式排序,例如:

sortByOrderings :: [a -> a -> Ordering] -> [a] -> [a]
sortByOrderings = sortBy . foldl1 mappend

sortByF3F1 = sortByOrderings [comparing f3,comparing f1]
sortByF2F3 = sortByOrderings [comparing f2,comparing f3]

所以,问题:

>在Ruby中实现此类事物的典型方法是什么?
>使用标准库中定义的内容,最好的方法是什么?
>相比之下,上面的Haskell代码有多接近,以及它的可靠性如何?如有必要,如何确保字段具有正确实现的< =>或者<和>操作符?

很明显,虽然这是一个Ruby问题,但如果本网站的长老如此同意,我很乐意考虑讨论Haskell技术.请随意评论这是否合适,如果是,则标记此帖子’haskell’.

解决方法

这是你的想法的一个即兴重复.它没有定义任何额外的常量,允许您使用实例变量和方法的任意组合来比较两个对象,提前退出不相等,并包括Comparable定义的所有方法.
class Object
    def self.compare_by(*symbols)
        include Comparable
        dispatchers = symbols.map do |symbol|
          if symbol.to_s =~ /^@/
            lambda { |o| o.instance_variable_get(symbol) }
          else
            lambda { |o| o.__send__(symbol) }
          end
        end
        define_method('<=>') do |other|
          dispatchers.inject(0) do |_,dispatcher|
            comp = dispatcher[self] <=> dispatcher[other]
            break comp if comp != 0
            comp
          end
        end
    end
end

class T
    def initialize(name,f1,f2,f3)
      @name,@f1,@f2,@f3 = name,f3;
    end

    def f1
      puts "checking #@name's f1"
      @f1
    end
    def f3
      puts "checking #@name's f3"
      @f3
    end

    compare_by :f1,:@f2,:f3
end

w = T.new('x',1,2)
x = T.new('x',2,3)
y = T.new('y',3,4)
z = T.new('z',5)

p w < x   #=> checking x's f1
          #   checking x's f1
          #   true
p x == y  #=> checking x's f1
          #   checking y's f1
          #   false
p y <= z  #=> checking y's f1
          #   checking z's f1
          #   checking y's f3
          #   checking z's f3
          #   true

如果您愿意,可以在其中插入一些额外的错误检查以确保这一点用于比较的值实际上响应< => (使用respond_to?’< =>‘),并尝试在他们没有的情况下给出更明确的错误消息.

原文地址:https://www.jb51.cc/ruby/267660.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐