動的定数割り当て


139
class MyClass
  def mymethod
    MYCONSTANT = "blah"
  end
end

私にエラーを与えます:

SyntaxError:動的定数割り当てエラー

なぜこれは動的定数と見なされるのですか?文字列を割り当てているだけです。


34
動的定数はドライウォーターのようなものですか?:)
fl00r 2011

39
定数が動的であるとは言いません。それは割り当てが動的であると言います。
sepp2k

回答:


141

問題は、メソッドを実行するたびに新しい値を定数に割り当てていることです。定数を非定数にするため、これは許可されません。文字列の内容は同じですが(今のところ、とりあえず)、メソッドが呼び出されるたびに実際の文字列オブジェクト自体は異なります。例えば:

def foo
  p "bar".object_id
end

foo #=> 15779172
foo #=> 15779112

おそらく、メソッドの定数の値を変更したいので、ユースケースを説明した場合は、より適切な実装を支援できます。

おそらく、クラスにインスタンス変数が必要でしょうか?

class MyClass
  class << self
    attr_accessor :my_constant
  end
  def my_method
    self.class.my_constant = "blah"
  end
end

p MyClass.my_constant #=> nil
MyClass.new.my_method

p MyClass.my_constant #=> "blah"

メソッド内の定数の値を本当に変更したい場合で、定数が文字列または配列である場合は、「チート」して#replaceメソッドを使用し、実際にオブジェクトを変更せずにオブジェクトに新しい値を適用させることができます。

class MyClass
  BAR = "blah"

  def cheat(new_bar)
    BAR.replace new_bar
  end
end

p MyClass::BAR           #=> "blah"
MyClass.new.cheat "whee"
p MyClass::BAR           #=> "whee"

19
OP は定数の値を変更したいとは言っていませんでしたが、値を割り当てたいだけでした。このRubyエラーにつながる頻繁な使用例は、他のランタイムアセット(変数、コマンドライン引数、ENV)からメソッドの値を構築するときですdef initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end。Rubyが単純な方法を持たないケースの1つです。
Arnaud Meuret 2013年

2
@ArnaudMeuretその場合@variable、定数ではなくインスタンス変数(など)が必要です。そうしないとDB、そのクラスの新しいインスタンスをインスタンス化するたびに再割り当てされます。
Ajedi32

2
@ Ajedi32この状況は通常、Sequelを使用した私の例のような設計上の選択ではなく、外部の制約から発生します。私のポイントは、定数への値の割り当ては、特定のスコープではRubyによって許可され、他のスコープでは許可されないということです。以前は、割り当てを実行するタイミングを賢く選択するのは開発者の責任でした。Rubyはこれを変更しました。誰にとっても良いことではありません。
Arnaud Meuret 2013

2
@ArnaudMeuret私はこれまでSequelを使ったことがないことを認めるので、100%の確実性でこれを言うことはできませんが、Sequelのドキュメントを一目見ただけではSequel.connect、DBという名前の定数に結果を割り当てる必要があるとは何も表示されません。。実際、ドキュメントには、これは単なる推奨事項であると明示されています。それは私にとって外部の制約のようには聞こえません。
Ajedi32 2013

@ Ajedi32 1)私はそれを書いたことがありません(定数の名前、またはどこかに保存する必要があったことも)それは単なる例です2)動的なコンテキストになるまで、ソフトウェアに必要な情報がない可能性があるという制約。
Arnaud Meuret 2014

69

Rubyの定数は変更するためのものではないため、Rubyでは、メソッド内など、複数回実行される可能性のあるコードの一部に定数を割り当てないようにしてください。

通常の状況では、クラス自体の内部で定数を定義する必要があります。

class MyClass
  MY_CONSTANT = "foo"
end

MyClass::MY_CONSTANT #=> "foo"

何らかの理由でメソッド内に定数を定義する必要がある場合(おそらく、ある種のメタプログラミングの場合)、次のように使用できますconst_set

class MyClass
  def my_method
    self.class.const_set(:MY_CONSTANT, "foo")
  end
end

MyClass::MY_CONSTANT
#=> NameError: uninitialized constant MyClass::MY_CONSTANT

MyClass.new.my_method
MyClass::MY_CONSTANT #=> "foo"

繰り返しますが、これconst_setは通常の状況で実際に使用する必要があるものではありません。あなたがいるかどうかわからない場合は、本当に定数にこの方法を割り当てることにしたい、あなたは次の選択肢のいずれかを検討する必要があります。

クラス変数

クラス変数は、多くの点で定数のように動作します。これらはクラスのプロパティであり、定義されているクラスのサブクラスでアクセスできます。

違いは、クラス変数は変更可能であるため、問題なく内部メソッドに割り当てることができることです。

class MyClass
  def self.my_class_variable
    @@my_class_variable
  end
  def my_method
    @@my_class_variable = "foo"
  end
end
class SubClass < MyClass
end

MyClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClass
SubClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClass

MyClass.new.my_method
MyClass.my_class_variable #=> "foo"
SubClass.my_class_variable #=> "foo"

クラス属性

クラス属性は、一種の「クラスのインスタンス変数」です。それらの値はサブクラスと共有されないことを除いて、クラス変数のように動作します。

class MyClass
  class << self
    attr_accessor :my_class_attribute
  end
  def my_method
    self.class.my_class_attribute = "blah"
  end
end
class SubClass < MyClass
end

MyClass.my_class_attribute #=> nil
SubClass.my_class_attribute #=> nil

MyClass.new.my_method
MyClass.my_class_attribute #=> "blah"
SubClass.my_class_attribute #=> nil

SubClass.new.my_method
SubClass.my_class_attribute #=> "blah"

インスタンス変数

そして、完全を期すために、おそらく言及する必要があります。クラスがインスタンス化された後にのみ決定できる値を割り当てる必要がある場合、実際にプレーンな古いインスタンス変数を探している可能性が十分あります。

class MyClass
  attr_accessor :instance_variable
  def my_method
    @instance_variable = "blah"
  end
end

my_object = MyClass.new
my_object.instance_variable #=> nil
my_object.my_method
my_object.instance_variable #=> "blah"

MyClass.new.instance_variable #=> nil

33

Rubyでは、名前が大文字で始まる変数は定数であり、1回しか割り当てることができません。次のいずれかの方法を選択してください:

class MyClass
  MYCONSTANT = "blah"

  def mymethod
    MYCONSTANT
  end
end

class MyClass
  def mymethod
    my_constant = "blah"
  end
end

2
「大文字で始まる名前の変数は定数です!」
ubienewbie


0

大文字で変数に名前を付けることはできません。そうでない場合、Rubyはその定数を想定し、その値を一定に保つことを望みます。その場合、値の変更は「動的定数割り当てエラー」のエラーになります。小文字で結構です

class MyClass
  def mymethod
    myconstant = "blah"
  end
end

0

Rubyは、メソッド内で定数を割り当てることを好みません。再割り当てのリスクがあるからです。私の前のいくつかのSOの答えは、メソッドの外でそれを割り当てる代わりの方法を示していますが、クラスでは、それを割り当てるのにより良い場所です。


1
SOジョンへのウェイカム。Yoは、あなたが説明している内容のサンプルコードを追加して、この回答の改善を検討するかもしれません。
クレプタス2018

0

「配列またはハッシュの内容を置き換える」ことができる配列(およびハッシュ)メソッド#replaceについて思い出してくれたDorianとPhrogzに感謝します。

CONSTANTの値は変更できるが、迷惑な警告が表示されるという概念は、Rubyのいくつかの概念的なミスステップの1つです。これらは完全に不変であるか、または一定のアイデアを完全にダンプする必要があります。コーダーの観点から見ると、定数は宣言的で意図的なものであり、「この値は、宣言または割り当てられた後は、実際には変更できない」というシグナルです。

しかし、「明白な宣言」が他の将来の有用な機会を実際に排除することもあります。例えば...

「定数」の値を実際に変更する必要がある正当なユースケースがあります。たとえば、REPLのようなプロンプトループからARGVを再ロードしてから、さらに(後続の)OptionParser.parseを介してARGVを再実行します。コール-出来上がり!「コマンドライン引数」にまったく新しい動的ユーティリティを提供します。

実用上の問題は、いずれかの「ARGVは一定でなければならない」という推定仮定して、またはその後の処理のインスタンスのvar @default_argvにどのハードコード、optparseは自身の初期化方法でARGVの割り当て-その配列(ARGV)本当に必要に応じて、再解析と再利用を促進するパラメーターにする必要があります。適切なデフォルト(たとえば、ARGV)を使用した適切なパラメーター化により、「定数」ARGVを変更する必要がなくなります。ちょうど2¢相当の考え...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.