`Proc#==` 如何评估?

15

Proc#==如何评估?RDoc说:

prc == other_proc → true or false

如果prc和other_proc是同一对象或者都是具有相同主体的procs,则返回true。

但是不清楚什么算“拥有相同的主体”。一个条件似乎是arity必须相同:

->{} == ->{} # => true
->{} == ->x{} # => false
->x{} == ->x{} # => true
->x{} == ->y{} # => true
->x{} == ->y,z{} # => false

但这不仅仅是如此。正如RDoc所说,正文内容也很重要:

->{nil} == ->{nil} # => true
->{nil} == ->{false} # => false
->{false} == ->{false} # => true

但是同时它看起来似乎这个过程没有完全被求值:

->{} == ->{nil} # => false
->{false} == ->{1 == 2} # => false

身体评估的程度有多高?


1
我想象中,代码主体必须相同,而不是其评估结果。 - Jim Stewart
1
我曾认为解析的源树必须是相同的,但是... ->{nil} == ->{nil; nil} #=> true 或者第一个nil被剥离了,因为它没有任何影响或意义? - Alex Wayne
1
另外:a,b = ->{}, ->{}; a == b #=> true,但是 a = ->{} [newline] b = ->{}; a == b #=> false。请注意,换行符必须是一个换行符;如果使用分号,则 a 等于 b。真是令人困惑。 - Zach Kemp
1
我不确定它是如何工作的,但看起来在每种情况下,Proc都会执行不同的操作,等价性是错误的。另一个奇怪的例子:https://gist.github.com/4611935 - Alex Wayne
2个回答

9

这在Ruby 2.0中已经发生了改变,因此您不应该尝试比较Procs。除非它们是完全相同的对象,否则它们不会是==

讨论可以在这里找到

如果您真的需要比较两个块的代码并且正在使用MRI,您可以尝试使用RubyVM::InstructionSequence.disassemble(block),或者更好的方法是在Ruby 2.0中使用RubyVM::InstructionSequence.of(block)


1
比较块的反汇编是一种有趣的技术。显然它也有局限性——过程必须共享行号(如果需要,可以使用eval来模拟),并且不考虑过程绑定中变量的值。但我能够创建一个方法缓存,考虑到了块参数。这是个好技巧。 - rcrogers
1
此外,据说 Proc#to_source 将在某个时候得到实现:http://bugs.ruby-lang.org/issues/2080 - rcrogers

4
为了回答这个问题,让我们看一下proc比较代码。
static VALUE
proc_eq(VALUE self, VALUE other)
{
    if (self == other) {
        return Qtrue;
    }
    else {
        if (rb_obj_is_proc(other)) {
           rb_proc_t *p1, *p2;
           GetProcPtr(self, p1);
           GetProcPtr(other, p2);
           if (p1->envval == p2->envval &&
              p1->block.iseq->iseq_size == p2->block.iseq->iseq_size &&
              p1->block.iseq->local_size == p2->block.iseq->local_size &&
              MEMCMP(p1->block.iseq->iseq, p2->block.iseq->iseq, VALUE,
                    p1->block.iseq->iseq_size) == 0) {
                 return Qtrue;
           }
       }
    }
    return Qfalse;
}

第一个 if 分支非常简单 - 比较两个 proc 是否为同一对象。 第二个分支有点棘手。它检查两个 proc 是否具有相同的 envval、iseq(proc 实现)大小和本地变量大小,并比较两个实现是否相同。这意味着 proc 的相等性是在语法层面上检查的,而不是在 proc 结果上检查。
让我们看看 https://gist.github.com/4611935 第一个示例运行得非常好,因为本地变量的数量相同,操作序列也相同。将 123 分配给本地变量。第二个示例被视为不同,因为操作序列不同 - 将 123 分配给不同的变量。
但是,确实,proc 比较非常令人困惑,并且我认为已经从 Ruby 2.0 中删除了。现在,procs 被视为普通对象按其 id 进行比较。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接