2013-01-23 19 views
15

Jak ocenia się Proc#==? RDoc mówi:Jak to jest "ProC# ==" oceniane?

prc == other_proc → prawda czy fałsz

Zwraca true jeśli prc jest taki sam przedmiot jak other_proc, lub jeśli są one zarówno procesy z tego samego ciała.

Ale nie jest jasne, co się liczy jako "to samo ciało". Wydaje się, że stan musi być taki sam:

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

Ale jest coś więcej. Jak rdoc mówi, ciało ma znaczenie:

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

Ale w tym samym czasie, to wygląda proc nie jest w pełni ocenić:

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

W jakim stopniu organizm oceniane?

+1

Wyobrażam sobie, że kod w treści musi być taki sam, a nie wynik jego oceny. –

+1

Myślałem, że analizowane drzewo źródłowe musi być identyczne, ale ... '-> {nil} == -> {nil; nil} # => true' A może pierwszy zero jest usuwany z drzewa źródłowego, ponieważ nie ma żadnego efektu lub znaczenia? –

+1

Również: 'a, b = -> {}, -> {}; a == b # => prawda', ale 'a = -> {} [nowa linia] b = -> {}; a == b # => fałsz'. Zauważ, że znak nowej linii MUSI być znakiem nowej linii; jeśli używasz średnika, 'a' jest równe' b'. WTF rzeczywiście. –

Odpowiedz

9

To ma changed in Ruby 2.0, więc nie powinieneś próbować porównywać Proc s. Nie będą one ==, chyba że są dokładnie tym samym obiektem.

Dyskusja może być found here.

Jeśli naprawdę potrzebujesz porównać kod dwóch bloków i używasz MRI, możesz grać z RubyVM::InstructionSequence.disassemble(block) lub jeszcze lepiej w Ruby 2.0 RubyVM::InstructionSequence.of(block).

+1

Porównywanie demontażu bloków to interesująca technika.Oczywiście ma swoje ograniczenia - procs muszą współdzielić numer linii (możesz to zrobić w razie potrzeby eval), a wartości zmiennych w powiązaniu proc nie są brane pod uwagę. Ale udało mi się stworzyć pamięć podręczną metody, która uwzględniała argument blokowy. Świetna rada. – rcrogers

+1

Ponadto, ProC# to_source prawdopodobnie zostanie zaimplementowane kiedyś: http://bugs.ruby-lang.org/issues/2080 – rcrogers

4

Aby odpowiedzieć na to pytanie, spójrzmy na kod porównania 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; 
} 

Pierwsze jeśli oddział jest dość prosta - porównać ją dwa procuje są tego samego obiektu. Drugi jest nieco trudniejszy. Sprawdza, czy oba procesy mają taką samą istotę, rozmiar iseq (implementacja proc), wielkość zmiennych lokalnych i porównuje, że obie implementacje są identyczne. Oznacza to, że równość procesu jest sprawdzana na poziomie składni, a nie na wyniku proc.

Pozwala wziąć https://gist.github.com/4611935 Pierwszy przykład działa dobrze, ponieważ liczba zmiennych lokalnych jest taka sama, a kolejność operacji jest taka sama. Przypisz 123 do zmiennej lokalnej. Druga próbka traktowana jest jako nie to samo, ponieważ kolejność operacji jest różna - przypisujesz 123 do różnych zmiennych.

Ale tak, porównanie proc jest dość mylące i zostało usunięte z ruby ​​2.0. Teraz procs są porównywane jako zwykły obiekt według jego id.