Ruby คืนค่าสองค่าอย่างไร?


95

เมื่อใดก็ตามที่ฉันสลับค่าในอาร์เรย์ฉันต้องแน่ใจว่าฉันเก็บค่าใดค่าหนึ่งไว้ในตัวแปรอ้างอิง แต่ฉันพบว่า Ruby สามารถคืนค่าสองค่าและสลับสองค่าโดยอัตโนมัติ ตัวอย่างเช่น,

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

ฉันสงสัยว่ารูบี้ทำสิ่งนี้ได้อย่างไร


9
ในทางเทคนิค Ruby จะไม่คืนค่าสองค่า มันสามารถส่งคืนหนึ่งอาร์เรย์ซึ่งจะถูกกำหนดให้กับสองตัวแปร
Charles Caldwell

คำตอบ:


166

ซึ่งแตกต่างจากภาษาอื่น ๆ ค่าที่ส่งคืนของการเรียกใช้เมธอดใด ๆ ใน Ruby จะเป็นวัตถุเสมอ สิ่งนี้เป็นไปได้เพราะทุกอย่างใน Ruby nilก็เป็นวัตถุ

คุณจะเห็นรูปแบบพื้นฐานสามแบบ ไม่ส่งคืนค่าเฉพาะ:

def nothing
end

nothing
# => nil

การส่งคืนค่าเอกพจน์:

def single
  1
end

x = single
# => 1

สิ่งนี้สอดคล้องกับสิ่งที่คุณคาดหวังจากภาษาโปรแกรมอื่น ๆ

สิ่งต่างๆจะแตกต่างกันเล็กน้อยเมื่อจัดการกับค่าที่ส่งคืนหลายค่า สิ่งเหล่านี้จำเป็นต้องระบุไว้อย่างชัดเจน:

def multiple
  return 1, 2
end

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

เมื่อทำการโทรที่ส่งคืนค่าหลายค่าคุณสามารถแบ่งออกเป็นตัวแปรอิสระ:

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

กลยุทธ์นี้ยังใช้ได้กับการทดแทนประเภทต่างๆที่คุณกำลังพูดถึง:

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

8
คุณส่งคืนอาร์เรย์อย่างชัดเจน[1, 2]และจะใช้งานได้เหมือนกับตัวอย่างด้านบน
Hauleth

6
@hauleth สังเกตดีๆ. ฉันควรแจ้งให้ชัดเจนว่า1,2โดยตัวมันเองนั้นไม่ถูกต้อง แต่อย่างใดอย่างหนึ่งreturn 1,2หรือ[1,2]ทำงาน
tadman

51

ไม่จริง Ruby ไม่สนับสนุนการส่งคืนวัตถุสองชิ้น (BTW: คุณส่งคืนอ็อบเจ็กต์ไม่ใช่ตัวแปรแม่นยำยิ่งขึ้นคุณส่งคืนพอยน์เตอร์ไปยังอ็อบเจ็กต์)

อย่างไรก็ตามสนับสนุนการมอบหมายงานคู่ขนาน หากคุณมีวัตถุมากกว่าหนึ่งชิ้นทางด้านขวามือของงานที่มอบหมายวัตถุจะถูกรวบรวมไว้ในArray:

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

หากคุณมี "เป้าหมาย" (วิธีตัวแปรหรือตัวตั้งค่า) มากกว่าหนึ่งรายการทางด้านซ้ายมือของการกำหนดตัวแปรจะถูกผูกไว้กับองค์ประกอบของArrayทางด้านขวามือ:

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

ถ้าด้านขวามือไม่ใช่ an ระบบArrayจะแปลงเป็นด้านขวาโดยใช้to_aryวิธี

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]

และถ้าเรารวมทั้งสองอย่างเข้าด้วยกันเราจะได้สิ่งนั้น

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

ที่เกี่ยวข้องคือตัวดำเนินการ Splat ทางด้านซ้ายมือของงาน หมายความว่า "นำองค์ประกอบด้านซ้ายทั้งหมดของทางด้าน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

และสุดท้าย แต่ไม่ท้ายสุดการกำหนดแบบขนานสามารถซ้อนกันได้โดยใช้วงเล็บ:

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]

เมื่อคุณreturnจากวิธีการหรือnextหรือbreakจากบล็อก Ruby จะปฏิบัติต่อสิ่งนี้เหมือนทางด้านขวามือของงานดังนั้น

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

อย่างไรก็ตามสิ่งนี้ยังใช้งานได้ในรายการพารามิเตอร์ของวิธีการและบล็อก (ด้วยวิธีการที่เข้มงวดกว่าและบล็อกที่เข้มงวดน้อยกว่า):

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

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

การบล็อกที่ "เข้มงวดน้อยกว่า" คือสิ่งที่ทำให้ได้Hash#eachผล มันจริงyieldSA เดียวสององค์ประกอบArrayของคีย์และความคุ้มค่าที่จะบล็อก แต่เรามักจะเขียน

some_hash.each {|k, v| }

แทน

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

17

tadman และJörg W Mittag รู้จัก Ruby ดีกว่าฉันและคำตอบของพวกเขาก็ไม่ผิด แต่ฉันไม่คิดว่าพวกเขาจะตอบในสิ่งที่ OP อยากรู้ ฉันคิดว่าคำถามยังไม่ชัดเจน ในความเข้าใจของฉันสิ่งที่ OP ต้องการถามไม่มีส่วนเกี่ยวข้องกับการคืนค่าหลายค่า


คำถามที่แท้จริงคือเมื่อคุณต้องการเปลี่ยนค่าของสองตัวแปรaและb(หรือสองตำแหน่งในอาร์เรย์เช่นเดียวกับคำถามเดิม) เหตุใดจึงไม่จำเป็นต้องใช้ตัวแปรชั่วคราวtempเช่น:

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

แต่สามารถทำได้โดยตรงเช่น:

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

คำตอบคือในการมอบหมายงานหลาย ๆ งานด้านขวามือทั้งหมดจะได้รับการประเมินก่อนการมอบหมายด้านซ้ายมือทั้งหมดและจะไม่ทำทีละงาน ดังนั้นจะไม่เทียบเท่ากับa, b = b, aa = b; b = a

อันดับแรกการประเมินด้านขวามือทั้งหมดก่อนการมอบหมายงานเป็นสิ่งจำเป็นที่ตามมาจากการปรับเปลี่ยนเมื่อทั้งสองฝ่าย=มีจำนวนคำที่แตกต่างกันและคำอธิบายของJörg W Mittag อาจเกี่ยวข้องโดยอ้อม แต่นั่นไม่ใช่ประเด็นหลัก


8

อาร์เรย์เป็นตัวเลือกที่ดีหากคุณมีค่าเพียงไม่กี่ค่า หากคุณต้องการส่งคืนค่าหลายค่าโดยไม่ต้องรู้ (และสับสน) ลำดับของผลลัพธ์อีกทางเลือกหนึ่งคือส่งคืน Hash ที่มีค่าที่ตั้งชื่อตามที่คุณต้องการ

เช่น

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.