Zawsze, gdy zamieniam wartości w tablicy, upewniam się, że zapisałem jedną z wartości w zmiennej referencyjnej. Ale odkryłem, że Ruby może zwracać dwie wartości, a także automatycznie zamieniać dwie wartości. Na przykład,
array = [1, 3, 5 , 6 ,7]
array[0], array[1] = array[1] , array[0] #=> [3, 1]
Zastanawiałem się, jak Ruby to robi.
Odpowiedzi:
W przeciwieństwie do innych języków, wartością zwracaną przez każde wywołanie metody w Rubim jest zawsze obiekt. Jest to możliwe, ponieważ, jak wszystko w Rubim,
nil
sam w sobie jest obiektem.Zobaczysz trzy podstawowe wzory. Nie zwraca żadnej określonej wartości:
def nothing end nothing # => nil
Zwracanie wartości pojedynczej:
def single 1 end x = single # => 1
Jest to zgodne z tym, czego można oczekiwać od innych języków programowania.
Sytuacja wygląda nieco inaczej, gdy mamy do czynienia z wieloma wartościami zwracanymi. Należy je wyraźnie określić:
def multiple return 1, 2 end x = multiple # => [ 1, 2 ] x # => [ 1, 2 ]
Podczas wykonywania wywołania, które zwraca wiele wartości, możesz podzielić je na zmienne niezależne:
x, y = multiple # => [ 1, 2 ] x # => 1 y # => 2
Ta strategia działa również w przypadku tego rodzaju zamian, o których mówisz:
a, b = 1, 2 # => [1, 2] a, b = b, a # => [2, 1] a # => 2 b # => 1
źródło
[1, 2]
i będzie działać tak samo, jak w powyższych przykładach.1,2
sam w sobie jest nieważny, ale albo działa,return 1,2
albo[1,2]
działa.Nie, Ruby w rzeczywistości nie obsługuje zwracania dwóch obiektów. (BTW: zwracasz obiekty, a nie zmienne. Dokładniej, zwracasz wskaźniki do obiektów).
Obsługuje jednak przypisywanie równoległe. Jeśli masz więcej niż jeden obiekt po prawej stronie przydziału, obiekty są gromadzone w
Array
:foo = 1, 2, 3 # is the same as foo = [1, 2, 3]
Jeśli masz więcej niż jeden „cel” (zmienna lub metoda ustawiająca) po lewej stronie przypisania, zmienne zostaną powiązane z elementami
Array
po prawej stronie:a, b, c = ary # is the same as a = ary[0] b = ary[1] c = ary[2]
Jeśli prawa strona nie jest ikoną
Array
, zostanie przekonwertowana na jedną przy użyciuto_ary
metodya, 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 połączymy te dwa elementy, otrzymamy to
a, b, c = d, e, f # is the same as ary = [d, e, f] a = ary[0] b = ary[1] c = ary[2]
Związany z tym jest operator splat po lewej stronie przydziału. To znaczy „weź wszystkie lewe elementy z
Array
prawej strony”: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 wreszcie, równoległe przypisania można zagnieżdżać za 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 ci
return
od sposobu lubnext
lubbreak
z bloku, Ruby będzie traktować tego rodzaju, jakby z prawej strony cesji, więcreturn 1, 2 next 1, 2 break 1, 2 # is the same as return [1, 2] next [1, 2] break [1, 2]
Nawiasem mówiąc, działa to również w przypadku list parametrów metod i bloków (metody są bardziej rygorystyczne, a bloki mniej rygorystyczne):
def foo(a, (b, c), d) p a, b, c, d end bar {|a, (b, c), d| p a, b, c, d }
Na przykład bloki są „mniej rygorystyczne”
Hash#each
. To faktycznieyield
sa pojedyncze dwuelementowaArray
klucza i wartości do bloku, ale zazwyczaj zapisusome_hash.each {|k, v| }
zamiast
some_hash.each {|(k, v)| }
źródło
tadman i Jörg W Mittag znają Ruby lepiej niż ja, a ich odpowiedzi nie są błędne, ale nie sądzę, aby odpowiadali na to, co OP chciał wiedzieć. Myślę, że pytanie nie było jednak jasne. W moim rozumieniu to, o co chciał zapytać OP, nie ma nic wspólnego z zwracaniem wielu wartości.
Prawdziwe pytanie brzmi, kiedy chcesz zamienić wartości dwóch zmiennych
a
ib
(lub dwie pozycje w tablicy, jak w oryginalnym pytaniu), dlaczego nie jest konieczne użycie zmiennej czasowej,temp
takiej 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 przypadku przypisania wielokrotnego cała prawa strona jest oceniana przed przypisaniem całej lewej strony i nie jest wykonywana pojedynczo. Więc
a, b = b, a
nie jest równoważnea = 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ę wyrazów, a opis Jörga W Mittaga może być z tym pośrednio powiązany, ale to nie jest główna kwestia.źródło
Tablice są dobrym rozwiązaniem, jeśli masz tylko kilka wartości. Jeśli chcesz, aby zwracane były wiele wartości bez znajomości (i dezorientacji) kolejności wyników, alternatywą byłoby zwrócenie skrótu zawierającego dowolne nazwane wartości.
na przykład
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
źródło