2015-03-26 22 views
5

Refinements è un'aggiunta sperimentale alla v2.0, quindi modificata e resa permanente nella v2.1. Fornisce un modo per evitare la "patch di scimmia" fornendo "un modo per estendere una classe localmente".Utilizzo gerarchico dei perfezionamenti

ho tentato di applicare Refinements-this recent question cui vi semplificare così:

a = [[1, "a"], 
    [2, "b"], 
    [3, "c"], 
    [4, "d"]] 

b = [[1, "AA"], 
    [2, "B"], 
    [3, "C"], 
    [5, "D"]] 

L'elemento all'offset i nel a corrisponde l'elemento all'offset i in b se:

a[i].first == b[i].first 

e

a[i].last.downcase == b[i].last.downcase 

In altre parole, la corrispondenza delle stringhe è indipendente dal caso.

Il problema è determinare il numero di elementi di a corrispondenti all'elemento corrispondente di b. Vediamo che la risposta è due, gli elementi a offset 1 e 2.

Un modo per farlo è quello di scimmia-patch String#==:

class String 
    alias :dbl_eql :== 
    def ==(other) 
    downcase.dbl_eql(other.downcase) 
    end 
end 

a.zip(b).count { |ae,be| ae.zip(be).all? { |aee,bee| aee==bee } } 
    #=> 2 

o utilizzare invece Refinements:

module M 
    refine String do 
    alias :dbl_eql :== 
    def ==(other) 
     downcase.dbl_eql(other.downcase) 
    end 
    end 
end 

'a' == 'A' 
    #=> false (as expected) 
a.zip(b).count { |ae,be| ae.zip(be).all? { |aee,bee| aee==bee } } 
    #=> 0 (as expected) 

using M 
'a' == 'A' 
    #=> true 
a.zip(b).count { |ae,be| ae.zip(be).all? { |aee,bee| aee==bee } } 
    #=> 2 

Tuttavia, vorrei utilizzare Refinements come questo:

using M 
a.zip(b).count { |ae,be| ae == be } 
    #=> 0 

ma, come vedi, dà la risposta sbagliata. Questo perché sto invocando Array#== e il perfezionamento non si applica all'interno di Array.

avrei potuto fare questo:

module N 
    refine Array do 
    def ==(other) 
     zip(other).all? do |ae,be| 
     case ae 
     when String 
      ae.downcase==be.downcase 
     else 
      ae==be 
     end 
     end 
    end 
    end 
end 

using N 
a.zip(b).count { |ae,be| ae == be } 
    #=> 2 

ma non è questo quello che voglio. Voglio fare qualcosa di simile:

module N 
    refine Array do 
    using M 
    end 
end 

using N 
a.zip(b).count { |ae,be| ae == be } 
    #=> 0 

ma chiaramente quello non funziona.

La mia domanda: c'è un modo per perfezionare String per l'uso in Array, quindi perfezionare Array per l'utilizzo nel mio metodo?

risposta

1

Wow, questo era davvero interessante con cui giocare! Grazie per aver chiesto questa domanda! Ho trovato un modo che funziona!

module M 
    refine String do 
    alias :dbl_eql :== 
     def ==(other) 
     downcase.dbl_eql(other.downcase) 
     end 
    end 

    refine Array do 
    def ==(other) 
     zip(other).all? {|x, y| x == y} 
    end 
    end 
end 

a = [[1, "a"], 
    [2, "b"], 
    [3, "c"], 
    [4, "d"]] 

b = [[1, "AA"], 
    [2, "B"], 
    [3, "C"], 
    [5, "D"]] 

using M 

a.zip(b).count { |ae,be| ae == be } # 2 

senza ridefinire == in Array, non si applicherà la raffinatezza.È interessante notare che, inoltre, non funziona se lo fai in due moduli separati; questo non funziona, per esempio:

module M 
    refine String do 
    alias :dbl_eql :== 
     def ==(other) 
     downcase.dbl_eql(other.downcase) 
     end 
    end 
end 

using M 

module N 
    refine Array do 
    def ==(other) 
     zip(other).all? {|x, y| x == y} 
    end 
    end 
end 

a = [[1, "a"], 
    [2, "b"], 
    [3, "c"], 
    [4, "d"]] 

b = [[1, "AA"], 
    [2, "B"], 
    [3, "C"], 
    [5, "D"]] 

using N 

a.zip(b).count { |ae,be| ae == be } # 0 

io non sono abbastanza familiarità con i dettagli di implementazione di refine essere totalmente fiducioso circa il motivo per cui si verifica questo comportamento. La mia ipotesi è che l'interno di un blocco refine viene trattato come se immettesse un diverso scope di livello superiore, analogamente a come i miglioramenti definiti al di fuori del file corrente si applicano solo se il file in cui sono definiti viene analizzato con require nel file corrente . Ciò spiegherebbe anche perché le raffinerie annidate non funzionano; il raffinamento interno va fuori campo nel momento in cui esce. Ciò anche spiegare perché scimmia-patching Array come segue opere:

class Array 
    using M 

    def ==(other) 
    zip(other).all? {|x, y| x == y} 
    end 
end 

Ciò non cadere preda alle questioni scoping che refine crea, in modo che i refine su String soggiorni in ambito.

+0

È grandioso! Un dettaglio: potresti prendere in considerazione la sostituzione di '! Self.zip (altro) .map {| x, y | x == y} .includere? false' con 'zip (altro) .tutto? {| x, y | x == y} '. (Ricorda che 'self' è il ricevitore predefinito.) –

+0

Ah, sì, grazie-ho acquisito una brutta abitudine di usare' self' ovunque possa applicarsi. Questo mi aiuterà a ricordare di considerare se ha senso usare. Sembra molto più bello/più leggibile qui senza "sé" e usando "tutto?". –

+0

Molti Rubyists usano 'self' quando non è necessario perché ritengono che la sua omissione possa confondere il lettore. Non sono in quel campo, ma non posso dire che si sbagliano. –

Problemi correlati