Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 在不更改块上下文的情况下使块中的方法可用?_Ruby - Fatal编程技术网

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,您是否尝试运行它?这是一段代码片段,不是完整的功能代码。我在我的项目中尝试了你的方法,但失败了。为了解释这个问题,我把你的代码片段(从末尾算起的第三个)修改成与我在项目中所做的一致。