我在以下示例中跟踪自我时遇到了一些麻烦:
# a simple class
class Foo; end
# open the Class class to add an instance method
class Class
# breakpoint 1 - self is equal to Class right here, I get why
puts self
# usage: Foo.some_method(1,2,3)
def some_method(*args)
# breakpoint 2 - when we call Foo.some_method(*args)
# self is equal to Foo, and once again I understand why
puts self
args.each do |arg|
# breakpoint 3 - self is still equal to Foo, even though we are calling each
# on an explicit receiver (an array called args)
puts self
end
end
end
那么当我在args数组上调用each
时, self 怎么没有改变?我的印象是自我总是等于接收器,这肯定是阵列?
答案 0 :(得分:4)
self
始终等于方法本身中的接收器。传递给每个块的块是定义它的作用域的闭包,其中self
等于Foo
。
当你想到它时,这是完全合理的。在C ++中,for
循环应该更改this
吗?使用块来允许您将在您的环境中执行的代码传递给其他代码,以便方法可以替换for
或某些语言using
等语言结构,等等上。
答案 1 :(得分:0)
在Ruby中,即使它不是可取的,几乎任何东西都可能。
正如您所指出的,只需在普通self
块中调用each
即可返回词法范围,即。在REPL中,main
:
[1,2,3].each {|e| p "#{self} - #{e}" }
# "main - 1"
# "main - 2"
# "main - 3"
# => [1, 2, 3]
但是,我们可以使用instance_eval来获取我们想要的范围,即each
的接收者实例:
[1,2,3].instance_eval { each {|e| p "#{self} - #{e}" } }
"[1, 2, 3] - 1"
"[1, 2, 3] - 2"
"[1, 2, 3] - 3"
=> [1, 2, 3]
Periculo tuo ingredere!
附录:
我出于好奇心做了一个基准测试,期待instance_eval
的性能损失很大:
require 'benchmark'
a = Array.new(1_000_000, 1)
Benchmark.bmbm(100) do |x|
x.report("inline, self=main") { a.each {|e| self && e } }
x.report("assigned") { a.each {|e| a && e } }
x.report("inline, instance_eval") { a.instance_eval { each {|e| self && e } } }
end
结果如下:
Rehearsal ---------------------------------------------------------
inline, self=main 0.040000 0.000000 0.040000 ( 0.037743)
assigned 0.040000 0.000000 0.040000 ( 0.043800)
inline, instance_eval 0.040000 0.000000 0.040000 ( 0.041955)
------------------------------------------------ total: 0.120000sec
user system total real
inline, self=main 0.030000 0.000000 0.030000 ( 0.038312)
assigned 0.040000 0.000000 0.040000 ( 0.043693)
inline, instance_eval 0.040000 0.000000 0.040000 ( 0.040029)