2013-01-23 16 views
15

Come viene valutato Proc#==? RDoc dice:Come viene valutato `ProC# ==`?

prc == other_proc → vero o falso

Restituisce vero se PRC è lo stesso oggetto come other_proc, o se sono entrambi procs con lo stesso corpo.

Ma non è chiaro quello che conta come avere "lo stesso corpo". Una condizione sembra essere che l'arity deve essere la stessa:

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

Ma c'è di più. Come RDoc dice, il corpo conta:

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

Ma allo stesso tempo, sembra che il proc non sia pienamente valutata:

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

Fino a che punto è il corpo valutata?

+1

Immagino che il codice nel corpo sia lo stesso, non il risultato della sua valutazione. –

+1

Stavo pensando che l'albero dei sorgenti analizzato debba essere identico, ma ... '-> {nil} == -> {nil; nil} # => true' O forse il primo nil viene rimosso dall'origine perché non ha alcun effetto o significato? –

+1

Inoltre: 'a, b = -> {}, -> {}; a == b # => true', ma 'a = -> {} [newline] b = -> {}; a == b # => falso'. Nota che la nuova riga DEVE essere una nuova riga; se usi un punto e virgola, 'a' è uguale a' b'. WTF davvero. –

risposta

9

Questo ha changed in Ruby 2.0, quindi non si dovrebbe provare a confrontare Proc s. Non saranno == a meno che non siano esattamente lo stesso oggetto.

La discussione può essere found here.

Se è davvero necessario confrontare il codice di due blocchi e si utilizza MRI, è possibile giocare con RubyVM::InstructionSequence.disassemble(block) o anche meglio in Ruby 2.0 RubyVM::InstructionSequence.of(block).

+1

Confrontare lo smontaggio dei blocchi è una tecnica interessante.Ha delle limitazioni, ovviamente - i proc devono condividere un numero di riga (puoi falsificare questo con eval se necessario), e i valori delle variabili nel binding del proc non vengono presi in considerazione. Ma sono stato in grado di creare una cache di metodi che tenesse conto dell'argomento del blocco. Ottimo consiglio. – rcrogers

+1

Inoltre, ProC# to_source dovrebbe essere implementato a volte: http://bugs.ruby-lang.org/issues/2080 – rcrogers

4

Per rispondere a questa domanda consente di guardare il codice confronto 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; 
} 

I primi se il lato è abbastanza semplice - confronto che due procs sono lo stesso oggetto. Il secondo è un po 'più complicato. Controlla che entrambi i proc abbiano la stessa enval, la dimensione di iseq (implementazione proc), le dimensioni delle variabili locali e che le due implementazioni siano identiche. Ciò significa che l'uguaglianza proc è verificata su un livello di sintassi, non sul risultato proc.

Prendere https://gist.github.com/4611935 Il primo esempio funziona bene perché il numero di variabili locali è lo stesso e la sequenza di operazioni è la stessa. Assegna 123 alla variabile locale. Il secondo campione viene considerato come non lo stesso perché la sequenza di operazioni differisce - si assegna 123 a variabili diverse.

Ma sì, il confronto proc è piuttosto confuso ed è stato rimosso dalla forma ruby ​​2.0 presumo. Ora i proc vengono confrontati come oggetti regolari dal suo id.