5

改进是对 v2.0 的实验性补充,然后在 v2.1 中进行了修改并永久化。通过提供“一种在本地扩展类的方法”,它提供了一种避免“猴子补丁”的方法。

我试图申请Refinements这个最近的问题,我将因此简化:

a = [[1, "a"],
     [2, "b"],
     [3, "c"],
     [4, "d"]]

b = [[1, "AA"],
     [2, "B"],
     [3, "C"],
     [5, "D"]]

如果:i_ a_ i_b

a[i].first == b[i].first

a[i].last.downcase == b[i].last.downcase

换句话说,字符串的匹配与大小写无关。

问题是确定a与 的对应元素匹配的元素数量b。我们看到答案是两个,偏移量1和的元素2

一种方法是猴子补丁String#==

class String
  alias :dbl_eql :==
  def ==(other)
    downcase.dbl_eql(other.downcase)
  end
end

a.zip(b).count { |ae,be| ae.zip(be).all? { |aee,bee| aee==bee } }
  #=> 2

或者改为使用Refinements

module M
  refine String do
    alias :dbl_eql :==
    def ==(other)
      downcase.dbl_eql(other.downcase)
    end
  end
end

'a' == 'A'
  #=> false (as expected)
a.zip(b).count { |ae,be| ae.zip(be).all? { |aee,bee| aee==bee } }
  #=> 0 (as expected)

using M
'a' == 'A'
  #=> true
a.zip(b).count { |ae,be| ae.zip(be).all? { |aee,bee| aee==bee } }
  #=> 2

但是,我想这样使用Refinements

using M
a.zip(b).count { |ae,be| ae == be }
  #=> 0

但是,如您所见,这给出了错误的答案。那是因为我正在调用Array#==并且细化不适用于Array.

我可以这样做:

module N
  refine Array do
    def ==(other)
      zip(other).all? do |ae,be|
        case ae
        when String
          ae.downcase==be.downcase
        else
          ae==be
        end
      end  
    end
  end
end

using N
a.zip(b).count { |ae,be| ae == be }
  #=> 2

但这不是我想要的。我想做这样的事情:

module N
  refine Array do
    using M
  end   
end

using N
a.zip(b).count { |ae,be| ae == be }
  #=> 0

但显然这不起作用。

我的问题:有没有一种方法可以优化String用于Array,然后优化Array用于我的方法?

4

1 回答 1

1

哇,玩起来真的很有趣!感谢您提出这个问题!我找到了一种可行的方法!

module M
  refine String do
    alias :dbl_eql :==
      def ==(other)
        downcase.dbl_eql(other.downcase)
      end
  end

  refine Array do
    def ==(other)
      zip(other).all? {|x, y| x == y}
    end
  end
end

a = [[1, "a"],
     [2, "b"],
     [3, "c"],
     [4, "d"]]

b = [[1, "AA"],
     [2, "B"],
     [3, "C"],
     [5, "D"]]

using M

a.zip(b).count { |ae,be| ae == be } # 2

如果不重新定义==in Array,则不会应用细化。有趣的是,如果您在两个单独的模块中执行此操作,它也不起作用;这不起作用,例如:

module M
  refine String do
    alias :dbl_eql :==
      def ==(other)
        downcase.dbl_eql(other.downcase)
      end
  end
end

using M

module N
  refine Array do
    def ==(other)
      zip(other).all? {|x, y| x == y}
    end
  end
end

a = [[1, "a"],
     [2, "b"],
     [3, "c"],
     [4, "d"]]

b = [[1, "AA"],
     [2, "B"],
     [3, "C"],
     [5, "D"]]

using N

a.zip(b).count { |ae,be| ae == be } # 0

我对实现细节不够熟悉,refine无法完全确定为什么会发生这种行为。我的猜测是,细化块的内部被视为进入不同的顶级范围,类似于在当前文件之外定义的细化仅适用require于在当前文件中解析它们的文件时。这也可以解释为什么嵌套细化不起作用;内部精炼在它退出的那一刻就超出了范围。这也可以解释为什么猴子修补Array如下工作:

class Array
  using M

  def ==(other)
    zip(other).all? {|x, y| x == y}
  end
end

这不会成为范围界定问题的牺牲品refine,因此refineonString保持在范围内。

于 2015-03-27T00:05:34.630 回答