Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/24.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 实例评估';s块参数-记录?目的?_Ruby_Metaprogramming - Fatal编程技术网

Ruby 实例评估';s块参数-记录?目的?

Ruby 实例评估';s块参数-记录?目的?,ruby,metaprogramming,Ruby,Metaprogramming,刚刚意识到,instance\u eval产生self作为相关块的参数(1.9.2版本中的bug除外:) 这在1.8.x中运行得很好(我想是因为没有强制执行块算术) 然后升级到1.9.2-它仍然有效!这是一个奇怪的巧合,尽管lambda块参数是严格执行的(因此它会抱怨没有为self声明参数),但是由于上面链接的错误,self实际上没有在这个版本中传递 然后升级到1.9.3,在那里这个错误得到了修复,所以它开始抛出参数错误-对于一个微小的版本更改IMHO来说非常令人惊讶 因此,一种解决方法是声明参

刚刚意识到,
instance\u eval
产生
self
作为相关块的参数(1.9.2版本中的bug除外:)

这在1.8.x中运行得很好(我想是因为没有强制执行块算术)

然后升级到1.9.2-它仍然有效!这是一个奇怪的巧合,尽管lambda块参数是严格执行的(因此它会抱怨没有为self声明参数),但是由于上面链接的错误,self实际上没有在这个版本中传递

然后升级到1.9.3,在那里这个错误得到了修复,所以它开始抛出参数错误-对于一个微小的版本更改IMHO来说非常令人惊讶

因此,一种解决方法是声明参数,或者将lambda改为块:

 l = proc {  }
  myobj.instance_eval(&l) # fine

只是想描述整个故事,以帮助其他人避免像我这样浪费时间——直到这是正确的记录

阅读Ruby的源代码,我能解释的是:

实例_eval正在执行以下操作:

return specific_eval(argc, argv, klass, self)
这反过来又运行:

 if (rb_block_given_p()) {
     if (argc > 0) {
         rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
     }
     return yield_under(klass, self, Qundef);
 }
您可以看到它们为VALUES参数传递
Qundef

if (values == Qundef) {
    return vm_yield_with_cref(th, 1, &self, cref);
}
在特定的代码行中,他们手动将argc(参数计数)设置为1,并将参数设置为“self”。稍后,准备块的代码将块的参数设置为这些参数,因此第一个参数=“self”,其余为零

设置块参数的代码正在执行以下操作:

   arg0 = argv[0];

   ... bunch of code ...

     else {
         argv[0] = arg0;
     }

     for (i=argc; i<m; i++) {
         argv[i] = Qnil;
     }
为什么??我不知道,但代码似乎是故意的。最好向实现者提问,看看他们对此有何看法

[编辑]

这可能是这样的,因为您传递给instance_eval的块可能是或不是为其精心编制的(取决于self的代码被设置为您希望块修改的类),相反,他们可能会假设您要将希望他们修改的实例作为参数传递给他们,这样他们也可以使用instance_eval

irb(main):001:0> blk = Proc.new do |x| x.class end
#<Proc:0x007fd2018447b8@(irb):1>
irb(main):002:0> blk.call
NilClass
irb(main):003:0> instance_eval &blk
Object
irb(main):001:0>blk=Proc.new do | x | x.class结束
#
irb(主):002:0>blk.call
尼尔Class
irb(主):003:0>实例评估和blk
对象

当然,这只是一个理论,没有官方文档,我只能猜测。

我刚刚发现,与主要用于字符串计算的#instance _eval不同,主要用于块计算的#instance _exec没有描述的行为:

o = Object.new
o.instance_exec { |*a| puts "a.size is #{a.size}" }
  => a.size is 0

这可能是意外的不一致,因此您可能发现了一个bug。发上去。

我刚才在这里问了同样的问题:

在阅读了答案和一些代码之后,我想我理解了ruby为什么会有这种奇怪的(IMHO)不一致性

它基本上允许
Symbol#to_proc
工作

例如
[“foo”,“bar”]。每个(&:put)
都是
[…]的缩写。每个{x | put x}

不是

[…]每个{self.put}

所以ruby也将self作为第一个参数传递给proc,因此基本上proc可以使用self或它的第一个参数

由于实例eval根据定义并不显式地传递参数,因此这几乎总是不可见的行为

例外情况是当proc是lambda时。这不起作用:

2.4.1 :015 > foo = -> { puts 'hi' }
 => #<Proc:0x007fcb578ece78@(irb):15 (lambda)> 
2.4.1 :016 > [1, 2, 3].each(&foo)
ArgumentError: wrong number of arguments (given 1, expected 0)
    from (irb):15:in `block in irb_binding'
    from (irb):16:in `each'
    from (irb):16

但是我们仍然想知道这是否在某个地方被记录下来,self也被作为一个参数传递到里面。instance_eval的主要作用是设置self=receiver_object,不是吗?如果传递同一个对象(上面的[0]),那么它将始终是“self”,不是吗?所以这对我来说是多余的,我是否遗漏了什么?或者你是否将这个问题误解为“实例评估的要点是什么”。不,你不是。事实上,对象在块中始终作为self可用,不需要将其作为参数传入。我不认为我们遗漏了什么,这是一个错误。请投票支持Francisco的分析。你答案的第二个版本肯定很有帮助。除了给
instance_exec
的块所提供的参数是。这里的重点不是有记录的instance_exec行为,而是讨论有无无无无记录的行为。另外,减少对其他方法的假设,特别是不要假设我在写我的答案之前没有阅读完整的可用文档。为什么一种方法的文档和行为应该被视为另一种方法的文档?仅仅因为
instance\u exec
以一种方式工作并不意味着
instance\u eval
也应该这样做,当然也不会让它成为一个bug。它们毕竟是不同的方法。我同意两种不同的方法不同的行为不是错误本身(尽管我同意这可能被视为不一致,可能违反了POL)。真正的bug IMHO是未记录的行为,它实际上破坏了某些用例,然后在调试上浪费时间(除非您确实想查看源代码)。
irb(main):001:0> blk = Proc.new do |x| x.class end
#<Proc:0x007fd2018447b8@(irb):1>
irb(main):002:0> blk.call
NilClass
irb(main):003:0> instance_eval &blk
Object
o = Object.new
o.instance_exec { |*a| puts "a.size is #{a.size}" }
  => a.size is 0
2.4.1 :015 > foo = -> { puts 'hi' }
 => #<Proc:0x007fcb578ece78@(irb):15 (lambda)> 
2.4.1 :016 > [1, 2, 3].each(&foo)
ArgumentError: wrong number of arguments (given 1, expected 0)
    from (irb):15:in `block in irb_binding'
    from (irb):16:in `each'
    from (irb):16
alias original_instance_eval instance_eval 
def instance_eval(*args, &block)
  block&.lambda? ? instance_exec(&block) : original_instance_eval(*args, &block)
end