2015-03-26 12 views
5

Refinements war eine experimentelle Ergänzung zu v2.0, dann modifiziert und dauerhaft in v2.1 gemacht. Es bietet eine Möglichkeit, "Monkey-Patching" zu vermeiden, indem es "eine Möglichkeit bietet, eine Klasse lokal zu erweitern".Verwenden von Verfeinerungen hierarchisch

ich versuchte Refinements-this recent question anzuwenden, die ich so vereinfacht:

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

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

Das Element bei Offset i in a bei dem Element entspricht Offset i in b wenn:

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

und

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

Mit anderen Worten, die Übereinstimmung der Zeichenfolgen ist unabhängig von der Groß-/Kleinschreibung.

Das Problem besteht darin, die Anzahl der Elemente von a zu ermitteln, die mit dem entsprechenden Element von b übereinstimmen. Wir sehen, dass die Antwort zwei ist, die Elemente bei Offsets 1 und 2.

Eine Möglichkeit, dies zu tun ist, um Affen-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 

oder verwenden Sie stattdessen 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 

Aber ich möchte Refinements wie folgt verwenden:

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

aber, wie Sie sehen, das gibt die falsche Antwort. Das liegt daran, dass ich Array#== anrufe und die Verfeinerung nicht innerhalb von Array gilt.

kann ich dies tun:

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 

aber das ist nicht das, was ich will. Ich möchte so etwas tun:

module N 
    refine Array do 
    using M 
    end 
end 

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

aber klar, dass das nicht funktioniert.

Meine Frage: Gibt es eine Möglichkeit, String zur Verwendung in Array zu verfeinern, dann verfeinern Array für die Verwendung in meiner Methode?

Antwort

1

Wow, das war wirklich interessant zu spielen mit! Danke, dass du diese Frage gestellt hast! Ich habe einen Weg gefunden, der funktioniert!

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 

Ohne == in Array neu zu definieren, gelten die Verfeinerung nicht.Interessanterweise funktioniert es auch nicht, wenn Sie es in zwei getrennten Modulen tun; dies nicht funktioniert, zum Beispiel:

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 

Ich bin nicht vertraut genug mit den Details der Implementierung refine total zuversichtlich zu sein, warum dieses Verhalten auftritt. Meine Annahme ist, dass das Innere eines Verfeinerungsblocks so behandelt wird, als würde es einen anderen Bereich der obersten Ebene eingeben, ähnlich wie außerhalb der aktuellen Datei definierte Verfeinerungen nur dann angewendet werden, wenn die Datei, in der sie definiert sind, in der aktuellen Datei mit require geparst wird . Dies würde auch erklären, warum verschachtelte Verfeinerungen nicht funktionieren; Die Innenveredelung geht im Moment des Verlassens außer Kraft. Dies würde auch erklären, warum Affen-Patching Array wie Werke folgt:

class Array 
    using M 

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

Dies gilt nicht Opfer der Scoping-Fragen fallen, dass refine erzeugt, so dass die refine auf String Aufenthalte in ihrem Umfang.

+0

Das ist großartig! Ein Detail: Sie könnten erwägen, '! Self.zip (andere) .map {| x, y | zu ersetzen x == y} .einschließen? falsch 'mit' zip (andere) .alle? {| x, y | x == y} '. (Erinnern Sie sich, dass "Selbst" der Standardempfänger ist.) –

+0

Ah, yeah, danke - Ich habe die schlechte Angewohnheit, 'self' zu verwenden, wo immer es angewendet werden könnte. Dies wird mir helfen, mich daran zu erinnern, ob es sinnvoll ist, es zu verwenden. Es sieht hier viel schöner/besser aus ohne "selbst" und "alle" zu benutzen. –

+0

Viele Rubyisten benutzen 'self', wenn sie nicht gebraucht werden, weil sie glauben, dass ihre Auslassung für den Leser verwirrend sein könnte. Ich bin nicht in diesem Lager, aber ich kann nicht sagen, dass sie falsch liegen. –

Verwandte Themen