Ruby referansla mı yoksa değere göre mi?


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?

@usernesnesi lang_errors,update_lanugages yöntemdeki . Ben bir kurtarış gerçekleştirdiğinizde @usernesnenin Başlangıçta saklandı hataları kaybetmek lang_errorsdeğişken.

Yapmaya çalıştığım şey daha fazla bir kesmek olurdu (ki bu çalışmıyor gibi görünüyor). Değişken değerlerin neden yıkandığını anlamak istiyorum. Ben referansla pass anlamak böylece değerin yıkanmadan o değişkende nasıl tutulabilir bilmek istiyorum.


Klonlanmış bir nesnede bu değeri koruyabildiğimi de fark ettim
Sid

1
Abe Voelker cevabına bakmalısın. Ama bu blokta koştuktan sonra, bunu nasıl söyleyeceğim. Bir nesneyi Foo'yu bir prosedüre ilettiğinizde, nesneye yapılan başvurunun bir kopyası geçirilir, çubuk, değere göre geçir. Foo'nun işaret ettiği nesneyi değiştiremezsiniz, ancak işaret ettiği nesnenin içeriğini değiştirebilirsiniz. Bu nedenle, bir dizi iletirseniz, dizinin içeriği değiştirilebilir, ancak hangi diziye başvurulduğunu değiştiremezsiniz. Foo'nun diğer bağımlılıklarını karıştırmaktan endişe etmeden Foo yöntemlerini kullanabilmek güzel.
bobbdelsol

Yanıtlar:


244

Geleneksel terminolojide, Ruby kesinlikle değerlidir . Ama burada gerçekten sorduğunuz şey bu değil.

Ruby'nin saf, referans olmayan bir değer kavramı yoktur, bu yüzden kesinlikle bir yönteme geçemezsiniz. Değişkenler her zaman nesnelere referanstır. Altınızdan değişmeyecek bir nesneyi elde etmek için, geçtiğiniz nesneyi kopyalamanız veya kopyalamanız, böylece kimsenin referans vermediği bir nesne vermeniz gerekir. (Bu bile kurşun geçirmez değildir - standart klonlama yöntemlerinin her ikisi de sığ bir kopya yapar, bu nedenle klonun örnek değişkenleri hala orijinallerinkiyle aynı nesnelere işaret eder. İvars tarafından başvurulan nesneler mutasyona uğrarsa, aynı nesnelere başvurduğundan kopyada görünmeye devam eder.)


88
Ruby by pass-value . Hayır ifs. Kesik yok. İstisna yok. Eğer Yakut (veya başka bir dili) olup olmadığını bilmek istiyorsanız referans ile-pass veya pass-by-value sadece denemek,: def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}".
Jörg W Mittag

95
@ JörgWMittag: Evet, ancak OP'nin kafa karışıklığı aslında kelimelerin katı CS anlamında değere göre veya referansla değil. Ne eksikti sen geçiyoruz "değerler" olduğunu vardır referanslar. Sadece "Bu değer" demek, bilgiçlik ve OP'nin bir kötülük yapmak olduğunu hissettim, çünkü aslında bu kastettiği şey değildi. Ancak açıklama için teşekkürler, çünkü gelecekteki okuyucular için önemlidir ve bunu eklemeliydim. (Ben her zaman daha fazla bilgi eklemekle insanları
Chuck

16
@Jorg ile katılmıyorum. Ruby referansla geçiyor, sadece referansı değiştiriyor. Bunun yerine şunu deneyin: def foo (bar) bar.replace 'reference' end; baz = 'değer'; fan (baz); "Ruby is pass-by - # {baz}"
pguardiario

15
@pguardiario: Bence bu sadece bir tanım meselesi. Jörg geleneksel bilgisayar bilimi tanımını kullanırken, kişisel olarak ortaya çıkardığınız "referansla" tanımını kullanıyorsunuz. Tabii ki, size kelimelerin nasıl kullanılacağını söylemek benim işimden hiç biri değil - sadece terimin normalde ne anlama geldiğini açıklamanın önemli olduğunu düşünüyorum . Geleneksel terminolojide Ruby, değere göre değerdir, ancak değerlerin kendileri referanstır. Siz ve OP'nin bunu neden referans olarak düşünmeyi sevdiğini tamamen anlıyorum - bu terimin geleneksel anlamı değil.
Chuck

7
Ruby'deki her şey bir nesnedir, bu yüzden Ruby en azından C ++ 'da kullanılan terimler, ne değere göre ne de referansa göre geçer. “nesne referansıyla geç”, Ruby'nin ne yaptığını açıklamanın daha iyi bir yolu olabilir. Sonunda, en iyi bahis, bu terimlerin hiçbirine çok fazla anlam vermemek ve sadece gerçekleşen davranışı iyi bir şekilde anlamak olabilir.
David Winiecki

424

Diğer cevaplayıcıların hepsi doğrudur, ancak bir arkadaşım bunu ona açıklamamı istedi ve gerçekten neyin kaynatıldığı Ruby'nin değişkenleri nasıl ele aldığıdır, bu yüzden onun için yazdığım bazı basit resimleri / açıklamaları paylaşacağımı düşündüm (uzunluk için özür dilerim) ve muhtemelen bazı aşırı basitleştirmeler):


S1: strdeğerine yeni bir değişken atadığınızda ne olur 'foo'?

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

resim açıklamasını buraya girin

C: strNesneye işaret 'foo'eden ve bu Ruby yorumlayıcısının durumu için bellek konumunda olan bir etiket oluşturulur 2000.


S2: Varolan değişkeni strkullanarak yeni bir nesneye atadığınızda ne olur =?

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

resim açıklamasını buraya girin

C: Etiket strşimdi farklı bir nesneyi gösteriyor.


S3: Eğer yeni bir değişken atamak ne olur =hiç str?

str2 = str
str2.object_id # => 2002

resim açıklamasını buraya girin

C: adlı yeni bir etiket str2oluşturulur Şuna noktaları aynı nesnenin olarak str.


S4: Başvurulan strve str2değiştirilen nesne değişirse ne olur ?

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

resim açıklamasını buraya girin

C: Her iki etiket de aynı nesneyi işaret ediyor, ancak bu nesnenin kendisi mutasyona uğradı (içeriği başka bir şey olarak değişti).


Bu orijinal soru ile nasıl ilişkilidir?

Temel olarak Q3 / Q4'te olanlarla aynı; yöntem, str2kendisine ( str) iletilen değişken / label ( ) özel kopyasını alır . Bu etiket hangi nesne değiştiremezsiniz str puan , ancak değiştirebilir içeriğini ikisi de referans başka içerecek bu nesne:

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

1
Robert Heaton da son zamanlarda bu konuda blog yazdı: robertheaton.com/2014/07/22/…
Michael Renner

48

Ruby "nesne referansıyla geç" kullanıyor

(Python'un terminolojisini kullanarak.)

Ruby'nin "değere göre geç" veya "referansla geç" kullandığını söylemek, yardımcı olacak kadar açıklayıcı değildir. Çoğu insanın bugünlerde bildiği gibi, terminolojinin ("değer" ve "referans") C ++ 'dan geldiğini düşünüyorum.

C ++ 'da, "değere göre geç", işlevin değişkenin bir kopyasını aldığı ve kopyadaki herhangi bir değişikliğin orijinali değiştirmediği anlamına gelir. Bu nesneler için de geçerlidir. Bir nesne değişkenini değere göre iletirseniz, tüm nesne (tüm üyeleri dahil) kopyalanır ve üyelerde yapılan değişiklikler orijinal nesnedeki üyeleri değiştirmez. (İşaretçiyi değere göre iletmek farklıdır, ancak Ruby'nin zaten işaretçileri yoktur, 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;
}

Çıktı:

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

C ++ 'da "referansla geç", işlevin orijinal değişkene eriştiği anlamına gelir. Tamamen yeni bir değişmez tamsayı atayabilir ve orijinal değişken de bu değere sahip olacaktır.

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;
}

Çıktı:

in replace: 10
in main: 10

Bağımsız değişken bir nesne değilse Ruby, değere göre pass değerini (C ++ anlamında) kullanır. Ancak Ruby'de her şey bir nesnedir, bu yüzden Ruby'deki C ++ anlamında gerçekten değere göre geçiş yoktur.

Ruby'de, "nesne referansıyla geç" (Python'un terminolojisini kullanmak için) kullanılır:

  • İşlev içinde, nesnenin üyelerinden herhangi birine kendilerine yeni değerler atanabilir ve bu değişiklikler işlev döndükten sonra da devam eder. *
  • İşlev içinde, değişkene tamamen yeni bir nesne atamak değişkenin eski nesneye başvurmayı durdurmasına neden olur. Ancak işlev döndükten sonra, orijinal değişken yine de eski nesneye başvurur.

Bu nedenle Ruby, C ++ anlamında "referansla geç" işlevini kullanmaz. Eğer öyleyse, fonksiyonun içindeki bir değişkene yeni bir nesne atamak, fonksiyon geri döndükten sonra eski nesnenin unutulmasına neden olur.

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

Çıktı:

1
2
2
3
2

1
2
1

* Bu nedenle, Ruby'de, bir işlev içindeki bir nesneyi değiştirmek ancak işlev döndüğünde bu değişiklikleri unutmak istiyorsanız, kopyada geçici değişikliklerinizi yapmadan önce nesnenin bir kopyasını açıkça oluşturmanız gerekir.


Cevabınız en iyisidir. Ben de basit bir örnek göndermek istiyorum def ch(str) str.reverse! end; str="abc"; ch(str); puts str #=> "cba"
fangxing

Bu doğru cevap! Bu da burada çok iyi açıklanmıştır: robertheaton.com/2014/07/22/… . Ama ne ben hala anlamıyorum şudur: def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}". Bu "Ruby by-pass-by-value" yazdırır. Ancak içindeki değişken fooyeniden atanır. Bir bardizi olsaydı, yeniden atama etkili olmaz baz. Neden?
haffla

Sorunu anlamıyorum. Burada yorum sormak yerine tamamen yeni bir soru sormalısınız.
David Winiecki

42

Ruby referansla mı yoksa değere göre mi?

Ruby by pass-value. Her zaman. İstisna yok. Hayır ifs. Kesik yok.

İşte bu gerçeği gösteren basit bir program:

def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value

15
Budur bir hata, değil - @DavidJ .: "Burada yapılacak bir hata, yerel parametresi (bellekte yeni bir yere işaret) yeniden olmasıdır" tanımı içinde by-pass değer . Ruby referansla geçilseydi, callee'deki yerel yöntem bağımsız değişkenine yeniden atama da, arayandaki yerel değişken bağlanmasını yeniden atayacaktır. Hangi değildi. Ergo, Ruby by pass-by-value. Değişebilir bir değeri değiştirirseniz, değer değişiklikleri tamamen önemsizdir, değişebilir durum böyle çalışır. Ruby saf bir işlevsel dil değildir.
Jörg W Mittag

5
Jörg'e, "by by-value" ın gerçek tanımını savunması için teşekkürler. Değer aslında bir referans olduğunda beynimizi açıkça eritir, ancak yakut her zaman yan değerden geçer.
Douglas

9
Bu sofistike. "Değere göre geç" ve "referansla geç" arasındaki pratik ayrım sözdizimsel değil semantiktir. C dizilerinin değer bazında olduğunu söyleyebilir misiniz? Tabii ki, bir dizinin adını bir işleve geçirdiğinizde değişmez bir işaretçi geçiriyor olsanız ve yalnızca işaretçinin başvurduğu veriler değiştirilebiliyor. Açıkça Ruby'deki değer türleri değere, referans türleri ise başvuruya göre geçirilir.
dodgethesteamroller

3
@ dodgethesteamroller: Hem Ruby hem de C, by-pass değeridir. Her zaman. İstisna yok, eğer hayır yok. By-pass-by-by-referans arasındaki fark, referans noktalarına referansı geçip geçmediğiniz veya referansı geçtiğinizdir. C daima değeri geçer, asla referansı geçmez . Değer bir işaretçi olabilir veya olmayabilir, ancak değerin ne olduğu, ilk etapta geçirilip iletilmemesi ile ilgisizdir. Ruby de her zaman değeri geçer, asla referans değildir. Bu değer her zaman bir işaretçi olmakla birlikte yine bu önemsizdir.
Jörg W Mittag

44
Bu cevap, kesinlikle doğru söz ederken , çok yararlı değildir . Aktarılan değerin her zaman bir işaretçi olması, önemsiz değildir . Öğrenmeye çalışan insanlar için bir karışıklık kaynağıdır ve cevabınız bu karışıklığa yardımcı olacak hiçbir şey yapmaz.

20

Ruby, tam anlamıyla by-pass değeridir, ancak değerler referanslardır.

Buna " değere göre referans değeri " denebilir . Bu makale okuduğum en iyi açıklamaya sahip: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/

Değere göre referans referansı kısaca şu şekilde açıklanabilir:

Bir işlev, arayan tarafından kullanılan bellekteki aynı nesneye referans alır (ve erişir). Ancak, arayanın bu nesneyi sakladığı kutuyu almaz; değere göre geçiş değerinde olduğu gibi, işlev kendi kutusunu sağlar ve kendisi için yeni bir değişken oluşturur.

Ortaya çıkan davranış aslında referans-by-pass ve by-value klasik tanımlarının bir kombinasyonudur.


"değere göre pass başvurusu" Ruby'nin argümanı geçerken tanımlamak için kullandığım ifadeyle aynı. Bence bu en doğru ve özlü ifade.
Wayne Conrad

16

Zaten bazı harika cevaplar var, ancak konuyla ilgili bir çift yetkilinin tanımını yayınlamak istiyorum, ancak aynı zamanda birinin otoritelerin Matz (Ruby'nin yaratıcısı) ve David Flanagan'ın mükemmel O'Reilly kitaplarında ne ifade ettiğini açıklayabileceğini umuyorum, Ruby Programlama Dili .

[3.8.1'den: Nesne Referansları]

Bir nesneyi Ruby'de bir yönteme ilettiğinizde, yönteme iletilen bir nesne başvurusudur. Nesnenin kendisi değildir ve nesneye yapılan başvuruya bir başvuru değildir. Bu Diğer bir deyişle, yöntemi argüman geçirilir olan değere göre değil, referans ile bu geçirilen değerleri nesne referansları olduğu, ancak.

Nesne başvuruları yöntemlere iletildiğinden, yöntemler bu başvuruyu temeldeki nesneyi değiştirmek için kullanabilir. Bu değişiklikler, yöntem döndüğünde görünür.

Bu son paragrafa kadar, özellikle de son cümleye kadar bu bana mantıklı geliyor . Bu en iyi yanıltıcı ve daha kötü karıştırıcıdır. Herhangi bir şekilde, bu değer bazında yapılan referansta yapılan değişiklikler temeldeki nesneyi nasıl değiştirebilir?


1
Referans değiştirilmediği için; altta yatan nesne.
dodgethesteamroller

1
Çünkü nesne değiştirilebilir. Ruby tamamen işlevsel bir dil değildir. Bu referans-by-pass-by-value ile tamamen ortogonaldir (tamamen işlevsel bir dilde, by-pass ve by-by-referansın her zaman aynı sonuçları vermesi dışında, dil hiç bilmeden birini veya her ikisini kullanın).
Jörg W Mittag

İyi bir örnek, bir işlevdeki değişken atama yerine, bir karmayı işleve geçirme ve birleştirme yapma durumuna bakarsanız! geçen karma. Orijinal karma değiştirilir.
elc

16

Ruby referansla mı yoksa değere göre mi?

Ruby referansla geçer. Her zaman. İstisna yok. Hayır ifs. Kesik yok.

İşte bu gerçeği gösteren basit bir program:

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, 2279146940 numaralı referansla geçer çünkü nesne_kimliği (bellek adresleri) her zaman aynıdır;)

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}"

=> bazı insanlar referanslarının farkında değiller çünkü yerel atama öncelik kazanabilir, ancak açıkça referans yoluyla geçer


Bu tek doğru cevaptır ve bazı güzel gotchalar sunar: Deneyin a = 'foobar'; b = a; b [5] = 'z', hem a hem de b değiştirilecek.
Martijn

2
@Martijn: argümanınız tamamen geçerli değil. Kod ifadenizi ifadeye göre inceleyelim. a = 'foobar', 'foobar'ı işaret eden yeni bir referans oluşturur. b = a, a ile aynı verilere ikinci bir başvuru oluşturur. b [5] = 'z', b tarafından başvurulan değerin altıncı karakterini bir 'z' olarak değiştirir (tesadüfen değiştirilen bir değişimle de değinilen değer). Bu nedenle terimlerinizde "her ikisi de değiştirilir" veya daha doğrusu, "her iki değişkenin referans verdiği değer neden değiştirilir".
Lukas_Skywalker

2
Metodunuzdaki referans ile hiçbir şey yapmıyorsunuz bar. Sadece nesneyi değiştirerek referansın noktalarını işaret , ancak referansın kendisini değiştirmiyorsunuz. Ruby'deki referansları değiştirmenin tek yolu atamaktır. Ruby'de yöntemleri çağırarak başvuruları değiştiremezsiniz, çünkü yöntemler yalnızca nesnelerde çağrılabilir ve başvurular Ruby'deki nesneler değildir. Kod örneğiniz, Ruby'nin değiştirilebilir durumu paylaştığını gösterir (burada tartışılmamaktadır), ancak by-pass-by-referans arasındaki farkı aydınlatmak için hiçbir şey yapmaz.
Jörg W Mittag

1
Birisi bir dilin "referansla" olup olmadığını sorduğunda, genellikle bir işleve bir şey ilettiğinizde ve işlev onu değiştirdiğinde, işlev dışında değiştirilecekse bilmek ister. Ruby için cevap 'evet'. Bu cevap, @ JörgWMittag'in cevabının son derece yararsız olduğunu göstermede faydalıdır.
Toby 1 Kenobi

@ Toby1Kenobi: Tabii ki yaygın, yaygın olarak kullanılan tanımdan farklı olan "by-value" terimine ilişkin kendi kişisel tanımınızı kullanmakta özgürsünüz. Bununla birlikte, bunu yaparsanız, özellikle de çok farklı bir konu hakkında konuştuğunuz gerçeğini açıklamayı ihmal ederseniz, bazı yönlerde, herkesten farklı olarak bile insanların kafalarını karıştırmaya hazır olmalısınız. Özellikle, "pass-by-referans" olduğunu değil değiştirilebilir geçirilir "şey" olsun ya da olmasın, fakat daha ziyade ile ilgilenen Ne "bir şey" referans ... olup olmadığı, özellikle olduğunu
Jörg W Mittag

8

Parametreler orijinal referansın bir kopyasıdır. Böylece, değerleri değiştirebilirsiniz, ancak orijinal başvuruyu değiştiremezsiniz.


2

Bunu dene:--

1.object_id
#=> 3

2.object_id
#=> 5

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

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

tanımlayıcı a değer nesnesi 1 için object_id 3 içerir ve b tanımlayıcısı değer 2 nesnesi için object_id 5 içerir.

Şimdi yap bunu:--

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

Şimdi, a ve b'nin her ikisi de değer nesnesi 2'ye atıfta bulunan aynı object_id 5'i içerir. Dolayısıyla, Ruby değişkeni değer nesnelerine başvurmak için object_ids içerir.

Aşağıdakileri yapmak da hata verir: -

c
#=> error

ancak bunu yapmak hata vermez: -

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

Burada tanımlayıcı bir nesne değeri 23 olan bir nesne değeri 11 yani nesne_kimliği 23 tanımlayıcı a'da, şimdi yöntem kullanarak bir örnek görüyoruz.

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

foo içindeki arg, x dönüş değeri ile atanır. Argümanın 11 değeriyle geçtiğini ve 11 nesnesinin kendisinin bir nesnenin benzersiz nesne kimliği 23'e sahip olduğunu açıkça göstermektedir.

Şimdi buna da bakınız: -

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

Burada, tanımlayıcı argümanı ilk olarak 11'e atıfta bulunmak için nesne_kidesi 23 içerir ve değer nesnesi 12 ile dahili atamadan sonra nesne_kimliği 25 içerir. Ancak çağrı yönteminde kullanılan tanımlayıcı x tarafından referans verilen değeri değiştirmez.

Bu nedenle, Ruby değere göre geçiştir ve Ruby değişkenleri değer içermez, ancak değer nesnesine başvuru içerir.


1

Ruby yorumlanır. Değişkenler verilere referanstır, ancak verinin kendisi değildir. Bu, farklı türdeki veriler için aynı değişkenin kullanılmasını kolaylaştırır.

Daha sonra lhs = rhs ataması, referansı rhs'ye kopyalar, verileri kopyalar. Bu, atamanın rh'den lhs'ye bir veri kopyası yaptığı C gibi diğer dillerde farklılık gösterir.

Bu nedenle işlev çağrısı için, geçen x değişkeni aslında işlevdeki yerel bir değişkene kopyalanır, ancak x bir referanstır. Daha sonra referansın iki kopyası olacaktır, her ikisi de aynı verilere gönderme yapar. Biri arayanda, diğeri fonksiyonda olacak.

İşlevdeki atama, işlevin x sürümüne yeni bir başvuru kopyalar. Bundan sonra, arayanın x sürümü değişmeden kalır. Hala orijinal verilere referanstır.

Buna karşılık, x üzerinde .replace yönteminin kullanılması ruby'nin veri kopyalaması yapmasına neden olur. Eğer yeni bir atamadan önce replasman kullanılırsa, arayan kişi versiyonundaki veri değişikliğini de görecektir.

Benzer şekilde, orijinal referans geçirilen değişken için inceli olduğu sürece, örnek değişkenleri arayanın gördüğü ile aynı olacaktır. Bir nesne çerçevesinde, örnek değişkenleri, ister arayan tarafından sağlansın, isterse sınıfın geçtiği işlevde ayarlanmış olsun, her zaman en güncel referans değerlerine sahiptir.

'Değere göre çağrı' veya 'referansla çağrı' burada karışıktır çünkü '=' Derlenmiş dillerde '=' bir veri kopyasıdır. Burada bu yorumlanmış dilde '=' referans bir kopyadır. Örnekte, referansın geçtiği ve ardından orijinalin referansta geçtiği '=' olsa bir referans kopyası var ve daha sonra '=' gibi konuşan insanlar bir veri kopyasıydı.

Tanımlarla tutarlı olabilmek için bir veri kopyası olduğu için '.replace' ifadesine uymalıyız. '.Replace' perspektifinden bakıldığında, bunun gerçekten referans olarak geçildiğini görüyoruz. Ayrıca, hata ayıklayıcıdan geçersek, değişkenlerin referans olduğu için referansların aktarıldığını görürüz.

Bununla birlikte, '=' ifadesini referans çerçevesi olarak tutmamız gerekiyorsa, gerçekten de bir atamaya kadar aktarılan verileri görürüz ve sonra arayan verileri değişmeden kalırken atamadan sonra artık göremeyiz. Davranışsal bir seviyede bu, geçirilen değerin bileşik olduğunu düşünmediğimiz sürece değere göre geçer - tek bir ödevdeki diğer bölümü değiştirirken bir kısmını tutamayacağımızdan (bu ödev gibi) referansı değiştirir ve orijinal kapsam dışına çıkar). Bir siğil de olacaktır, bu durumda nesnelerdeki değişkenler ve tüm değişkenler referans olacaktır. Bu nedenle, 'değere göre referanslar' geçirmekten bahsetmek zorunda kalacağız ve ilgili yerleri kullanmak zorundayız.


1

Orijinal değer değerini değiştirmek için "replace" yöntemini bile kullanmanız gerekmediğine dikkat edilmelidir. Bir karma için karma değerlerinden birini atarsanız, orijinal değeri değiştirirsiniz.

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

hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"

Bulduğum başka bir şey. Sayısal bir türden geçiyorsanız, tüm sayısal türler değiştirilemez ve dolayısıyla değere göre ARE'dir. Yukarıdaki dize ile çalışan replace işlevi, sayısal türlerin hiçbiri için ÇALIŞMAZ.
Don Carr

1
Two references refer to same object as long as there is no reassignment. 

Aynı nesnedeki hiçbir güncelleme, hala aynı bellekte olduğundan yeni belleğe referans yapmaz. İşte birkaç örnek:

    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"}}

0

Evet ama ....

Ruby bir nesneye referans verir ve yakuttaki her şey bir nesne olduğundan, referansla geçtiğini söyleyebilirsiniz.

Buradaki değerlere göre geçtiğini iddia eden yayınlara katılmıyorum, bana bilgiçlik, symantic oyunlar gibi geliyor.

Ancak, gerçekte davranışı "gizler" çünkü ruby ​​işlemlerinin çoğu "kutudan çıkarılır" sağlar - örneğin dize işlemleri, nesnenin bir kopyasını üretir:

> astringobject = "lowercase"

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

> puts astringobject
lowercase

> puts bstringobject
LOWERCASE

Bu, çoğu zaman, orijinal nesnenin değişmeden bırakıldığı, yakutun "değere göre geç" olduğu görünümünde olduğu anlamına gelir.

Elbette kendi sınıflarınızı tasarlarken, bu davranışın ayrıntılarının anlaşılması hem işlevsel davranış, bellek verimliliği hem de performans için önemlidir.

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.