2016-08-30 2 views
1

Oft verwende ich den Operator || =, um redundante Aufrufe zu reduzieren. Ich möchte ähnlicheObjektvariable innerhalb der Instanzmethode zuweisen

@my_variable ||= my_calculation_method 

etwas nehmen und es in

@my_variable.assign { code_block } 

diese Idee mit

class Object 
    def assign 
     if self.instance_of? NilClass 
      self = yield 
     end 
    end 
end 

Wie Sie bereits, self Zuordnung erraten haben keinen Sinn macht und tut nicht arbeiten.

Wie gehen Sie über den @my_variable Zeiger innerhalb der assign Methode, um den Wert zu ändern?

+0

Sie können 'if self.instance_of 'ersetzen? NilClass ('self.not needed, btw) mit' if nil? '. (Siehe [Objekt # nil?] (http://ruby-doc.org/core-2.3.0/Object.html#method-i-nil-3F).) Für viele Unterklassen von 'Object' - aber nicht Alles - Sie könnten schreiben "ersetzen Ausbeute wenn Null?". –

Antwort

3

self = x ist völlig unmöglich. Sie können einem Objekt nicht sagen, was es ist, das sich nie ändert. Wenn es mit einer bestimmten Klasse erstellt wird, stirbt es mit derselben Klasse. Alles, was Sie tun können, ist Änderungen an diesem Objekt oder der ihm zugeordneten Klasse vorzunehmen, aber die Klasse des Objekts ist unveränderlich.

Der einzige Grund, @x ||= y funktioniert, ist, weil @x ist einfach eine Variable, es ist ein Zeiger auf ein Objekt, das Sie neu zuweisen können. Sprechen @x = z ändert das Objekt nicht, es ändert nur den Zeiger. self ist ein Sonderfall, der sich auf den Kontext bezieht, in dem Sie arbeiten, und der nicht neu zugewiesen werden kann.

halten, wenn von einigen fluke Ihre self = yield Aufruf erfolgreich war man neu definieren würde, was nil ist programmweite im Auge behalten.

Diese ganze Anstrengung ist fehlgeleitet, das ||= Muster ist ziemlich idiomatisch Ruby an diesem Punkt. Wenn Sie dieses Muster verbessern wollen, dann machen Sie etwas alternative to memoize.

+0

Ist "absolut unmöglich" weniger wahrscheinlich als "unmöglich"? –

+0

Es gibt viele "unmögliche" Dinge in Ruby, die einfach verrückt sind, darüber nachzudenken. Zum Beispiel: Neudefinition was '+' und '-' für Zahlen bedeuten, oder wenn' true' als 'false' erscheint, wenn' inspect'-ed. – tadman

5

Wenn Ihre Initialisierung Logik nicht-trivial ist und erstreckt sich über mehrere Zeilen (die, nehme ich an, war die Prämisse der Frage), hier ist was Sie tun können, in idiomatischen Rubin:

@my_variable ||= begin 
    # code block 
end 

Was ich normalerweise Do teilt die Logik in zwei Teile. Memoisierung wird von der Berechnung getrennt. Du hast das in deiner ersten Zeile.

Ich finde dieses Muster von XXX + compute_XXX ziemlich einfach zu erkennen/zu folgen.

2

Wie Tadman sagt, können Sie self = nicht schreiben. Sie könnten jedoch eine Methode schreiben, die den Namen der Instanzvariable übernimmt und sie festlegt.

Sie haben Kommentare, dass dies "unelegant" oder "fehlgeleitet" ist. Obwohl ich dieses Urteil nicht teile, ist es gute Praxis, unprätentiösen Code zu schreiben, der so weit wie möglich Standardkonventionen verwendet. Dies hilft anderen, Ihren Code leicht zu verstehen. I.e. es ist wahrscheinlich nicht produktiv, alternative Syntaxen für solche fundamentalen Ruby-Methoden wie ||= zu schreiben; Andere Leute werden viel durch Ihren Code tun müssen, um zu verstehen, wie die Dinge funktionieren.

aber sagen, dass im Interesse der Lehre, wie Ruby tun, was Sie wollen, kann ich diese Object Verfahren zu diesem Zweck vorschlagen:

class Object 
    def assign(key) 
     # make sure the key is a valid instance variable 
     unless key =~ /^[A-Za-z0-9\_]+$/ 
     raise ArgumentError, "#{key} is not a valid ivar name" 
     end 

     if instance_variable_get("@#{key}").nil? 
     instance_variable_set("@#{key}", yield) 
     end 
    end 
end 

# example 
class Foo 
    def set_bsd 
    assign("bsd", yield) 
    end 
end 
foo = Foo.new 
foo.assign("my_ivar") { "val" } 
foo 
# => #<Foo:0x007fc2e4ccaca0 @asd=1> 
foo.set_bsd { 2 } 
foo 
# => #<Foo:0x007fc2e4ccaca0 @asd=1 @bsd = 2> 

obwohl ich Object gegen das Patchen empfehlen würde in auf diese Weise - es ist zu allgemein eine Klasse und könnte unbeabsichtigte Konflikte mit internen Funktionen haben. Fügen Sie diese Funktionalität lieber einer benutzerdefinierten Klasse/einem benutzerdefinierten Modul und entweder include dem Modul hinzu oder erben Sie die Klasse.

+2

Das ist eine große Waffe, die du gibst :) –

+1

Obwohl ich denke, dass diese Art von hartem Flicken ist eine schlechte Angewohnheit, in zu kommen, geben Sie +1 für schiere Bravado. Versuchen Sie, '\ A' und' \ z' in Ihren regulären Ausdrücken zu verwenden, da die '^' und '$' Gegenstücke newline-sensitiv sind. Der Ausdruck sollte lauten: '\ A \ w + \ z', da' \ w' eine Abkürzung für '[a-zA-Z0-9_]' ist. – tadman

+0

FYI Ihr regulärer Ausdruck ist übermäßig streng, zum Beispiel '@' ist eine zulässige Instanzvariable. –

Verwandte Themen