Как оценивается `ProС# ==`?

Как оценивается Proc#==? RDoc говорит:

prc == other_proc → true или false

Возвращает true, если prc - это тот же объект, что и other_proc, или если они оба являются procs с одним и тем же телом.

Но непонятно, что значит иметь "одно и то же тело". Одним из условий является то, что арность должна быть одинаковой:

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

Но есть нечто большее. Как говорит RDoc, тело имеет значение:

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

Но в то же время похоже, что proc не полностью оценен:

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

В какой степени оценивается тело?

Ответы

Ответ 1

Это изменилось в Ruby 2.0, поэтому вы не должны пытаться сравнивать Proc s. Они не будут ==, если они не являются точно одним и тем же объектом.

Обсуждение может быть найдено здесь.

Если вам действительно нужно сравнить код двух блоков и использовать MRI, вы можете играть с RubyVM::InstructionSequence.disassemble(block) или даже лучше в Ruby 2.0 RubyVM::InstructionSequence.of(block).

Ответ 2

Чтобы ответить на этот вопрос, рассмотрим код сравнения 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;
}

Первая ветвь, если ветка довольно проста - сравните ее, два procs - это один и тот же объект. Второй немного сложнее. Он проверяет, что оба procs имеют одинаковый envval, размер iseq (proc-реализация), размер локальных переменных и сравнивают, что обе реализации идентичны. Это означает, что равенство proc проверяется на уровне синтаксиса, а не на результате proc.

Возьмем https://gist.github.com/4611935 Первый образец работает просто отлично, потому что число локальных переменных одинаково, а последовательность операций одинакова. Назначьте 123 локальной переменной. Второй образец рассматривается как не тот, который отличается от последовательности операций - вы назначаете 123 различным переменным.

Но да, сравнение proc довольно запутанно и было удалено из ruby ​​2.0, я полагаю. Теперь procs сравнивается как обычный объект по его id.