Ruby 在不更改块上下文的情况下使块中的方法可用?
我想创建一个执行以下操作的类:Ruby 在不更改块上下文的情况下使块中的方法可用?,ruby,Ruby,我想创建一个执行以下操作的类: 它的实例接受一个块 在实例初始化期间,它执行某些操作,然后调用块,然后执行更多操作 在块中,该类中的另一个方法应该可用 以下是我希望它的工作方式: Foo.new do 写着“见鬼,我可以在今天下午3点之前帮你弄个脚趾……” 酒吧 把“…涂上指甲油。” 结束 我通过以下课程成功实现了这一目标: class-Foo def初始化(&block) 放置“这表示开始操作” 实例评估和块 “这象征着一个结束的行动” 结束 def棒 写上“我应该在街区内” 结束 结束
- 它的实例接受一个块
- 在实例初始化期间,它执行某些操作,然后调用块,然后执行更多操作
- 在块中,该类中的另一个方法应该可用
Foo.new do
写着“见鬼,我可以在今天下午3点之前帮你弄个脚趾……”
酒吧
把“…涂上指甲油。”
结束
我通过以下课程成功实现了这一目标:
class-Foo
def初始化(&block)
放置“这表示开始操作”
实例评估和块
“这象征着一个结束的行动”
结束
def棒
写上“我应该在街区内”
结束
结束
如您所见,我使用了instance\u eval
技巧。它允许在块内使用条
它工作正常,但这里的问题是instance\u eval
使当前本地上下文不可用。如果我从另一个类中使用它,我将失去对该类方法的访问权。例如:
Baz类
def初始化
Foo.newdo
bar#->有效
quux#->因“无此方法”而失败
结束
结束
def quux
把“Quux”
结束
结束
问题是:如何允许在块内执行bar
,而不丢失对qux
的访问
我的新手想到的唯一方法是将
bar
作为参数传递到块中。但是,这需要更多的打字,所以我希望尽可能地避免。<> > P>代码> StistaSeValue<代码>不考虑调用块的范围,因此每个方法调用只与FO中定义的内容有关。
所以你有两个选择。或者
def initialize
baz = self
Foo.new do
bar # -> Works
baz.quux # -> Works
end
end
或
我不确定哪一个性能更好,但您选择的选项取决于您自己的偏好
它工作得很好,但这里的问题是,instance_eval
当前本地上下文不可用
instance_eval()不做这样的事情。所有块内的代码,即看起来像:
{ code here }
可以查看创建块时周围范围中存在的变量。执行块时,块无法看到周围范围中的变量。在计算机科学术语中,块被称为闭包,因为它在创建时“关闭”了周围范围中的变量
实例_eval所做的是为块关闭的自变量指定一个新值。以下是一个例子:
puts self #=>main
func = Proc.new {puts self}
func.call #=>main
class Dog
def do_stuff(f)
puts self
f.call
end
end
d = Dog.new
d.do_stuff(func)
--output:--
#<Dog:0x000001019325b8>
main #The block still sees self=main because self was equal to main when the block was created and nothing changed the value of that self variable
…然后ruby将该行转换为:
self.quux()
因此,了解变量self的值很重要。检查此代码:
class Dog
def do_stuff(f)
puts self #Dog_instance
instance_eval &f #equivalent to self.instance_val &f,
#which is equivalent to Dog_instance.instance_eval &f
end
end
因为instance_eval()将块内的self变量的值设置为instance_eval()的“receiver”,所以块内self的值设置为等于Dog_实例
在此处检查您的代码:
puts self #=> main
Foo.new do
puts self #=>main
bar #equivalent to self.bar--and self is not a Foo or Baz instance
#so self cannot call methods in those classes
end
class Foo
def initialize(&block)
instance_eval &block #equivalent to self.instance_eval &block
end
end
在此处检查您的代码:
puts self #=> main
Foo.new do
puts self #=>main
bar #equivalent to self.bar--and self is not a Foo or Baz instance
#so self cannot call methods in those classes
end
class Foo
def initialize(&block)
instance_eval &block #equivalent to self.instance_eval &block
end
end
在Foo#initialize()内部,self等于新的Foo实例。这意味着在块内部,self被设置为等于一个Foo实例,因此如果在块内部写入以下内容:
quux()
这相当于:
self.quux()
Foo_instance.quux()
这相当于:
self.quux()
Foo_instance.quux()
这意味着必须在Foo中定义qux()
在这个答复中:
class Baz
def initialize
puts self #=>Baz_instance
baz = self
Foo.new do
bar # -> Works
baz.quux # -> Works
end
end
def quux
puts "Quux"
end
end
b = Baz.new
…吧台和baz系列似乎有相同的“接收器”:
puts self #=>Baz_instance
baz = self #To evaluate that assignment ruby has to replace the variable self
#with its current value, so this is equivalent to baz = Baz_instance
#and baz no longer has any connection to a variable called self.
Foo.new do
bar #=> equivalent to self.bar, which is equivalent to Baz_instance.bar
baz.quux #=> equivalent to Baz_instance.quux
end
但当instance_eval()执行该块时,即do和end之间的所有内容,instance_eval()会更改self的值:
Foo.new do #instance_eval changes self inside the block so that self = Foo_instance
bar #=> equivalent to self.bar which is now equivalent to Foo_instance.bar
baz.quux #=> the block still sees baz = Baz_instance, so equivalent to Baz_instance.bar
end
向上投票支持大Lebowski参考。。。做得很好。很好,简洁的解释。谢谢你的回答,史蒂文。对于第一种方法,我的问题是:第二种方法确实有效@lolmaus AndreyMikhaylov无法在quux方法中调用bar的原因是,您已经从Baz对象调用了quux,这意味着
self
不再是Foo。您的解决方案是将Foo作为参数传递。谢谢您的详细解释,7stud。(没关系,这里是错的)@lolmaus AndreyMikhaylov,代码不会产生你所说的错误;它在“initialize”中生成错误::未初始化的常量Baz::Foo(namererror)
@7stud,您是否尝试运行它?这是一段代码片段,不是完整的功能代码。我在我的项目中尝试了你的方法,但失败了。为了解释这个问题,我把你的代码片段(从末尾算起的第三个)修改成与我在项目中所做的一致。