分层次地使用细化
精化是对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
的相应元素匹配的元素的数量。 我们看到答案是两个,即偏移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
以便在我的方法中使用?
哇,玩这个真的很有趣! 谢谢你提出这个问题! 我发现了一种有效的方法!
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
保留在范围内。