Ruby iki değeri nasıl döndürür?


96

Bir dizideki değerleri ne zaman değiştirsem, değerlerden birini bir referans değişkeninde sakladığımdan emin oluyorum. Ancak Ruby'nin iki değer döndürebildiğini ve iki değeri otomatik olarak değiştirebileceğini buldum. Örneğin,

array = [1, 3, 5 , 6 ,7]
array[0], array[1] = array[1] , array[0] #=> [3, 1] 

Ruby'nin bunu nasıl yaptığını merak ediyordum.


10
Teknik olarak Ruby iki değer döndürmez. Sırasıyla iki değişkene atanan bir dizi döndürebilir.
Charles Caldwell

Yanıtlar:


167

Diğer dillerin aksine, Ruby'deki herhangi bir yöntem çağrısının dönüş değeri her zaman bir nesnedir. Bu mümkündür çünkü Ruby'deki her şey gibi, nilkendisi de bir nesnedir.

Göreceğiniz üç temel model var. Belirli bir değer döndürmeyen:

def nothing
end

nothing
# => nil

Tekil bir değer döndürmek:

def single
  1
end

x = single
# => 1

Bu, diğer programlama dillerinden beklediğinizle uyumludur.

Birden fazla dönüş değeri ile uğraşırken işler biraz farklılaşır. Bunların açıkça belirtilmesi gerekir:

def multiple
  return 1, 2
end

x = multiple
# => [ 1, 2 ]
x
# => [ 1, 2 ]

Birden çok değer döndüren bir çağrı yaparken, bunları bağımsız değişkenlere ayırabilirsiniz:

x, y = multiple
# => [ 1, 2 ]
x
# => 1
y
# => 2

Bu strateji, bahsettiğiniz ikame türleri için de işe yarar:

a, b = 1, 2
# => [1, 2]
a, b = b, a
# => [2, 1]
a
# => 2
b
# => 1

8
Açıkça bir dizi döndürebilirsiniz [1, 2]ve bu yukarıdaki örneklerinizle aynı şekilde çalışacaktır.
Hauleth

6
@hauleth İyi bir gözlem. 1,2Kendi başına geçersiz olduğunu açıkça belirtmeliydim , ama ya return 1,2ya [1,2]işe yarıyor.
tadman

51

Hayır, Ruby aslında iki nesnenin döndürülmesini desteklemiyor. (BTW: değişkenleri değil, nesneleri döndürürsünüz. Daha doğrusu, nesnelere işaretçileri döndürürsünüz.)

Bununla birlikte, paralel atamayı destekler. Bir ödevin sağ tarafında birden fazla nesneniz varsa, nesneler şu şekilde toplanır Array:

foo = 1, 2, 3
# is the same as
foo = [1, 2, 3]

Bir atamanın sol tarafında birden fazla "hedef" (değişken veya ayarlayıcı yöntemi) varsa, değişkenler Arraysağ taraftaki bir öğesinin öğelerine bağlanır :

a, b, c = ary
# is the same as
a = ary[0]
b = ary[1]
c = ary[2]

Sağ taraftaki ise değil bir Array, bu kullanarak birine dönüştürülür to_aryyöntemi

a, b, c = not_an_ary
# is the same as
ary = not_an_ary.to_ary
a = ary[0]
b = ary[1]
c = ary[2]

Ve ikisini bir araya getirirsek, bunu elde ederiz

a, b, c = d, e, f
# is the same as
ary = [d, e, f]
a = ary[0]
b = ary[1]
c = ary[2]

Bununla ilgili, bir atamanın sol tarafındaki uyarma operatörüdür. " Sağ taraftaki tüm sol taraftaki öğeleri al" anlamına gelir Array:

a, b, *c = ary
# is the same as
a = ary[0]
b = ary[1]
c = ary.drop(2) # i.e. the rest of the Array

Ve son olarak, paralel atamalar parantezler kullanılarak iç içe yerleştirilebilir:

a, (b, c), d = ary
# is the same as
a = ary[0]
b, c = ary[1]
d = ary[2]
# which is the same as
a = ary[0]
b = ary[1][0]
c = ary[1][1]
d = ary[2]

Ne zaman seni returnbir yöntem ya gelen nextya breakbir blok, Yakut böylece bir atamanın sağ tarafında gibi bu-tür tedavi edecek

return 1, 2
next 1, 2
break 1, 2
# is the same as
return [1, 2]
next [1, 2]
break [1, 2]

Bu arada, bu aynı zamanda yöntemlerin ve blokların parametre listelerinde de çalışır (yöntemler daha katıdır ve daha az katı engeller):

def foo(a, (b, c), d) p a, b, c, d end

bar {|a, (b, c), d| p a, b, c, d }

Örneğin, blokların "daha az katı" olması Hash#eachişe yarayan şeydir . Aslında yieldsa tek iki eleman Arraybloğuna anahtar ve değer, ama genellikle yazma

some_hash.each {|k, v| }

onun yerine

some_hash.each {|(k, v)| }

17

tadman ve Jörg W Mittag Ruby'yi benden daha iyi tanıyor ve cevapları yanlış değil, ancak OP'nin bilmek istediklerini cevapladıklarını sanmıyorum. Yine de sorunun net olmadığını düşünüyorum. Anladığım kadarıyla, OP'nin sormak istediği şeyin birden fazla değeri döndürmekle ilgisi yok.


Asıl soru, iki değişkenin ave b(veya orijinal soruda olduğu gibi bir dizideki iki konumun) değerlerini değiştirmek istediğinizde , aşağıdaki tempgibi geçici bir değişken kullanmak neden gerekli değildir :

a, b = :foo, :bar
temp = a
a = b
b = temp

ancak doğrudan şu şekilde yapılabilir:

a, b = :foo, :bar
a, b = b, a

Cevap, çoklu atamada, sol tarafın tamamı atanmadan önce sağ tarafın tamamı değerlendirilir ve tek tek yapılmaz. Yani a, b = b, aeşdeğer değildir a = b; b = a.

İlk olarak atamadan önce sağ tarafın tamamını değerlendirmek, her iki tarafın =farklı sayıda terime sahip olduğu zaman ayarlamadan sonra gelen bir zorunluluktur ve Jörg W Mittag'ın açıklaması dolaylı olarak bununla ilişkili olabilir, ancak asıl mesele bu değildir.


8

Yalnızca birkaç değeriniz varsa diziler iyi bir seçenektir. Sonuçların sırasını bilmek zorunda kalmadan (ve karıştırmak zorunda kalmadan) birden çok dönüş değeri istiyorsanız, bir alternatif, istediğiniz adlandırılmış değerleri içeren bir Karma döndürmektir.

Örneğin

def make_hash
  x = 1
  y = 2
  {x: x, y: y}
end

hash = make_hash
# => {:x=>1, :y=>2}
hash[:x]
# => 1
hash[:y]
# => 2
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.