2015-02-25 13 views
60

Bất cứ khi nào tôi trao đổi giá trị trong một mảng, tôi đảm bảo rằng tôi đã lưu trữ một trong các giá trị trong biến tham chiếu. Nhưng tôi thấy rằng Ruby có thể trả về hai giá trị cũng như tự động hoán đổi hai giá trị. Ví dụ:Ruby trả về hai giá trị như thế nào?

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

Tôi đã tự hỏi làm thế nào Ruby thực hiện việc này.

+8

Về mặt kỹ thuật, Ruby không trả về hai giá trị. Nó có thể trả về một mảng mà lần lượt được gán cho hai biến. –

Trả lời

106

Không giống như các ngôn ngữ khác, giá trị trả về của bất kỳ lệnh gọi phương thức nào trong Ruby là luôn là một đối tượng. Điều này là có thể bởi vì, giống như tất cả mọi thứ trong Ruby, nil chính nó là một đối tượng.

Có ba mẫu cơ bản bạn sẽ thấy. Quay trở lại không có giá trị đặc biệt:

def nothing 
end 

nothing 
# => nil 

Trả về một giá trị đặc biệt:

def single 
    1 
end 

x = single 
# => 1 

này là phù hợp với những gì bạn mong đợi từ ngôn ngữ lập trình khác.

Mọi thứ có đôi chút khác biệt khi xử lý nhiều giá trị trả lại. Những cần phải được xác định một cách rõ ràng:

def multiple 
    return 1, 2 
end 

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

Khi thực hiện cuộc gọi trả về nhiều giá trị, bạn có thể phá vỡ chúng ra thành các biến độc lập:

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

Chiến lược này cũng làm việc cho các loại thay bạn' đang nói về:

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

Bạn ca explictly trả về một mảng '[1, 2]' và điều này sẽ làm việc giống như ví dụ của bạn ở trên. – Hauleth

+4

@hauleth Một quan sát tốt. Tôi nên nói rõ rằng bản thân '1,2' không hợp lệ, nhưng một trong hai tác phẩm' return 1,2' hoặc '[1,2]'. – tadman

37

Không, Ruby không thực sự hỗ trợ trả lại hai đối tượng. (BTW: bạn trả về các đối tượng, không phải biến. Chính xác hơn, bạn trả lại con trỏ cho các đối tượng.)

Tuy nhiên, nó hỗ trợ gán song song. Nếu bạn có nhiều hơn một đối tượng ở phía bên tay phải của một bài tập, các đối tượng được thu thập vào một Array:

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

Nếu bạn có nhiều hơn một "mục tiêu" (phương pháp biến hoặc setter) ở bên trái bên -Tay của một bài tập, các biến được ràng buộc với các yếu tố của một Array ở phía bên tay phải:

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

Nếu phía bên tay phải là không một Array, nó sẽ được chuyển đổi sang một sử dụng phương thức 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] 

Và nếu chúng ta đặt hai cùng nhau, chúng tôi nhận được rằng

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

liên quan vấn đề này là các nhà điều hành splat ở phía bên trái của một bài tập.Nó có nghĩa là "mất tất cả các yếu tố còn sót lại của Array ở phía bên tay phải":

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 

Và cuối cùng nhưng không kém, nhiệm vụ song song có thể được lồng vào nhau bằng dấu ngoặc đơn:

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] 

Khi bạn return từ một phương pháp hay next hoặc break từ một khối hoặc yield đến một khối, Ruby sẽ đối xử tử tế của việc này như phía bên tay phải của một bài tập, vì vậy

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

Bằng cách này, điều này cũng làm việc trong danh sách tham số của phương pháp và khối (với các phương pháp là nghiêm ngặt hơn và khối ít nghiêm ngặt):

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

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

Blocks là "ít nghiêm ngặt" là ví dụ những gì làm cho Hash#each công việc. Nó thực sự yield sa đơn hai yếu tố Array khóa và giá trị cho các khối, nhưng chúng ta thường viết

some_hash.each {|k, v| } 

thay vì

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

tadman và Jörg W Mittag biết Ruby tốt hơn tôi, và câu trả lời của họ không sai, nhưng tôi không nghĩ họ trả lời những gì OP muốn biết. Tôi nghĩ rằng câu hỏi không rõ ràng. Theo hiểu biết của tôi, những gì OP muốn hỏi không liên quan gì đến việc trả về nhiều giá trị.


Câu hỏi thực sự là, khi bạn muốn chuyển đổi các giá trị của hai biến ab (hoặc hai vị trí trong một mảng như trong câu hỏi ban đầu), tại sao nó không cần thiết phải sử dụng một biến tạm thời temp như:

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

nhưng có thể được thực hiện trực tiếp như:

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

Câu trả lời là trong nhiều nhiệm vụ, toàn bộ bên tay phải được đánh giá trước khi chuyển nhượng toàn bộ bên trái, và nó không được thực hiện từng cái một. Vì vậy, a, b = b, a không tương đương với a = b; b = a.

Đầu tiên đánh giá toàn bộ bên phải trước khi gán là điều cần thiết sau khi điều chỉnh khi cả hai bên của = có các số hạng khác nhau và mô tả của Jörg W Mittag có thể liên quan gián tiếp đến điều đó, nhưng đó không phải là chính vấn đề.

1

Mảng là một lựa chọn tốt nếu bạn chỉ có một vài giá trị. Nếu bạn muốn nhiều giá trị trả lại mà không cần phải biết (và bị nhầm lẫn bởi) thứ tự của kết quả, một thay thế sẽ là trả về một Hash có chứa bất kỳ giá trị được đặt tên nào bạn muốn.

ví dụ:

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 
Các vấn đề liên quan