Apakah Ruby lulus dengan referensi atau berdasarkan nilai?

248
@user.update_languages(params[:language][:language1], 
                       params[:language][:language2], 
                       params[:language][:language3])
lang_errors = @user.errors
logger.debug "--------------------LANG_ERRORS----------101-------------" 
                + lang_errors.full_messages.inspect

if params[:user]
  @user.state = params[:user][:state]
  success = success & @user.save
end
logger.debug "--------------------LANG_ERRORS-------------102----------" 
                + lang_errors.full_messages.inspect

if lang_errors.full_messages.empty?

@userobjek menambahkan kesalahan ke lang_errorsvariabel dalam update_lanugagesmetode. ketika saya melakukan save pada @userobjek saya kehilangan kesalahan yang awalnya disimpan dalam lang_errorsvariabel.

Meskipun apa yang saya coba lakukan akan lebih merupakan peretasan (yang sepertinya tidak berfungsi). Saya ingin memahami mengapa nilai variabel terhapus. Saya mengerti lewat referensi jadi saya ingin tahu bagaimana nilai dapat disimpan dalam variabel itu tanpa terhapus.

Sid
sumber
Saya juga memperhatikan bahwa saya dapat mempertahankan nilai itu dalam objek yang dikloning
Sid
1
Anda harus melihat jawaban Abe Voelker. Tapi setelah berlari di sekitar blok ini, inilah bagaimana saya akan mengatakannya. ketika Anda melewati objek Foo ke prosedur, salinan referensi ke objek dilewatkan, bar, Lewati nilai. Anda tidak dapat mengubah objek yang ditunjuk Foo, tetapi Anda dapat mengubah konten objek yang ditunjuknya. Jadi jika Anda melewatkan array, isi array bisa diubah, tetapi Anda tidak bisa mengubah array apa yang sedang direferensikan. senang bisa menggunakan metode Foo tanpa harus khawatir mengacaukan dependensi lain pada Foo.
bobbdelsol

Jawaban:

244

Dalam terminologi tradisional, Ruby benar-benar merupakan nilai tambah . Tapi bukan itu yang sebenarnya kamu tanyakan di sini.

Ruby tidak memiliki konsep nilai murni, non-referensi, jadi Anda tentu tidak dapat meneruskannya ke suatu metode. Variabel selalu referensi ke objek. Untuk mendapatkan objek yang tidak akan berubah dari bawah Anda, Anda perlu menggandakan atau mengkloning objek yang Anda lewati, sehingga memberikan objek yang tidak memiliki referensi orang lain. (Meskipun ini bukan anti peluru) - kedua metode kloning standar melakukan salinan dangkal, jadi variabel instance dari klon masih menunjuk ke objek yang sama dengan yang asli lakukan. Jika objek yang dirujuk oleh ivars bermutasi, itu akan masih muncul dalam salinan, karena itu merujuk objek yang sama.)

Membuang
sumber
88
Ruby adalah nilai per nilai . Tidak ada seandainya. Tidak ada tapi Tidak ada pengecualian. Jika Anda ingin tahu apakah Ruby (atau bahasa lainnya) adalah pass-by-referensi atau pass-by-value , hanya mencobanya: def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}".
Jörg W Mittag
95
@ JörgWMittag: Ya, tapi kebingungan OP sebenarnya bukan pass-by-value atau pass-by-reference dalam arti kata-kata CS yang ketat. Apa yang dia lewatkan adalah bahwa "nilai" yang Anda sampaikan adalah referensi. Saya merasa bahwa hanya mengatakan "Ini nilai-by-value" akan menjadi sangat mahal dan membuat OP merugikan, karena itu sebenarnya bukan yang ia maksudkan. Tetapi terima kasih atas klarifikasi, karena ini penting untuk pembaca masa depan dan saya harus memasukkannya. (Saya selalu terbelah antara memasukkan lebih banyak info dan tidak membingungkan orang.)
Chuck
16
Tidak setuju dengan @Jorg. Ruby lulus dengan referensi, dia hanya mengubah referensi. Coba ini sebagai gantinya: def foo (bar) bar. Ganti 'referensi' akhir; baz = 'nilai'; foo (baz); menempatkan "Ruby pass-by - # {baz}"
pguardiario
15
@ pguardiario: Saya pikir itu benar-benar hanya masalah definisi. Anda menggunakan definisi "pass-by-reference" yang telah Anda buat sendiri, sementara Jörg menggunakan definisi ilmu komputer tradisional. Tentu saja, bukan urusan saya untuk memberi tahu Anda bagaimana menggunakan kata-kata - Saya pikir penting untuk menjelaskan apa arti istilah itu secara normal . Dalam terminologi tradisional, Ruby adalah nilai per nilai, tetapi nilai itu sendiri adalah referensi. Saya benar-benar mengerti mengapa Anda dan OP suka menganggap ini sebagai pass-by-reference - itu hanya bukan makna tradisional dari istilah tersebut.
Chuck
7
Semua yang ada di Ruby adalah objek, jadi Ruby bukanlah pass by value atau pass by reference, setidaknya dalam arti istilah tersebut digunakan dalam C ++. "lewat referensi objek" mungkin cara yang lebih baik untuk menggambarkan apa yang dilakukan Ruby. Namun pada akhirnya, taruhan terbaik adalah jangan terlalu banyak memaknai istilah-istilah ini, dan cukup pahami perilaku yang benar-benar terjadi.
David Winiecki
424

Penjawab lainnya semuanya benar, tetapi seorang teman meminta saya untuk menjelaskan ini kepadanya dan apa yang sebenarnya menjadi intinya adalah bagaimana Ruby menangani variabel, jadi saya pikir saya akan membagikan beberapa gambar / penjelasan sederhana yang saya tulis untuknya (permintaan maaf untuk panjangnya) dan mungkin beberapa penyederhanaan berlebihan):


T1: Apa yang terjadi ketika Anda menetapkan variabel baru strke nilai 'foo'?

str = 'foo'
str.object_id # => 2000

masukkan deskripsi gambar di sini

A: Label yang dipanggil strdibuat yang menunjuk ke objek 'foo', yang untuk keadaan interpreter Ruby ini berada di lokasi memori 2000.


T2: Apa yang terjadi ketika Anda menetapkan variabel yang ada strke objek baru menggunakan =?

str = 'bar'.tap{|b| puts "bar: #{b.object_id}"} # bar: 2002
str.object_id # => 2002

masukkan deskripsi gambar di sini

A: Label strsekarang menunjuk ke objek yang berbeda.


Q3: Apa yang terjadi ketika Anda menetapkan variabel baru =ke str?

str2 = str
str2.object_id # => 2002

masukkan deskripsi gambar di sini

A: Label baru bernama str2dibuat yang menunjuk pada objek yang sama dengan str.


T4: Apa yang terjadi jika objek direferensikan oleh strdan str2diubah?

str2.replace 'baz'
str2 # => 'baz'
str  # => 'baz'
str.object_id # => 2002
str2.object_id # => 2002

masukkan deskripsi gambar di sini

A: Kedua label masih menunjuk pada objek yang sama, tetapi objek itu sendiri telah bermutasi (isinya telah berubah menjadi sesuatu yang lain).


Bagaimana ini berhubungan dengan pertanyaan awal?

Ini pada dasarnya sama dengan apa yang terjadi di Q3 / Q4; metode mendapatkan salinan pribadi dari variabel / label ( str2) yang diteruskan ke sana ( str). Itu tidak dapat mengubah objek yang str ditunjuk label , tetapi itu dapat mengubah konten objek yang keduanya rujuk mengandung yang lain:

str = 'foo'

def mutate(str2)
  puts "str2: #{str2.object_id}"
  str2.replace 'bar'
  str2 = 'baz'
  puts "str2: #{str2.object_id}"
end

str.object_id # => 2004
mutate(str) # str2: 2004, str2: 2006
str # => "bar"
str.object_id # => 2004
Abe Voelker
sumber
1
Robert Heaton juga membuat blog tentang ini akhir-akhir ini: robertheaton.com/2014/07/22/...
Michael Renner
48

Ruby menggunakan "referensi objek lewat"

(Menggunakan terminologi Python.)

Untuk mengatakan Ruby menggunakan "pass by value" atau "pass by reference" tidak cukup deskriptif untuk membantu. Saya pikir karena kebanyakan orang tahu hari ini, bahwa terminologi ("nilai" vs "referensi") berasal dari C ++.

Dalam C ++, "pass by value" berarti fungsi mendapatkan salinan variabel dan setiap perubahan pada salinan tidak mengubah yang asli. Itu juga berlaku untuk objek. Jika Anda melewatkan variabel objek berdasarkan nilai, maka seluruh objek (termasuk semua anggotanya) akan disalin dan perubahan apa pun pada anggota tidak mengubah anggota tersebut pada objek asli. (Ini berbeda jika Anda melewatkan pointer dengan nilai tetapi Ruby tetap tidak memiliki pointer, AFAIK.)

class A {
  public:
    int x;
};

void inc(A arg) {
  arg.x++;
  printf("in inc: %d\n", arg.x); // => 6
}

void inc(A* arg) {
  arg->x++;
  printf("in inc: %d\n", arg->x); // => 1
}

int main() {
  A a;
  a.x = 5;
  inc(a);
  printf("in main: %d\n", a.x); // => 5

  A* b = new A;
  b->x = 0;
  inc(b);
  printf("in main: %d\n", b->x); // => 1

  return 0;
}

Keluaran:

in inc: 6
in main: 5
in inc: 1
in main: 1

Dalam C ++, "lulus dengan referensi" berarti fungsi mendapatkan akses ke variabel asli. Itu dapat menetapkan integer literal baru dan variabel asli kemudian akan memiliki nilai itu juga.

void replace(A &arg) {
  A newA;
  newA.x = 10;
  arg = newA;
  printf("in replace: %d\n", arg.x);
}

int main() {
  A a;
  a.x = 5;
  replace(a);
  printf("in main: %d\n", a.x);

  return 0;
}

Keluaran:

in replace: 10
in main: 10

Ruby menggunakan pass by value (dalam pengertian C ++) jika argumennya bukan objek. Tapi di Ruby semuanya adalah objek, jadi benar-benar tidak ada nilai yang lewat dalam pengertian C ++ di Ruby.

Di Ruby, "lewat referensi objek" (untuk menggunakan terminologi Python) digunakan:

  • Di dalam fungsi, anggota objek mana pun dapat memiliki nilai baru yang ditugaskan kepadanya dan perubahan ini akan bertahan setelah fungsi kembali. *
  • Di dalam fungsi, menugaskan objek yang sama sekali baru ke variabel menyebabkan variabel berhenti referensi objek lama. Tetapi setelah fungsi kembali, variabel asli masih akan merujuk objek lama.

Karenanya Ruby tidak menggunakan "pass by reference" dalam pengertian C ++. Jika ya, maka menetapkan objek baru ke variabel di dalam fungsi akan menyebabkan objek lama dilupakan setelah fungsi kembali.

class A
  attr_accessor :x
end

def inc(arg)
  arg.x += 1
  puts arg.x
end

def replace(arg)
  arg = A.new
  arg.x = 3
  puts arg.x
end

a = A.new
a.x = 1
puts a.x  # 1

inc a     # 2
puts a.x  # 2

replace a # 3
puts a.x  # 2

puts ''

def inc_var(arg)
  arg += 1
  puts arg
end

b = 1     # Even integers are objects in Ruby
puts b    # 1
inc_var b # 2
puts b    # 1

Keluaran:

1
2
2
3
2

1
2
1

* Inilah sebabnya, di Ruby, jika Anda ingin memodifikasi objek di dalam suatu fungsi tetapi melupakan perubahan itu ketika fungsi kembali, maka Anda harus secara eksplisit membuat salinan objek sebelum membuat perubahan sementara pada salinan.

David Winiecki
sumber
Jawaban Anda adalah yang terbaik. Saya juga ingin memposting contoh sederhana def ch(str) str.reverse! end; str="abc"; ch(str); puts str #=> "cba"
fangxing
Ini jawaban yang benar! Ini juga dijelaskan dengan sangat baik di sini: robertheaton.com/2014/07/22/… . Tapi apa yang saya masih tidak mengerti adalah ini: def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}". Ini mencetak "Ruby is by-value". Tetapi variabel di dalamnya foodipindahkan. Jika barakan menjadi array, penugasan kembali tidak akan berpengaruh baz. Mengapa?
haffla
Saya tidak mengerti pertanyaan Anda. Saya pikir Anda harus mengajukan pertanyaan yang sama sekali baru alih-alih bertanya dalam komentar di sini.
David Winiecki
42

Apakah Ruby lulus dengan referensi atau berdasarkan nilai?

Ruby adalah nilai per nilai. Selalu. Tidak ada pengecualian. Tidak ada seandainya. Tidak ada tapi

Berikut ini adalah program sederhana yang menunjukkan fakta itu:

def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value
Jörg W Mittag
sumber
15
@DavidJ .: "kesalahan di sini adalah bahwa parameter lokal ditugaskan kembali (menunjuk ke tempat baru di memori)" - Itu bukan kesalahan, itulah definisi dari pass-by-value . Jika Ruby adalah pass-by-reference, maka penugasan kembali ke argumen metode lokal yang mengikat dalam callee juga akan menugaskan ulang variabel lokal yang mengikat dalam penelepon. Yang mana tidak. Ergo, Ruby adalah nilai tambah. Fakta bahwa jika Anda mengubah nilai yang bisa berubah, perubahan nilai itu sama sekali tidak relevan, itu hanya cara kerja yang bisa berubah. Ruby bukan bahasa fungsional murni.
Jörg W Mittag
5
Terima kasih kepada Jörg untuk mempertahankan definisi sebenarnya dari "pass-by-value". Jelas melelehkan otak kita ketika nilainya sebenarnya merupakan referensi, meskipun ruby ​​selalu melewati nilai.
Douglas
9
Ini menyesatkan. Perbedaan praktis antara "pass by value" dan "pass by reference" adalah semantik, bukan sintaksis. Apakah Anda akan mengatakan bahwa array C bernilai pass-by-value? Tentu saja tidak, meskipun saat Anda meneruskan nama array ke suatu fungsi, Anda meneruskan pointer yang tidak dapat diubah, dan hanya data yang dirujuk oleh pointer tersebut yang dapat dimutasi. Jelas tipe nilai dalam Ruby diteruskan oleh nilai, dan tipe referensi diteruskan oleh referensi.
dodgethesteamroller
3
@dodgethesteamroller: Ruby dan C sama-sama bernilai. Selalu. Tidak ada pengecualian, tidak ada tapi tidak ada. Perbedaan antara pass-by-value dan pass-by-reference adalah apakah Anda meneruskan nilai yang ditunjuk oleh referensi atau meneruskan referensi. C selalu melewati nilai, tidak pernah referensi. Nilai mungkin atau mungkin tidak pointer, tapi apa nilai tersebut tidak relevan dengan apakah itu sedang lewat di tempat pertama. Ruby juga selalu melewati nilai, tidak pernah referensi. Nilai itu selalu berupa pointer, tetapi sekali lagi, itu tidak relevan.
Jörg W Mittag
44
Jawaban ini, meskipun benar , benar , tidak terlalu berguna . Fakta bahwa nilai yang diteruskan selalu menjadi penunjuk tidak tidak relevan. Ini adalah sumber kebingungan bagi orang-orang yang mencoba belajar, dan jawaban Anda sama sekali tidak membantu kebingungan itu.
20

Ruby adalah pass-by-value dalam arti yang ketat, TETAPI nilainya adalah referensi.

Ini bisa disebut " pass-reference-by-value ". Artikel ini memiliki penjelasan terbaik yang pernah saya baca: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/

Pass-reference-by-value secara singkat dapat dijelaskan sebagai berikut:

Suatu fungsi menerima referensi ke (dan akan mengakses) objek yang sama dalam memori seperti yang digunakan oleh pemanggil. Namun, itu tidak menerima kotak di mana pemanggil menyimpan objek ini di; seperti pada pass-value-by-value, fungsi menyediakan kotaknya sendiri dan membuat variabel baru untuk dirinya sendiri.

Perilaku yang dihasilkan sebenarnya merupakan kombinasi dari definisi klasik pass-by-reference dan pass-by-value.

Ari
sumber
"lulus referensi dengan nilai" adalah frasa yang sama yang saya gunakan untuk menggambarkan lewat argumen Ruby. Saya pikir itu ungkapan yang paling akurat dan ringkas.
Wayne Conrad
16

Sudah ada beberapa jawaban yang bagus, tetapi saya ingin memposting definisi sepasang otoritas pada subjek, tetapi juga berharap seseorang dapat menjelaskan apa yang dimaksud otoritas Matz (pencipta Ruby) dan David Flanagan dalam buku O'Reilly mereka yang sangat bagus, Bahasa Pemrograman Ruby .

[dari 3.8.1: Referensi Obyek]

Saat Anda meneruskan objek ke metode di Ruby, itu adalah referensi objek yang diteruskan ke metode. Ini bukan objek itu sendiri, dan itu bukan referensi ke referensi ke objek. Cara lain untuk mengatakan ini adalah bahwa argumen metode dilewatkan oleh nilai daripada oleh referensi , tetapi nilai yang dikirimkan adalah referensi objek.

Karena referensi objek diteruskan ke metode, metode dapat menggunakan referensi tersebut untuk memodifikasi objek yang mendasarinya. Modifikasi ini kemudian terlihat ketika metode kembali.

Ini semua masuk akal bagi saya sampai paragraf terakhir, dan terutama kalimat terakhir itu. Ini paling menyesatkan, dan lebih buruk lagi membingungkan. Bagaimana, dengan cara apa pun, modifikasi terhadap referensi yang dilewatkan oleh nilai dapat mengubah objek yang mendasarinya?

Dominick
sumber
1
Karena referensi tidak dimodifikasi; objek yang mendasarinya adalah.
dodgethesteamroller
1
Karena objeknya bisa berubah. Ruby bukan bahasa yang sepenuhnya fungsional. Ini sepenuhnya ortogonal untuk pass-by-reference vs pass-by-value (kecuali untuk fakta bahwa dalam bahasa yang murni fungsional, pass-by-value dan pass-by-reference selalu menghasilkan hasil yang sama, sehingga bahasa tersebut dapat gunakan salah satu atau keduanya tanpa Anda sadari).
Jörg W Mittag
Contoh yang baik adalah jika alih-alih tugas variabel dalam suatu fungsi, Anda melihat kasus melewati hash ke suatu fungsi dan melakukan penggabungan! pada hash yang disahkan. Hash asli akhirnya dimodifikasi.
Dll
16

Apakah Ruby lulus dengan referensi atau berdasarkan nilai?

Ruby adalah pass-by-reference. Selalu. Tidak ada pengecualian. Tidak ada seandainya. Tidak ada tapi

Berikut ini adalah program sederhana yang menunjukkan fakta itu:

def foo(bar)
  bar.object_id
end

baz = 'value'

puts "#{baz.object_id} Ruby is pass-by-reference #{foo(baz)} because object_id's (memory addresses) are always the same ;)"

=> 2279146940 Ruby adalah pass-by-reference 2279146940 karena object_id (alamat memori) selalu sama;)

def bar(babar)
  babar.replace("reference")
end

bar(baz)

puts "some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-#{baz}"

=> beberapa orang tidak menyadari itu referensi karena tugas lokal dapat diutamakan, tapi itu jelas lewat-referensi

Brett Allred
sumber
Ini adalah satu-satunya jawaban yang benar dan menyajikan beberapa gotcha bagus: Coba a = 'foobar'; b = a; b [5] = 'z', baik a dan b akan dimodifikasi.
Martijn
2
@ Martijn: argumen Anda tidak sepenuhnya valid. Mari kita lihat pernyataan kode Anda dengan pernyataan. a = 'foobar' menciptakan referensi baru yang menunjuk ke 'foobar'. b = a membuat referensi kedua ke data yang sama dengan a. b [5] = 'z' mengubah karakter keenam dari nilai yang dirujuk oleh b ke 'z' (nilai yang secara kebetulan juga dirujuk oleh akan diubah). Itu sebabnya "keduanya dimodifikasi" dalam istilah Anda, atau lebih tepatnya, mengapa "nilai yang dirujuk oleh kedua variabel akan dimodifikasi".
Lukas_Skywalker
2
Anda tidak melakukan apa pun dengan referensi dalam barmetode Anda . Anda hanya memodifikasi objek yang referensi menunjuk ke, tetapi tidak referensi itu sendiri. Mereka satu-satunya cara untuk memodifikasi referensi di Ruby adalah dengan penugasan. Anda tidak dapat mengubah referensi dengan memanggil metode di Ruby karena metode hanya dapat dipanggil pada objek dan referensi bukan objek di Ruby. Sampel kode Anda menunjukkan bahwa Ruby telah berbagi status yang bisa berubah-ubah (yang tidak sedang dibahas di sini), namun demikian, hal itu tidak menjelaskan perbedaan antara pass-by-value dan pass-by-reference.
Jörg W Mittag
1
Ketika seseorang bertanya apakah suatu bahasa adalah "pass-by-reference", mereka biasanya ingin tahu ketika Anda mengirimkan sesuatu ke suatu fungsi dan fungsi memodifikasinya, apakah itu akan dimodifikasi di luar fungsi. Untuk Ruby jawabannya adalah 'ya'. Jawaban ini sangat membantu dalam menunjukkan bahwa, jawaban JörgWMittag sangat tidak membantu.
Toby 1 Kenobi
@ Toby1Kenobi: Anda tentu saja bebas menggunakan definisi pribadi Anda sendiri dari istilah "pass-by-value" yang berbeda dari definisi umum yang banyak digunakan. Namun, jika Anda melakukannya, Anda harus siap untuk orang-orang menjadi bingung, terutama jika Anda lalai untuk mengungkapkan fakta bahwa Anda berbicara tentang yang sangat berbeda, dalam beberapa aspek bahkan berlawanan pendapat daripada orang lain. Secara khusus, "pass-by-reference" tidak peduli dengan apakah "sesuatu" yang disahkan dapat dimodifikasi, melainkan dengan apa "sesuatu" itu, khususnya, apakah itu rujukannya ...
Jörg W Mittag
8

Parameter adalah salinan dari referensi asli. Jadi, Anda dapat mengubah nilai, tetapi tidak dapat mengubah referensi asli.

Rael Gugelmin Cunha
sumber
2

Coba ini:--

1.object_id
#=> 3

2.object_id
#=> 5

a = 1
#=> 1
a.object_id
#=> 3

b = 2
#=> 2
b.object_id
#=> 5

identifier a berisi object_id 3 untuk value object 1 dan identifier b berisi object_id 5 untuk value object 2.

Sekarang lakukan ini: -

a.object_id = 5
#=> error

a = b
#value(object_id) at b copies itself as value(object_id) at a. value object 2 has object_id 5
#=> 2

a.object_id 
#=> 5

Sekarang, a dan b keduanya berisi object_id 5 yang sama yang merujuk ke objek nilai 2. Jadi, variabel Ruby berisi object_ids untuk merujuk ke objek nilai.

Melakukan hal berikut juga memberikan kesalahan: -

c
#=> error

tetapi melakukan ini tidak akan memberikan kesalahan: -

5.object_id
#=> 11

c = 5
#=> value object 5 provides return type for variable c and saves 5.object_id i.e. 11 at c
#=> 5
c.object_id
#=> 11 

a = c.object_id
#=> object_id of c as a value object changes value at a
#=> 11
11.object_id
#=> 23
a.object_id == 11.object_id
#=> true

a
#=> Value at a
#=> 11

Berikut identifier objek nilai pengembalian 11 yang id objeknya 23 yaitu object_id 23 berada di identifier a, Sekarang kita melihat contoh dengan menggunakan metode.

def foo(arg)
  p arg
  p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23

arg in foo ditugaskan dengan nilai balik x. Ini jelas menunjukkan bahwa argumen dilewatkan oleh nilai 11, dan nilai 11 menjadi objek sendiri memiliki id objek unik 23.

Sekarang lihat ini juga: -

def foo(arg)
  p arg
  p arg.object_id
  arg = 12
  p arg
  p arg.object_id
end

#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
#=> 12
#=> 25
x
#=> 11
x.object_id
#=> 23

Di sini, identifier arg pertama berisi object_id 23 untuk merujuk 11 dan setelah penugasan internal dengan nilai objek 12, itu berisi object_id 25. Tapi itu tidak mengubah nilai yang dirujuk oleh pengidentifikasi x yang digunakan dalam metode pemanggilan.

Oleh karena itu, Ruby diteruskan oleh nilai dan variabel Ruby tidak mengandung nilai tetapi berisi referensi ke objek nilai.

Alok Anand
sumber
1

Ruby ditafsirkan. Variabel adalah referensi ke data, tetapi bukan data itu sendiri. Ini memfasilitasi menggunakan variabel yang sama untuk data dari berbagai jenis.

Penugasan lhs = rhs kemudian menyalin referensi pada rhs, bukan data. Ini berbeda dalam bahasa lain, seperti C, di mana tugas tidak menyalin data ke lhs dari rhs.

Jadi untuk pemanggilan fungsi, variabel yang dikirimkan, misalkan x, memang disalin ke variabel lokal dalam fungsi, tetapi x adalah referensi. Kemudian akan ada dua salinan referensi, keduanya merujuk data yang sama. Satu akan berada di pemanggil, satu di fungsi.

Tugas dalam fungsi kemudian akan menyalin referensi baru ke versi fungsi x. Setelah ini, versi x penelepon tetap tidak berubah. Itu masih referensi ke data asli.

Sebaliknya, menggunakan metode .replace pada x akan menyebabkan ruby ​​melakukan salinan data. Jika ganti digunakan sebelum penugasan baru maka penelepon memang akan melihat perubahan data dalam versinya juga.

Demikian pula, selama referensi asli sesuai dengan variabel yang diteruskan, variabel instan akan sama dengan yang dilihat pemanggil. Dalam kerangka suatu objek, variabel instan selalu memiliki nilai referensi yang paling terkini, baik yang disediakan oleh pemanggil atau diatur dalam fungsi kelas yang diteruskan.

'Panggilan berdasarkan nilai' atau 'panggilan dengan referensi' kacau di sini karena kebingungan mengenai '=' Dalam bahasa yang dikompilasi '=' adalah salinan data. Di sini, dalam bahasa yang ditafsirkan ini '=' adalah salinan referensi. Dalam contoh Anda memiliki referensi yang diteruskan diikuti oleh salinan referensi meskipun '=' yang clobbers yang asli lulus dalam referensi, dan kemudian orang-orang membicarakannya seolah-olah '=' adalah salinan data.

Agar konsisten dengan definisi, kita harus tetap menggunakan '.replace' karena ini adalah salinan data. Dari perspektif '. Ganti' kita melihat bahwa ini memang lulus dengan referensi. Lebih jauh, jika kita berjalan di debugger, kita melihat referensi dilewatkan, karena variabel adalah referensi.

Namun jika kita harus menjaga '=' sebagai kerangka referensi, maka memang kita bisa melihat data yang diteruskan hingga tugas, dan kemudian kita tidak bisa melihatnya lagi setelah penugasan sementara data pemanggil tetap tidak berubah. Pada tingkat perilaku, ini dilewati oleh nilai selama kami tidak menganggap nilai yang diteruskan sebagai komposit - karena kami tidak akan dapat menyimpan bagian darinya sambil mengubah bagian lainnya dalam satu tugas tunggal (karena tugas itu mengubah referensi dan yang asli keluar dari ruang lingkup). Juga akan ada kutil, dalam hal variabel variabel dalam objek akan menjadi referensi, seperti semua variabel. Karenanya kita akan dipaksa untuk berbicara tentang melewati 'referensi berdasarkan nilai' dan harus menggunakan lokasi terkait.


sumber
1

Perlu dicatat bahwa Anda bahkan tidak perlu menggunakan metode "ganti" untuk mengubah nilai nilai asli. Jika Anda menetapkan salah satu nilai hash untuk hash, Anda mengubah nilai aslinya.

def my_foo(a_hash)
  a_hash["test"]="reference"
end;

hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"
Don Carr
sumber
Hal lain yang saya temukan. Jika Anda melewati tipe numerik, semua tipe numerik tidak berubah, dan dengan demikian ADALAH nilai yang lewat. Fungsi ganti yang bekerja dengan string di atas, TIDAK berfungsi untuk semua jenis numerik.
Don Carr
1
Two references refer to same object as long as there is no reassignment. 

Pembaruan di objek yang sama tidak akan membuat referensi ke memori baru karena masih di memori yang sama. Berikut ini beberapa contoh:

    a = "first string"
    b = a



    b.upcase! 
    => FIRST STRING
    a
    => FIRST STRING

    b = "second string"


a
    => FIRST STRING
    hash = {first_sub_hash: {first_key: "first_value"}}
first_sub_hash = hash[:first_sub_hash]
first_sub_hash[:second_key] = "second_value"

    hash
    => {first_sub_hash: {first_key: "first_value", second_key: "second_value"}}

    def change(first_sub_hash)
    first_sub_hash[:third_key] = "third_value"
    end

    change(first_sub_hash)

    hash
    =>  {first_sub_hash: {first_key: "first_value", second_key: "second_value", third_key: "third_value"}}
Ayman Hussain
sumber
0

Ya tapi ....

Ruby memberikan referensi ke sebuah objek dan karena semua yang ada di ruby ​​adalah sebuah objek, maka Anda bisa mengatakan itu lewat referensi.

Saya tidak setuju dengan posting di sini yang mengklaim itu lewat nilai, yang tampak seperti permainan yang simpatik dan simpatik bagi saya.

Namun, efeknya "menyembunyikan" perilaku karena sebagian besar operasi ruby ​​menyediakan "di luar kotak" - misalnya operasi string, menghasilkan salinan objek:

> astringobject = "lowercase"

> bstringobject = astringobject.upcase
> # bstringobject is a new object created by String.upcase

> puts astringobject
lowercase

> puts bstringobject
LOWERCASE

Ini berarti bahwa sebagian besar waktu, objek asli dibiarkan tidak berubah memberikan penampilan yang ruby ​​adalah "lewat nilai".

Tentu saja ketika merancang kelas Anda sendiri, pemahaman tentang detail perilaku ini penting untuk perilaku fungsional, efisiensi memori dan kinerja.

Tomm P
sumber