分层使用细化

Car*_*and 5 ruby refinements

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

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

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

b = [[1, "AA"],
     [2, "B"],
     [3, "C"],
     [5, "D"]]
Run Code Online (Sandbox Code Playgroud)

在位置的元素ia的元素相匹配的偏移ib,如果:

a[i].first == b[i].first
Run Code Online (Sandbox Code Playgroud)

a[i].last.downcase == b[i].last.downcase
Run Code Online (Sandbox Code Playgroud)

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

问题是确定a匹配相应元素的元素数量b.我们看到答案是两个,即抵消的元素12.

一种方法是使用Monkey-patch 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
Run Code Online (Sandbox Code Playgroud)

或者改为使用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
Run Code Online (Sandbox Code Playgroud)

但是,我想这样使用Refinements:

using M
a.zip(b).count { |ae,be| ae == be }
  #=> 0
Run Code Online (Sandbox Code Playgroud)

但是,如你所见,这给出了错误的答案.那是因为我正在调用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
Run Code Online (Sandbox Code Playgroud)

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

module N
  refine Array do
    using M
  end   
end

using N
a.zip(b).count { |ae,be| ae == be }
  #=> 0
Run Code Online (Sandbox Code Playgroud)

但显然这不起作用.

我的问题:有没有办法改进String使用Array,然后改进Array我的方法使用?

Zoë*_*rks 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
Run Code Online (Sandbox Code Playgroud)

如果不重新定义==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
Run Code Online (Sandbox Code Playgroud)

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

class Array
  using M

  def ==(other)
    zip(other).all? {|x, y| x == y}
  end
end
Run Code Online (Sandbox Code Playgroud)

这不会成为refine所产生的范围问题的牺牲品,因此refineonString仍在范围内。