分层使用优化
Using Refinements Hierarchically
Refinements 是对 v2.0 的实验性添加,然后在 v2.1 中进行了修改并永久保留。它提供了一种通过提供 "a way to extend a class locally".
来避免 "monkey-patching" 的方法
我试图将 Refinements
应用于 this recent question,我将这样简化:
a = [[1, "a"],
[2, "b"],
[3, "c"],
[4, "d"]]
b = [[1, "AA"],
[2, "B"],
[3, "C"],
[5, "D"]]
a
中偏移量 i
处的元素匹配 b
中偏移量 i
处的元素,如果:
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
以便在我的方法中使用?
哇,玩起来真的很有趣!感谢您提出这个问题!我找到了一个可行的方法!
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
的实现细节不够熟悉,无法完全确定为什么会出现这种行为。我的猜测是,refine 块的内部被视为进入不同的顶级范围,类似于在当前文件外部定义的 refines 仅在使用 require
解析定义它们的文件时才适用在当前文件中。这也可以解释为什么嵌套优化不起作用;内部精炼在它退出的那一刻就超出了范围。这将 也 解释为什么猴子修补 Array
如下工作:
class Array
using M
def ==(other)
zip(other).all? {|x, y| x == y}
end
end
这不会成为 refine
造成的范围界定问题的牺牲品,因此 String
上的 refine
仍在范围内。
Refinements 是对 v2.0 的实验性添加,然后在 v2.1 中进行了修改并永久保留。它提供了一种通过提供 "a way to extend a class locally".
来避免 "monkey-patching" 的方法我试图将 Refinements
应用于 this recent question,我将这样简化:
a = [[1, "a"],
[2, "b"],
[3, "c"],
[4, "d"]]
b = [[1, "AA"],
[2, "B"],
[3, "C"],
[5, "D"]]
a
中偏移量 i
处的元素匹配 b
中偏移量 i
处的元素,如果:
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
以便在我的方法中使用?
哇,玩起来真的很有趣!感谢您提出这个问题!我找到了一个可行的方法!
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
的实现细节不够熟悉,无法完全确定为什么会出现这种行为。我的猜测是,refine 块的内部被视为进入不同的顶级范围,类似于在当前文件外部定义的 refines 仅在使用 require
解析定义它们的文件时才适用在当前文件中。这也可以解释为什么嵌套优化不起作用;内部精炼在它退出的那一刻就超出了范围。这将 也 解释为什么猴子修补 Array
如下工作:
class Array
using M
def ==(other)
zip(other).all? {|x, y| x == y}
end
end
这不会成为 refine
造成的范围界定问题的牺牲品,因此 String
上的 refine
仍在范围内。