如果我有一个包含两个实例变量@x
和@y
的类,我可以使用self.instance_exec
从proc更改它们:
class Location
attr_accessor :x, :y
def initialize
@x = 0
@y = 0
end
def set &block
self.instance_exec(&block)
end
end
location = Location.new
location.set do
@x = rand(100)
@y = rand(100)
end
puts location.x
puts location.y
如果我有一个方法set
的类有两个局部变量x
和y
,我可以使用proc返回值来设置它们:
class Location
def set &block
x = 0;
y = 0;
x, y = block.call()
# do something with x and y
puts x
puts y
end
end
location = Location.new
location.set do
x = rand(100)
y = rand(100)
[x, y]
end
有没有办法在不使用返回值的情况下从proc访问set
方法局部变量x
和y
?
答案 0 :(得分:2)
块有一种方法可以在调用方法中设置变量,但它并不漂亮。您可以传入绑定,然后使用绑定来评估一些代码:
def foo(binding)
binding.eval "x = 2"
end
x = 1
foo(binding)
p x # => 2
块还带有定义它们的绑定,因此如果正在传递块,则:
def foo(&block)
block.binding.eval "x = 2"
end
x = 1
foo {}
p x # => 2
在这种情况下,块中的内容无关紧要。它只是被用作绑定的载体。
一个块与其调用者交互的更多步行方式是将一个对象传递给该块:
class Point
attr_accessor :x
attr_accessor :y
end
class Location
def set
point = Point.new
yield point
p point.x # => 10
p point.y # => 20
end
end
location = Location.new
location.set do |point|
point.x = 10
point.y = 20
end
这通常比较高级的技术更受欢迎:它很容易理解它的实现和使用。
如果你想(but you probably shouldn't want to),块的调用者可以使用instance_eval / instance_exec来调用块。这会将 self 设置为该块的对象。
class Location
def set(&block)
point = Point.new
point.instance_eval(&block)
p point.x # => 10
p point.y # => 20
end
end
location = Location.new
location.set do
self.x = 10
self.y = 20
end
你看到块在调用编写器时必须使用use self.
,否则会声明新的局部变量,这不是这里想要的。
即使你still probably shouldn't use instance_eval,有时它也很有用。但是,你并不总是知道它什么时候好,所以让我们让方法的调用者决定使用哪种技术。所有方法都要做的是检查块是否有参数:
class Location
def set(&block)
point = Point.new
if block.arity == 1
block.call point
else
point.instance_eval(&block)
end
p point.x
p point.y
end
end
现在,用户可以在该点的范围内执行该块:
location = Location.new
location.set do
self.x = 10
self.y = 20
end
# => 10
# => 20
或者它可以传递给它:
location.set do |point|
point.x = 30
point.y = 40
end
# => 30
# => 40