Dynamische Konstante ist so etwas wie trockenes Wasser? :)
fl00r
39
Es heißt nicht, dass die Konstante dynamisch ist. Es heißt, dass die Zuordnung dynamisch ist.
sepp2k
Antworten:
141
Ihr Problem ist, dass Sie der Konstante jedes Mal, wenn Sie die Methode ausführen, einen neuen Wert zuweisen. Dies ist nicht zulässig, da dadurch die Konstante nicht konstant wird. obwohl die Inhalte der Zeichenfolge gleich sind (für den Moment, jedenfalls), die tatsächliche String - Objekt selbst unterscheidet sich jedes Mal , wenn die Methode aufgerufen wird. Beispielsweise:
def foo
p "bar".object_idend
foo #=> 15779172
foo #=> 15779112
Wenn Sie Ihren Anwendungsfall erläutern - warum Sie den Wert einer Konstante in einer Methode ändern möchten -, können wir Ihnen möglicherweise bei einer besseren Implementierung helfen.
Vielleicht möchten Sie lieber eine Instanzvariable für die Klasse haben?
classMyClassclass<<self
attr_accessor :my_constantenddef my_methodself.class.my_constant ="blah"endend
p MyClass.my_constant #=> nilMyClass.new.my_method
p MyClass.my_constant #=> "blah"
Wenn Sie den Wert einer Konstante in einer Methode wirklich ändern möchten und Ihre Konstante ein String oder ein Array ist, können Sie die #replaceMethode "betrügen" und verwenden , um das Objekt dazu zu bringen, einen neuen Wert anzunehmen, ohne das Objekt tatsächlich zu ändern:
classMyClass
BAR ="blah"def cheat(new_bar)
BAR.replace new_barendend
p MyClass::BAR #=> "blah"MyClass.new.cheat "whee"
p MyClass::BAR #=> "whee"
Das OP hat nie gesagt, dass er den Wert der Konstante ändern möchte, sondern nur einen Wert zuweisen möchte. Der häufige Anwendungsfall, der zu diesem Ruby-Fehler führt, besteht darin, dass Sie den Wert in einer Methode aus anderen Laufzeit-Assets (Variablen, Befehlszeilenargumente, ENV) erstellen, normalerweise in einem Konstruktor, z def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end. Es ist einer dieser Fälle, in denen Ruby keinen einfachen Weg hat.
Arnaud Meuret
2
@ArnaudMeuret In diesem Fall möchten Sie eine Instanzvariable (z. B. @variable), keine Konstante. Andernfalls würden Sie DBjedes Mal neu zuweisen, wenn Sie eine neue Instanz dieser Klasse instanziieren.
Ajedi32
2
@ Ajedi32 Diese Situation ergibt sich normalerweise aus externen Einschränkungen, nicht aus Entwurfsentscheidungen wie meinem Beispiel mit Sequel. Mein Punkt ist, dass Ruby in bestimmten Bereichen einen Wert einer Konstanten zuweisen darf und in anderen nicht. Früher war es Sache des Entwicklers, mit Bedacht zu entscheiden, wann die Aufgabe ausgeführt werden soll. Ruby hat das geändert. Nicht für alle gut.
Arnaud Meuret
2
@ArnaudMeuret Ich gebe zu, dass ich Sequel noch nie zuvor verwendet habe, daher kann ich dies nicht mit 100% iger Sicherheit sagen, aber wenn ich nur die Dokumentation für Sequel betrachte, sehe ich nichts, was besagt, dass Sie das Ergebnis Sequel.connecteiner Konstanten mit dem Namen DB zuweisen MÜSSEN . In der Dokumentation heißt es ausdrücklich, dass dies nur eine Empfehlung ist. Das klingt für mich nicht nach einer externen Einschränkung.
Ajedi32
@ Ajedi32 1) Ich habe nie geschrieben, dass (Name der Konstante oder sogar, dass Sie sie irgendwo aufbewahren mussten) dies nur ein Beispiel ist. 2) Die Einschränkung besteht darin, dass Ihre Software möglicherweise nicht über die erforderlichen Informationen verfügt, bis Sie sich normalerweise in einem dynamischen Kontext befinden .
Arnaud Meuret
69
Da Konstanten in Ruby nicht geändert werden sollen, rät Ruby Sie davon ab, sie in Teilen des Codes zuzuweisen, die möglicherweise mehrmals ausgeführt werden, z. B. innerhalb von Methoden.
Unter normalen Umständen sollten Sie die Konstante innerhalb der Klasse selbst definieren:
Wenn Sie aus irgendeinem Grund wirklich eine Konstante innerhalb einer Methode definieren müssen (möglicherweise für eine Art von Metaprogrammierung), können Sie Folgendes verwenden const_set:
Auch hier const_setsollten Sie unter normalen Umständen nicht unbedingt darauf zurückgreifen müssen. Wenn Sie nicht sicher sind, ob Sie Konstanten auf diese Weise wirklich zuweisen möchten, sollten Sie eine der folgenden Alternativen in Betracht ziehen:
Klassenvariablen
Klassenvariablen verhalten sich in vielerlei Hinsicht wie Konstanten. Sie sind Eigenschaften einer Klasse und in Unterklassen der Klasse, für die sie definiert sind, zugänglich.
Der Unterschied besteht darin, dass Klassenvariablen modifizierbar sein sollen und daher problemlos internen Methoden zugewiesen werden können.
classMyClassdefself.my_class_variable@@my_class_variableenddef my_method@@my_class_variable="foo"endendclassSubClass<MyClassendMyClass.my_class_variable#=> NameError: uninitialized class variable @@my_class_variable in MyClassSubClass.my_class_variable#=> NameError: uninitialized class variable @@my_class_variable in MyClassMyClass.new.my_methodMyClass.my_class_variable #=> "foo"SubClass.my_class_variable #=> "foo"
Klassenattribute
Klassenattribute sind eine Art "Instanzvariable für eine Klasse". Sie verhalten sich ein bisschen wie Klassenvariablen, außer dass ihre Werte nicht mit Unterklassen geteilt werden.
Und der Vollständigkeit halber sollte ich wahrscheinlich erwähnen: Wenn Sie einen Wert zuweisen müssen, der erst bestimmt werden kann, nachdem Ihre Klasse instanziiert wurde, besteht eine gute Chance, dass Sie tatsächlich nach einer einfachen alten Instanzvariablen suchen.
In Ruby ist jede Variable, deren Name mit einem Großbuchstaben beginnt, eine Konstante, die Sie nur einmal zuweisen können. Wählen Sie eine dieser Alternativen:
Sie können eine Variable nicht mit Großbuchstaben benennen, oder Ruby nimmt an, dass sie eine Konstante ist, und möchte, dass sie ihren Wert konstant hält. In diesem Fall wäre das Ändern ihres Werts ein Fehler, ein "dynamischer Konstantenzuweisungsfehler". Mit Kleinbuchstaben sollte in Ordnung sein
Ruby mag es nicht, dass Sie die Konstante innerhalb einer Methode zuweisen, da dies zu einer erneuten Zuweisung führen kann. Einige SO-Antworten vor mir bieten die Alternative, sie außerhalb einer Methode zuzuweisen - aber in der Klasse, die ein besserer Ort ist, um sie zuzuweisen.
Weicome zu SO John. Möglicherweise möchten Sie diese Antwort verbessern und einen Beispielcode für das hinzufügen, was Sie beschreiben.
Cleptus
0
Vielen Dank an Dorian und Phrogz, die mich an die Array- (und Hash-) Methode #replace erinnert haben, die "den Inhalt eines Arrays oder Hashs ersetzen kann".
Die Vorstellung, dass der Wert eines CONSTANT geändert werden kann, aber mit einer nervigen Warnung, ist einer der wenigen konzeptionellen Fehltritte von Ruby - diese sollten entweder vollständig unveränderlich sein oder die konstante Idee insgesamt verwerfen. Aus der Sicht eines Codierers ist eine Konstante deklarativ und beabsichtigt, ein Signal an andere, dass "dieser Wert nach der Deklaration / Zuweisung wirklich unveränderlich ist".
Aber manchmal schließt eine "offensichtliche Erklärung" tatsächlich andere, zukünftige nützliche Gelegenheiten aus. Beispielsweise...
Es gibt legitime Anwendungsfälle, in denen der Wert einer "Konstante" möglicherweise wirklich geändert werden muss: Zum Beispiel das erneute Laden von ARGV aus einer REPL-ähnlichen Eingabeaufforderungsschleife und das erneute Ausführen von ARGV über weitere (nachfolgende) OptionParser.parse! Anrufe - voila! Verleiht "Befehlszeilenargumenten" ein völlig neues dynamisches Dienstprogramm.
Das praktische Problem besteht entweder in der vermuteten Annahme, dass "ARGV eine Konstante sein muss", oder in der eigenen Initialisierungsmethode von optparse, die die Zuordnung von ARGV zur Instanz var @default_argv für die nachfolgende Verarbeitung fest codiert - dieses Array (ARGV) wirklich sollte ein Parameter sein, der gegebenenfalls eine erneute Analyse und Wiederverwendung fördert. Eine ordnungsgemäße Parametrisierung mit einem geeigneten Standard (z. B. ARGV) würde die Notwendigkeit vermeiden, das "konstante" ARGV jemals zu ändern. Nur ein paar Gedanken wert ...
Antworten:
Ihr Problem ist, dass Sie der Konstante jedes Mal, wenn Sie die Methode ausführen, einen neuen Wert zuweisen. Dies ist nicht zulässig, da dadurch die Konstante nicht konstant wird. obwohl die Inhalte der Zeichenfolge gleich sind (für den Moment, jedenfalls), die tatsächliche String - Objekt selbst unterscheidet sich jedes Mal , wenn die Methode aufgerufen wird. Beispielsweise:
Wenn Sie Ihren Anwendungsfall erläutern - warum Sie den Wert einer Konstante in einer Methode ändern möchten -, können wir Ihnen möglicherweise bei einer besseren Implementierung helfen.
Vielleicht möchten Sie lieber eine Instanzvariable für die Klasse haben?
Wenn Sie den Wert einer Konstante in einer Methode wirklich ändern möchten und Ihre Konstante ein String oder ein Array ist, können Sie die
#replace
Methode "betrügen" und verwenden , um das Objekt dazu zu bringen, einen neuen Wert anzunehmen, ohne das Objekt tatsächlich zu ändern:quelle
def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end
. Es ist einer dieser Fälle, in denen Ruby keinen einfachen Weg hat.@variable
), keine Konstante. Andernfalls würden SieDB
jedes Mal neu zuweisen, wenn Sie eine neue Instanz dieser Klasse instanziieren.Sequel.connect
einer Konstanten mit dem Namen DB zuweisen MÜSSEN . In der Dokumentation heißt es ausdrücklich, dass dies nur eine Empfehlung ist. Das klingt für mich nicht nach einer externen Einschränkung.Da Konstanten in Ruby nicht geändert werden sollen, rät Ruby Sie davon ab, sie in Teilen des Codes zuzuweisen, die möglicherweise mehrmals ausgeführt werden, z. B. innerhalb von Methoden.
Unter normalen Umständen sollten Sie die Konstante innerhalb der Klasse selbst definieren:
Wenn Sie aus irgendeinem Grund wirklich eine Konstante innerhalb einer Methode definieren müssen (möglicherweise für eine Art von Metaprogrammierung), können Sie Folgendes verwenden
const_set
:Auch hier
const_set
sollten Sie unter normalen Umständen nicht unbedingt darauf zurückgreifen müssen. Wenn Sie nicht sicher sind, ob Sie Konstanten auf diese Weise wirklich zuweisen möchten, sollten Sie eine der folgenden Alternativen in Betracht ziehen:Klassenvariablen
Klassenvariablen verhalten sich in vielerlei Hinsicht wie Konstanten. Sie sind Eigenschaften einer Klasse und in Unterklassen der Klasse, für die sie definiert sind, zugänglich.
Der Unterschied besteht darin, dass Klassenvariablen modifizierbar sein sollen und daher problemlos internen Methoden zugewiesen werden können.
Klassenattribute
Klassenattribute sind eine Art "Instanzvariable für eine Klasse". Sie verhalten sich ein bisschen wie Klassenvariablen, außer dass ihre Werte nicht mit Unterklassen geteilt werden.
Instanzvariablen
Und der Vollständigkeit halber sollte ich wahrscheinlich erwähnen: Wenn Sie einen Wert zuweisen müssen, der erst bestimmt werden kann, nachdem Ihre Klasse instanziiert wurde, besteht eine gute Chance, dass Sie tatsächlich nach einer einfachen alten Instanzvariablen suchen.
quelle
In Ruby ist jede Variable, deren Name mit einem Großbuchstaben beginnt, eine Konstante, die Sie nur einmal zuweisen können. Wählen Sie eine dieser Alternativen:
quelle
Konstanten in Ruby können nicht innerhalb von Methoden definiert werden. Siehe zum Beispiel die Hinweise unten auf dieser Seite
quelle
Sie können eine Variable nicht mit Großbuchstaben benennen, oder Ruby nimmt an, dass sie eine Konstante ist, und möchte, dass sie ihren Wert konstant hält. In diesem Fall wäre das Ändern ihres Werts ein Fehler, ein "dynamischer Konstantenzuweisungsfehler". Mit Kleinbuchstaben sollte in Ordnung sein
quelle
Ruby mag es nicht, dass Sie die Konstante innerhalb einer Methode zuweisen, da dies zu einer erneuten Zuweisung führen kann. Einige SO-Antworten vor mir bieten die Alternative, sie außerhalb einer Methode zuzuweisen - aber in der Klasse, die ein besserer Ort ist, um sie zuzuweisen.
quelle
Vielen Dank an Dorian und Phrogz, die mich an die Array- (und Hash-) Methode #replace erinnert haben, die "den Inhalt eines Arrays oder Hashs ersetzen kann".
Die Vorstellung, dass der Wert eines CONSTANT geändert werden kann, aber mit einer nervigen Warnung, ist einer der wenigen konzeptionellen Fehltritte von Ruby - diese sollten entweder vollständig unveränderlich sein oder die konstante Idee insgesamt verwerfen. Aus der Sicht eines Codierers ist eine Konstante deklarativ und beabsichtigt, ein Signal an andere, dass "dieser Wert nach der Deklaration / Zuweisung wirklich unveränderlich ist".
Aber manchmal schließt eine "offensichtliche Erklärung" tatsächlich andere, zukünftige nützliche Gelegenheiten aus. Beispielsweise...
Es gibt legitime Anwendungsfälle, in denen der Wert einer "Konstante" möglicherweise wirklich geändert werden muss: Zum Beispiel das erneute Laden von ARGV aus einer REPL-ähnlichen Eingabeaufforderungsschleife und das erneute Ausführen von ARGV über weitere (nachfolgende) OptionParser.parse! Anrufe - voila! Verleiht "Befehlszeilenargumenten" ein völlig neues dynamisches Dienstprogramm.
Das praktische Problem besteht entweder in der vermuteten Annahme, dass "ARGV eine Konstante sein muss", oder in der eigenen Initialisierungsmethode von optparse, die die Zuordnung von ARGV zur Instanz var @default_argv für die nachfolgende Verarbeitung fest codiert - dieses Array (ARGV) wirklich sollte ein Parameter sein, der gegebenenfalls eine erneute Analyse und Wiederverwendung fördert. Eine ordnungsgemäße Parametrisierung mit einem geeigneten Standard (z. B. ARGV) würde die Notwendigkeit vermeiden, das "konstante" ARGV jemals zu ändern. Nur ein paar Gedanken wert ...
quelle