分层次地使用细化

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

我试图将Refinements应用于最近的这个问题 ,我将简化这个问题 :

 a = [[1, "a"], [2, "b"], [3, "c"], [4, "d"]] b = [[1, "AA"], [2, "B"], [3, "C"], [5, "D"]] 

如果出现以下情况, b中偏移量i处的元素与b中偏移量i处的元素匹配:

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

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

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

问题是确定与b的相应元素匹配的元素的数量。 我们看到答案是两个,即偏移12处的元素。

一种方法是使用猴子补丁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以便在我的方法中使用?

哇,玩这个真的很有趣! 谢谢你提出这个问题! 我发现了一种有效的方法!

 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 

如果不在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创建的作用域问题的牺牲品,因此String上的refine保留在范围内。