2015-02-25 15 views
60

Ilekroć zamieniam wartości w tablicy, upewniam się, czy zapisałem jedną z wartości w zmiennej referencyjnej. Ale odkryłem, że Ruby może zwrócić dwie wartości, a także automatycznie zamienić dwie wartości. Na przykład:W jaki sposób Ruby zwraca dwie wartości?

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

Zastanawiam się, jak to robi Ruby.

+8

Technicznie Ruby nie zwraca dwóch wartości. Może zwrócić jedną tablicę, która z kolei zostanie przypisana do dwóch zmiennych. –

Odpowiedz

106

W odróżnieniu od innych języków, wartość zwracana każdego wywołania metody w Ruby to zawsze obiekt. Jest to możliwe, ponieważ, podobnie jak wszystko w Rubim, sam obiekt jest obiektem nil.

Widoczne są trzy podstawowe wzory. Wracając ma szczególną wartość:

def nothing 
end 

nothing 
# => nil 

powrocie wartość pojedynczej:

def single 
    1 
end 

x = single 
# => 1 

Jest to zgodne z tym, co można oczekiwać od innych języków programowania.

Rzeczy stają się nieco inne, gdy mamy do czynienia z wieloma wartościami zwracanymi. Muszą one być określone jednoznacznie:

def multiple 
    return 1, 2 
end 

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

Podczas wykonywania połączenia zwracającą wiele wartości, można podzielić je na zmienne niezależne:

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

Strategia ta działa również dla rodzaju podstawienia you” ponownie mówimy o:

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

Powinieneś zwrócić tablicę '[1, 2]' i będzie działać tak samo jak twoje przykłady powyżej. – Hauleth

+4

@hauleth Dobra obserwacja. Powinienem był wyjaśnić, że "1,2" samo w sobie jest nieważne, ale jeden z "powrotu 1,2" lub "[1,2]" działa. – tadman

37

Nie, Ruby w rzeczywistości nie obsługuje zwracania dwóch obiektów. (BTW: zwracasz obiekty, nie zmienne. Dokładniej, zwracasz wskaźniki do obiektów.)

Obsługuje jednak równoległe przypisanie. Jeśli masz więcej niż jeden obiekt po stronie prawej cesji, obiekty są zbierane w produkt Array:

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

jeśli masz więcej niż jeden „cel” (metoda zmienna lub setter) po lewej stronie -hand boku zadania, zmienne się zobowiązany do elementów o Array na prawej stronie:

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

Jeśli po prawej stronie jest nieArray, zostanie zamienione na jeden przy użyciu the to_ary method

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 jeśli umieścić dwa razem, otrzymujemy że

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

związane z tym jest operatorem ikona z lewej strony cesji.To znaczy „wziąć wszystko lewej nad elementów Array na prawej stronie”:

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 

i last but not least, zadania równoległe mogą być zagnieżdżone pomocą nawiasów:

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] 

Kiedy return od sposobu lub next lub break z bloku lub yield do bloku, Ruby będzie traktować tego rodzaju-jakby z prawej strony cesji, więc

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] 

Nawiasem mówiąc, to działa także w listach parametrów metod i bloków (z metody są bardziej rygorystyczne i blokuje mniej rygorystyczne):

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

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

bloki są „mniej ścisły” jest na przykład to, co sprawia, że ​​Hash#each praca. To faktycznie yield sa pojedynczy dwuelementowa Array od klucza i wartości do bloku, ale zwykle napisać

some_hash.each {|k, v| } 

zamiast

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

tadman i Jörg W Mittag wiedzieć Ruby lepiej niż ja, i ich odpowiedzi nie są złe, ale nie sądzę, że odpowiadają na to, co OP chciał wiedzieć. Myślę, że pytanie nie było jednak jasne. W moim rozumieniu to, co OP chciał zadać, nie ma nic wspólnego z zwracaniem wielu wartości.


Prawdziwym pytaniem jest, gdy chcesz przełączyć wartości dwóch zmiennych a i b (lub dwóch pozycjach w szyku, jak w oryginalnym pytanie), to dlaczego nie jest to konieczne, aby użyć zmiennej czasowej temp jak:

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

ale można to zrobić bezpośrednio jak:

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

odpowiedź jest taka, że ​​w stwardnieniu zadania, cała prawa strona jest oceniana przed przypisaniem całej lewej strony i nie jest wykonywana jedna po drugiej. Więc a, b = b, a nie jest równoważne z a = b; b = a.

Pierwsza ocena całej prawej strony przed przypisaniem jest koniecznością wynikającą z dostosowania, gdy obie strony = mają różną liczbę terminów, a opis Jörga W Mittaga może być pośrednio z tym powiązany, ale to nie jest główny kwestia.

1

Tablice są dobrym rozwiązaniem, jeśli masz tylko kilka wartości. Jeśli chcesz uzyskać wiele wartości zwracanych bez konieczności poznania (i zmieszania) kolejności wyników, alternatywą może być zwrócenie skrótu zawierającego wszystkie żądane wartości.

np.

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 
Powiązane problemy