Constant Constant adalah sesuatu seperti Air Kering? :)
fl00r
39
Itu tidak mengatakan bahwa konstanta itu dinamis. Dikatakan bahwa tugasnya dinamis.
sepp2k
Jawaban:
141
Masalah Anda adalah bahwa setiap kali Anda menjalankan metode, Anda menetapkan nilai baru ke konstanta. Ini tidak diperbolehkan, karena membuat konstan tidak konstan; meskipun isi string sama (untuk saat ini, bagaimanapun), objek string yang sebenarnya itu sendiri berbeda setiap kali metode ini dipanggil. Sebagai contoh:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Mungkin jika Anda menjelaskan kasus penggunaan Anda - mengapa Anda ingin mengubah nilai konstanta dalam suatu metode - kami dapat membantu Anda dengan implementasi yang lebih baik.
Mungkin Anda lebih suka memiliki variabel instan di kelas?
classMyClassclass<<self
attr_accessor :my_constant
enddef my_method
self.class.my_constant ="blah"endend
p MyClass.my_constant #=> nilMyClass.new.my_method
p MyClass.my_constant #=> "blah"
Jika Anda benar - benar ingin mengubah nilai konstanta dalam sebuah metode, dan konstanta Anda adalah sebuah String atau Array, Anda dapat 'menipu' dan menggunakan #replacemetode ini untuk menyebabkan objek mengambil nilai baru tanpa benar-benar mengubah objek:
classMyClass
BAR ="blah"def cheat(new_bar)
BAR.replace new_bar
endend
p MyClass::BAR #=> "blah"MyClass.new.cheat "whee"
p MyClass::BAR #=> "whee"
OP tidak pernah mengatakan dia ingin mengubah nilai konstanta tetapi hanya ingin memberikan nilai. Kasus penggunaan yang sering menyebabkan kesalahan Ruby ini adalah ketika Anda membangun nilai dalam metode dari aset run-time lainnya (variabel, argumen baris perintah, ENV), biasanya dalam konstruktor misalnya def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end. Itu salah satu kasus di mana Ruby tidak memiliki cara sederhana.
Arnaud Meuret
2
@ArnaudMeuret Untuk kasus ini Anda menginginkan variabel instan (misal @variable), bukan konstanta. Kalau tidak, Anda akan menugaskan kembali DBsetiap kali Anda membuat instance baru dari kelas itu.
Ajedi32
2
@ Ajedi32 Situasi ini biasanya muncul dari kendala eksternal bukan pilihan desain seperti contoh saya dengan Sequel. Maksud saya adalah bahwa menetapkan nilai ke konstanta diizinkan oleh Ruby dalam lingkup tertentu dan bukan yang lain. Dulu tergantung pada pengembang untuk memilih dengan bijak kapan melakukan tugas. Ruby berubah pada ini. Tidak untuk kebaikan semua orang.
Arnaud Meuret
2
@ArnaudMeuret Saya akui saya belum pernah menggunakan Sequel sebelumnya jadi saya tidak bisa mengatakan ini dengan kepastian 100%, tetapi hanya melihat sekilas dokumentasi untuk Sequel. Saya tidak melihat apa pun yang mengatakan bahwa Anda HARUS menetapkan hasil Sequel.connectuntuk konstanta bernama DB . Bahkan, dokumentasi secara eksplisit mengatakan bahwa itu hanya rekomendasi. Itu tidak terdengar seperti kendala eksternal bagi saya.
Ajedi32
@ Ajedi32 1) Saya tidak pernah menulis itu (nama konstanta atau bahkan Anda harus menyimpannya di suatu tempat) itu hanya contoh 2) Kendala adalah bahwa perangkat lunak Anda mungkin tidak memiliki informasi yang diperlukan sampai Anda biasanya berada dalam konteks dinamis .
Arnaud Meuret
69
Karena konstanta di Ruby tidak dimaksudkan untuk diubah, Ruby tidak menyarankan Anda untuk menugaskan mereka dalam bagian kode yang mungkin dieksekusi lebih dari sekali, seperti metode di dalam.
Dalam keadaan normal, Anda harus mendefinisikan konstanta di dalam kelas itu sendiri:
Jika karena alasan tertentu Anda benar-benar perlu mendefinisikan konstanta di dalam suatu metode (mungkin untuk beberapa jenis pemrograman), Anda dapat menggunakan const_set:
Lagi-lagi, const_setbukan sesuatu yang harus benar-benar Anda gunakan dalam keadaan normal. Jika Anda tidak yakin apakah Anda benar - benar ingin ditugaskan ke konstanta dengan cara ini, Anda mungkin ingin mempertimbangkan salah satu alternatif berikut:
Variabel kelas
Variabel kelas berperilaku seperti konstanta dalam banyak hal. Mereka adalah properti di kelas, dan mereka dapat diakses di subclass dari kelas yang mereka tetapkan.
Perbedaannya adalah bahwa variabel kelas dimaksudkan untuk dapat dimodifikasi, dan karenanya dapat ditugaskan ke dalam metode tanpa masalah.
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_method
MyClass.my_class_variable #=> "foo"SubClass.my_class_variable #=> "foo"
Atribut kelas
Atribut kelas adalah semacam "variabel instan pada kelas". Mereka berperilaku sedikit seperti variabel kelas, kecuali bahwa nilainya tidak dibagi dengan subclass.
Dan hanya untuk kelengkapan yang mungkin harus saya sebutkan: jika Anda perlu menetapkan nilai yang hanya dapat ditentukan setelah kelas Anda dipakai, ada kemungkinan Anda benar-benar mencari variabel instan yang lama.
Di Ruby, variabel apa pun yang namanya dimulai dengan huruf kapital adalah konstan dan Anda hanya dapat menetapkan satu kali. Pilih salah satu dari alternatif ini:
Anda tidak dapat memberi nama variabel dengan huruf kapital atau Ruby akan menganggapnya sebagai konstanta dan ingin agar variabel itu tetap konstan, dalam hal ini mengubah nilainya akan menjadi kesalahan yang merupakan "kesalahan penetapan konstan yang dinamis". Dengan huruf kecil harus baik-baik saja
Ruby tidak suka bahwa Anda menetapkan konstanta di dalam metode karena berisiko penugasan kembali. Beberapa jawaban SO sebelum saya memberikan alternatif untuk menetapkannya di luar metode - tetapi di kelas, yang merupakan tempat yang lebih baik untuk menetapkannya.
Weicome untuk SO John. Anda dapat mempertimbangkan untuk meningkatkan jawaban ini dengan menambahkan beberapa kode contoh dari apa yang Anda gambarkan.
Cleptus
0
Terima kasih banyak kepada Dorian dan Phrogz untuk mengingatkan saya tentang metode array (dan hash) #replace, yang dapat "mengganti konten array atau hash."
Gagasan bahwa nilai KONSTAN dapat diubah, tetapi dengan peringatan yang menjengkelkan, adalah salah satu dari beberapa langkah keliru konseptual Ruby - ini harus sepenuhnya tidak berubah, atau membuang ide konstan sama sekali. Dari sudut pandang seorang pembuat kode, sebuah konstanta bersifat deklaratif dan disengaja, sebuah sinyal bagi yang lain bahwa "nilai ini benar-benar tidak dapat diubah begitu dideklarasikan / ditugaskan."
Tetapi kadang-kadang "deklarasi yang jelas" sebenarnya menyita kesempatan lain yang bermanfaat di masa depan. Sebagai contoh...
Ada yang kasus penggunaan yang sah di mana nilai "konstan ini" mungkin benar-benar perlu diubah: misalnya, re-loading ARGV dari prompt loop repl-seperti, kemudian Siarang ARGV melalui lebih (berikutnya) OptionParser.parse! panggilan - voila! Memberikan "command line args" sebuah utilitas dinamis yang sama sekali baru.
Masalah praktis adalah baik dengan asumsi dugaan bahwa "ARGV harus konstan", atau dalam metode initialize optparse sendiri, yang keras-kode penugasan ARGV ke @default_argv misalnya var untuk diproses selanjutnya - bahwa array (ARGV) benar-benar harus menjadi parameter, mendorong penguraian ulang dan penggunaan kembali, jika sesuai. Parameterisasi yang tepat, dengan standar yang sesuai (katakanlah, ARGV) akan menghindari keharusan untuk mengubah ARGV "konstan". Hanya sekitar 2 ¢ - layak dipikirkan ...
Jawaban:
Masalah Anda adalah bahwa setiap kali Anda menjalankan metode, Anda menetapkan nilai baru ke konstanta. Ini tidak diperbolehkan, karena membuat konstan tidak konstan; meskipun isi string sama (untuk saat ini, bagaimanapun), objek string yang sebenarnya itu sendiri berbeda setiap kali metode ini dipanggil. Sebagai contoh:
Mungkin jika Anda menjelaskan kasus penggunaan Anda - mengapa Anda ingin mengubah nilai konstanta dalam suatu metode - kami dapat membantu Anda dengan implementasi yang lebih baik.
Mungkin Anda lebih suka memiliki variabel instan di kelas?
Jika Anda benar - benar ingin mengubah nilai konstanta dalam sebuah metode, dan konstanta Anda adalah sebuah String atau Array, Anda dapat 'menipu' dan menggunakan
#replace
metode ini untuk menyebabkan objek mengambil nilai baru tanpa benar-benar mengubah objek:sumber
def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end
. Itu salah satu kasus di mana Ruby tidak memiliki cara sederhana.@variable
), bukan konstanta. Kalau tidak, Anda akan menugaskan kembaliDB
setiap kali Anda membuat instance baru dari kelas itu.Sequel.connect
untuk konstanta bernama DB . Bahkan, dokumentasi secara eksplisit mengatakan bahwa itu hanya rekomendasi. Itu tidak terdengar seperti kendala eksternal bagi saya.Karena konstanta di Ruby tidak dimaksudkan untuk diubah, Ruby tidak menyarankan Anda untuk menugaskan mereka dalam bagian kode yang mungkin dieksekusi lebih dari sekali, seperti metode di dalam.
Dalam keadaan normal, Anda harus mendefinisikan konstanta di dalam kelas itu sendiri:
Jika karena alasan tertentu Anda benar-benar perlu mendefinisikan konstanta di dalam suatu metode (mungkin untuk beberapa jenis pemrograman), Anda dapat menggunakan
const_set
:Lagi-lagi,
const_set
bukan sesuatu yang harus benar-benar Anda gunakan dalam keadaan normal. Jika Anda tidak yakin apakah Anda benar - benar ingin ditugaskan ke konstanta dengan cara ini, Anda mungkin ingin mempertimbangkan salah satu alternatif berikut:Variabel kelas
Variabel kelas berperilaku seperti konstanta dalam banyak hal. Mereka adalah properti di kelas, dan mereka dapat diakses di subclass dari kelas yang mereka tetapkan.
Perbedaannya adalah bahwa variabel kelas dimaksudkan untuk dapat dimodifikasi, dan karenanya dapat ditugaskan ke dalam metode tanpa masalah.
Atribut kelas
Atribut kelas adalah semacam "variabel instan pada kelas". Mereka berperilaku sedikit seperti variabel kelas, kecuali bahwa nilainya tidak dibagi dengan subclass.
Variabel instan
Dan hanya untuk kelengkapan yang mungkin harus saya sebutkan: jika Anda perlu menetapkan nilai yang hanya dapat ditentukan setelah kelas Anda dipakai, ada kemungkinan Anda benar-benar mencari variabel instan yang lama.
sumber
Di Ruby, variabel apa pun yang namanya dimulai dengan huruf kapital adalah konstan dan Anda hanya dapat menetapkan satu kali. Pilih salah satu dari alternatif ini:
sumber
Konstanta dalam ruby tidak dapat didefinisikan di dalam metode. Lihat catatan di bagian bawah halaman ini, misalnya
sumber
Anda tidak dapat memberi nama variabel dengan huruf kapital atau Ruby akan menganggapnya sebagai konstanta dan ingin agar variabel itu tetap konstan, dalam hal ini mengubah nilainya akan menjadi kesalahan yang merupakan "kesalahan penetapan konstan yang dinamis". Dengan huruf kecil harus baik-baik saja
sumber
Ruby tidak suka bahwa Anda menetapkan konstanta di dalam metode karena berisiko penugasan kembali. Beberapa jawaban SO sebelum saya memberikan alternatif untuk menetapkannya di luar metode - tetapi di kelas, yang merupakan tempat yang lebih baik untuk menetapkannya.
sumber
Terima kasih banyak kepada Dorian dan Phrogz untuk mengingatkan saya tentang metode array (dan hash) #replace, yang dapat "mengganti konten array atau hash."
Gagasan bahwa nilai KONSTAN dapat diubah, tetapi dengan peringatan yang menjengkelkan, adalah salah satu dari beberapa langkah keliru konseptual Ruby - ini harus sepenuhnya tidak berubah, atau membuang ide konstan sama sekali. Dari sudut pandang seorang pembuat kode, sebuah konstanta bersifat deklaratif dan disengaja, sebuah sinyal bagi yang lain bahwa "nilai ini benar-benar tidak dapat diubah begitu dideklarasikan / ditugaskan."
Tetapi kadang-kadang "deklarasi yang jelas" sebenarnya menyita kesempatan lain yang bermanfaat di masa depan. Sebagai contoh...
Ada yang kasus penggunaan yang sah di mana nilai "konstan ini" mungkin benar-benar perlu diubah: misalnya, re-loading ARGV dari prompt loop repl-seperti, kemudian Siarang ARGV melalui lebih (berikutnya) OptionParser.parse! panggilan - voila! Memberikan "command line args" sebuah utilitas dinamis yang sama sekali baru.
Masalah praktis adalah baik dengan asumsi dugaan bahwa "ARGV harus konstan", atau dalam metode initialize optparse sendiri, yang keras-kode penugasan ARGV ke @default_argv misalnya var untuk diproses selanjutnya - bahwa array (ARGV) benar-benar harus menjadi parameter, mendorong penguraian ulang dan penggunaan kembali, jika sesuai. Parameterisasi yang tepat, dengan standar yang sesuai (katakanlah, ARGV) akan menghindari keharusan untuk mengubah ARGV "konstan". Hanya sekitar 2 ¢ - layak dipikirkan ...
sumber