Refinements是对v2.0的实验性补充,然后在v2.1中进行了修改并永久化。它提供了一种避免"猴子修补的方法。通过提供"一种在本地扩展课程的方法"。
我尝试将Refinements
应用于this recent question,因此我将简化:
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
以便在我的方法中使用?
答案 0 :(得分: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
如果不在==
中重新定义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
创建的范围问题的牺牲品,因此refine
上的String
仍然在范围内。