Как оценивается `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.