Dinamik sabit atama


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

bana hata veriyor:

Sözdizimi Hatası: dinamik sabit atama hatası

Bu neden dinamik bir sabit olarak kabul edilir? Ben sadece ona bir dize veriyorum.


34
Dinamik Sabit Kuru Su gibi bir şey mi? :)
fl00r

39
Sabitin dinamik olduğu söylenemez. Ödevin dinamik olduğunu söylüyor.
sepp2k

Yanıtlar:


141

Sorun, yöntemi her çalıştırdığınızda sabit için yeni bir değer atamak olmasıdır. Sabit olmamasını sağladığı için buna izin verilmez; dizenin içeriği aynı olmasına rağmen (şu an için, her neyse), yöntem her çağrıldığında gerçek dize nesnesinin kendisi farklıdır. Örneğin:

def foo
  p "bar".object_id
end

foo #=> 15779172
foo #=> 15779112

Belki de kullanım durumunuzu (bir yöntemdeki sabitin değerini neden değiştirmek istediğinizi) açıkladıysanız, size daha iyi bir uygulamada yardımcı olabiliriz.

Belki de sınıfta bir örnek değişkeni mi tercih edersiniz?

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"

Eğer varsa gerçekten bir yöntemde bir sabit değerini değiştirmek istiyorum, ve sürekli bir dize veya bir Array, sen 'hile' ve kullanabilirsiniz #replaceaslında nesneyi değiştirmeden yeni bir değer almak için nesneyi neden yöntemini:

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 , sabitin değerini değiştirmek istediğini, ancak sadece bir değer atamak istediğini söylemedi . Bu Ruby hatasına yol açan sık kullanım durumu, değeri diğer çalışma zamanı varlıklarından (değişkenler, komut satırı bağımsız değişkenleri, ENV) bir yöntemde, genellikle bir kurucuda oluşturduğunuz zamandır def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end. Ruby'nin basit bir yolu olmadığı durumlardan biri.
Arnaud Meuret

2
@ArnaudMeuret Bu durumda @variablesabit değil, bir örnek değişkeni (örn. ) İstiyorsunuz . Aksi takdirde DB, söz konusu sınıfın her yeni örneğini her başlatışınızda yeniden atarsınız.
Ajedi32

2
@ Ajedi32 Bu durum genellikle Sequel ile ilgili örneğim gibi seçimler tasarlamayan dış kısıtlamalardan kaynaklanır. Demek istediğim, sabite bir değer atamanın Ruby tarafından bazı kapsamlarda değil diğer kapsamlarda olmasına izin verilmesi. Ödevin ne zaman gerçekleştirileceğini akıllıca seçmek için geliştiriciye bağlıydı. Ruby bu konuda değişti. Herkes için iyi değil.
Arnaud Meuret

2
@ArnaudMeuret Daha önce hiç Sequel kullanmadığımı itiraf edeceğim, bu yüzden bunu% 100 kesin olarak söyleyemem, ama sadece Sequel için belgelere göz atarak sonucunu Sequel.connectDB adlı bir sabite atamanız gerektiğini söyleyen hiçbir şey görmüyorum . Aslında, belgeler açıkça bunun sadece bir tavsiye olduğunu söylüyor. Bu bana dışsal bir kısıt gibi gelmiyor.
Ajedi32

@ Ajedi32 1) Bunu hiç yazmadım (sabitin adı veya hatta bir yerde tutmak zorunda kaldığın) sadece bir örnek 2) Kısıtlama, yazılımınızın genellikle dinamik bir bağlamda olana kadar gerekli bilgilere sahip olmaması olabilir .
Arnaud Meuret

69

Ruby'deki sabitlerin değiştirilmesi amaçlanmadığı için Ruby, kodları iç kodlar gibi birden fazla çalıştırılabilecek kod bölümlerine atamanızı önler.

Normal şartlar altında, sınıfın içindeki sabiti tanımlamanız gerekir:

class MyClass
  MY_CONSTANT = "foo"
end

MyClass::MY_CONSTANT #=> "foo"

Herhangi bir nedenle bir yöntemin içinde bir sabit tanımlamanız gerekiyorsa (belki de bir tür metaprogramlama için), şunları kullanabilirsiniz 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"

Yine de, const_setnormal şartlar altında başvurmanız gereken bir şey değildir. Sabitlere gerçekten bu şekilde atamak isteyip istemediğinizden emin değilseniz , aşağıdaki alternatiflerden birini düşünmek isteyebilirsiniz:

Sınıf değişkenleri

Sınıf değişkenleri birçok şekilde sabit gibi davranır. Bir sınıfın özellikleridir ve tanımlandıkları sınıfın alt sınıflarında erişilebilirler.

Aradaki fark, sınıf değişkenlerinin değiştirilebilir olması ve bu nedenle sorunsuz bir şekilde iç yöntemlere atanabilmeleridir.

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"

Sınıf özellikleri

Sınıf öznitelikleri bir çeşit "sınıftaki örnek değişkeni" dir. Değerlerinin alt sınıflarla paylaşılmaması dışında, sınıf değişkenleri gibi davranırlar.

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"

Örnek değişkenler

Ve sadece bütünlük için bahsetmeliyim: sadece sınıfınız başlatıldıktan sonra belirlenebilecek bir değer atamanız gerekiyorsa, aslında eski bir düz örnek değişkeni aramak için iyi bir şans var.

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'de, adı büyük harfle başlayan herhangi bir değişken sabittir ve buna yalnızca bir kez atayabilirsiniz. Aşağıdaki alternatiflerden birini seçin:

class MyClass
  MYCONSTANT = "blah"

  def mymethod
    MYCONSTANT
  end
end

class MyClass
  def mymethod
    my_constant = "blah"
  end
end

2
Çok şükür ki birisi "adı büyük harfle başlayan herhangi bir değişken sabittir!"
ubienewbie


0

Büyük harfli bir değişkeni adlandıramazsınız ya da Ruby bir sabiti alır ve değerini sabit tutmasını ister, bu durumda değerini değiştirmek bir "dinamik sabit atama hatası" hatası olur. Küçük harf ile iyi olmalı

class MyClass
  def mymethod
    myconstant = "blah"
  end
end

0

Ruby, bir yöntemin içindeki sabiti atadığınızdan hoşlanmaz çünkü yeniden atama riski vardır. Benden önce birkaç SO yanıtı, bir yöntemin dışında atama alternatifi veriyor - ancak sınıfta, atamak için daha iyi bir yer.


1
SO John için Weicome. Açıkladığınız şeyin bazı örnek kodlarını ekleyerek bu yanıtı geliştirmeyi düşünebilirsiniz.
Cleptus

0

Dorian ve Phrogz'a, "bir dizinin veya karma'nın içeriğini değiştirebilecek" dizi (ve karma) yöntemi hakkında hatırlattığı için çok teşekkürler.

SABİT'in değerinin değiştirilebileceği, ancak can sıkıcı bir uyarıyla, Ruby'nin birkaç kavramsal yanlış adımından biridir - bunlar tamamen değişmez olmalı veya sabit fikri tamamen dökmelidir. Bir kodlayıcının bakış açısından, bir sabit bildirimsel ve kasıtlıdır, diğerine "bu değer beyan edildiğinde / atandığında gerçekten değişmezdir" sinyalidir.

Ancak bazen "açık bir deklarasyon" aslında gelecekteki diğer faydalı fırsatları da öngörür. Örneğin...

Orada vardır örneğin bir REPL benzeri istemi-döngüden yeniden yükleme ARGV, daha sonra (sonradan) OptionParser.parse aracılığıyla argv rerunning: Bir "sabitin" değeri gerçekten değiştirilmesi gerekebilir geçerli durumlar! çağrıları - voila! Tamamen yeni bir dinamik yardımcı program olan "komut satırı argümanları" verir.

Pratik problem ya "ARGV'nin sabit olması gerektiği" varsayımı ile, veya optparse kendi initialize yönteminde, hangi sert kodları sonraki işlemler için örnek var @default_argv için ARGV atama - gerçekten bu dizi (ARGV) uygun olduğunda yeniden ayrıştırma ve yeniden kullanımı teşvik eden bir parametre olmalıdır. Uygun bir varsayılan değerle (örneğin, ARGV) uygun parametrelendirme, "sabit" ARGV'yi değiştirme ihtiyacını ortadan kaldırır. Sadece bazı 2 ¢-düşünceleri değer ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.