Czy Ruby przechodzi przez referencję czy wartość?

248
@user.update_languages(params[:language][:language1], 
                       params[:language][:language2], 
                       params[:language][:language3])
lang_errors = @user.errors
logger.debug "--------------------LANG_ERRORS----------101-------------" 
                + lang_errors.full_messages.inspect

if params[:user]
  @user.state = params[:user][:state]
  success = success & @user.save
end
logger.debug "--------------------LANG_ERRORS-------------102----------" 
                + lang_errors.full_messages.inspect

if lang_errors.full_messages.empty?

@userobiekt dodaje błędy do lang_errorszmiennej w update_lanugagesmetodzie. kiedy wykonuję zapis @userobiektu, tracę błędy, które początkowo były przechowywane w lang_errorszmiennej.

Chociaż to, co próbuję zrobić, byłoby bardziej włamaniem (co wydaje się nie działać). Chciałbym zrozumieć, dlaczego wartości zmiennych są wypłukiwane. Rozumiem przekazanie przez odniesienie, więc chciałbym wiedzieć, jak wartość może być utrzymywana w tej zmiennej bez wymywania.

Sid
źródło
Zauważam również, że jestem w stanie zachować tę wartość w sklonowanym obiekcie
Sid
1
Powinieneś spojrzeć na odpowiedź Abe Voelkera. Ale po obejściu tego bloku, oto jak bym to powiedział. kiedy przekazujesz Foo obiektu do procedury, przekazywana jest kopia odwołania do obiektu, pasek, Przekaż według wartości. nie możesz zmienić obiektu, na który wskazuje Foo, ale możesz zmienić zawartość obiektu, na który wskazuje. Jeśli więc przekażesz tablicę, zawartość tablicy można zmienić, ale nie możesz zmienić, do której tablicy się odwołuje. miło jest móc korzystać z metod Foo bez obawy o zepsucie innych zależności od Foo.
bobbdelsol

Odpowiedzi:

244

W tradycyjnej terminologii Ruby jest ściśle przekazywana według wartości . Ale tak naprawdę nie o to tu pytasz.

Ruby nie ma pojęcia o wartości czystej, bez odniesienia, więc z pewnością nie można przekazać żadnej metody. Zmienne są zawsze odniesieniami do obiektów. Aby uzyskać obiekt, który nie zmieni się spod ciebie, musisz zduplikować lub sklonować przekazany obiekt, dając w ten sposób obiekt, do którego nikt inny nie ma odniesienia. (Nawet to nie jest kuloodporne - obie standardowe metody klonowania wykonują płytką kopię, więc zmienne instancji klonu nadal wskazują na te same obiekty, które zrobiły oryginały. Jeśli obiekty, do których odwołują się ivars, mutują, to będzie nadal pojawiają się w kopii, ponieważ odnoszą się do tych samych obiektów).

Głaskanie pod brodę
źródło
88
Ruby jest wartością przekazywaną . Nie, jeśli. Żadnych ale. Bez wyjątków. Jeśli chcesz wiedzieć, czy Ruby (lub dowolny inny język) jest przekazywany przez referencję lub przekazywany przez wartość , po prostu go wypróbować: def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}".
Jörg W Mittag
95
@ JörgWMittag: Tak, ale zamieszanie PO nie jest w rzeczywistości przekazywaniem wartości lub przekazywaniem referencji w ścisłym znaczeniu tego słowa. Brakowało mu tylko tego, że przekazywane przez ciebie „wartości” odniesieniami. Wydawało mi się, że samo powiedzenie „To wartość dodana” byłoby pedantyczne i uczyniłoby OP niekorzystną, ponieważ tak naprawdę nie miał na myśli tego. Ale dziękuję za wyjaśnienie, ponieważ jest to ważne dla przyszłych czytelników i powinienem je uwzględnić. (Zawsze jestem rozdarty między podawaniem większej ilości informacji a nie myleniem ludzi.)
Chuck,
16
Nie zgadzam się z @Jorg. Ruby jest przekazywany przez referencję, po prostu zmienia referencję. Spróbuj zamiast tego: def foo (bar) bar.replace 'reference' end; baz = „wartość”; foo (baz); wstawia „Ruby is pass-by - # {baz}”
pguardiario
15
@pguardiario: Myślę, że to naprawdę tylko kwestia definicji. Używasz definicji „pass-by-reference”, którą sam wymyśliłeś, podczas gdy Jörg stosuje tradycyjną definicję informatyki. Oczywiście, moim zadaniem nie jest mówić ci, jak używać słów - myślę, że ważne jest, aby wyjaśnić, co to normalnie oznacza. W tradycyjnej terminologii Ruby jest wartością przekazywaną, ale same wartości są odniesieniami. Całkowicie rozumiem, dlaczego ty i OP lubicie myśleć o tym jako o odnośniku - to nie jest tradycyjne znaczenie tego terminu.
Chuck,
7
Wszystko w Ruby jest obiektem, więc Ruby nie jest przekazywane ani przez wartość, ani przez referencję, przynajmniej w tym sensie, że te terminy są używane w C ++. „pass by object reference” może być lepszym sposobem na opisanie tego, co robi Ruby. Ostatecznie jednak najlepszym rozwiązaniem może być nie nadawanie zbyt dużego znaczenia żadnym z tych terminów i po prostu dobre zrozumienie zachowań, które naprawdę się zdarzają.
David Winiecki
424

Pozostałe odpowiedzi są prawidłowe, ale przyjaciel poprosił mnie, żebym mu to wytłumaczył, a tak naprawdę sprowadza się to do tego, jak Ruby obsługuje zmienne, więc pomyślałem, że podzielę się kilkoma prostymi zdjęciami / wyjaśnieniami, które dla niego napisałem (przepraszam za długość i prawdopodobnie pewne uproszczenia):


P1: Co się stanie, gdy przypiszesz nową zmienną strdo wartości 'foo'?

str = 'foo'
str.object_id # => 2000

wprowadź opis zdjęcia tutaj

Odp .: Wywoływana strjest etykieta wskazująca obiekt 'foo', który dla stanu tego interpretera języka Ruby znajduje się w miejscu pamięci 2000.


P2: Co się stanie, gdy przypiszesz istniejącą zmienną strdo nowego obiektu za pomocą =?

str = 'bar'.tap{|b| puts "bar: #{b.object_id}"} # bar: 2002
str.object_id # => 2002

wprowadź opis zdjęcia tutaj

Odp .: Etykieta strwskazuje teraz na inny obiekt.


Q3: Co się dzieje, kiedy przypisać nową zmienną =do str?

str2 = str
str2.object_id # => 2002

wprowadź opis zdjęcia tutaj

Odp .: Tworzona jest nowa etykieta o nazwie, str2która wskazuje na ten sam obiekt co str.


P4: Co się stanie, jeśli obiekt odwołuje się do str i str2zostanie zmieniony?

str2.replace 'baz'
str2 # => 'baz'
str  # => 'baz'
str.object_id # => 2002
str2.object_id # => 2002

wprowadź opis zdjęcia tutaj

Odp .: Obie etykiety nadal wskazują ten sam obiekt, ale sam obiekt zmutował (jego zawartość zmieniła się na coś innego).


Jak to się ma do pierwotnego pytania?

Jest to w zasadzie to samo, co dzieje się w Q3 / Q4; metoda otrzymuje własną prywatną kopię zmiennej / label ( str2), która jest do niej przekazywana ( str). Nie może zmienić obiektu, na który str wskazuje etykieta , ale może zmienić zawartość obiektu, do którego odwołują się oboje, aby zawierał jeszcze:

str = 'foo'

def mutate(str2)
  puts "str2: #{str2.object_id}"
  str2.replace 'bar'
  str2 = 'baz'
  puts "str2: #{str2.object_id}"
end

str.object_id # => 2004
mutate(str) # str2: 2004, str2: 2006
str # => "bar"
str.object_id # => 2004
Abe Voelker
źródło
1
Robert Heaton także ostatnio pisał o tym na blogu
Michael Renner,
48

Ruby używa „przekaż odniesienie do obiektu”

(Korzystanie z terminologii Python.)

Powiedzenie, że Ruby używa „przekaż przez wartość” lub „przekaż przez referencję”, nie jest tak naprawdę wystarczająco opisowe, aby było pomocne. Myślę, że jak większość ludzi wie to w dzisiejszych czasach, terminologia („wartość” vs „referencja”) pochodzi z C ++.

W C ++ „przekazanie przez wartość” oznacza, że ​​funkcja pobiera kopię zmiennej, a wszelkie zmiany w kopii nie zmieniają oryginału. Dotyczy to również obiektów. Jeśli przekażesz zmienną obiektową według wartości, cały obiekt (w tym wszystkie jego elementy) zostanie skopiowany, a wszelkie zmiany w elementach członkowskich nie zmienią tych elementów w oryginalnym obiekcie. (Jest inaczej, jeśli przekażesz wskaźnik wartości, ale Ruby i tak nie ma wskaźników, AFAIK.)

class A {
  public:
    int x;
};

void inc(A arg) {
  arg.x++;
  printf("in inc: %d\n", arg.x); // => 6
}

void inc(A* arg) {
  arg->x++;
  printf("in inc: %d\n", arg->x); // => 1
}

int main() {
  A a;
  a.x = 5;
  inc(a);
  printf("in main: %d\n", a.x); // => 5

  A* b = new A;
  b->x = 0;
  inc(b);
  printf("in main: %d\n", b->x); // => 1

  return 0;
}

Wynik:

in inc: 6
in main: 5
in inc: 1
in main: 1

W C ++ „przekazanie przez odniesienie” oznacza, że ​​funkcja uzyskuje dostęp do oryginalnej zmiennej. Może przypisać zupełnie nową literalną liczbę całkowitą, a oryginalna zmienna również będzie miała tę wartość.

void replace(A &arg) {
  A newA;
  newA.x = 10;
  arg = newA;
  printf("in replace: %d\n", arg.x);
}

int main() {
  A a;
  a.x = 5;
  replace(a);
  printf("in main: %d\n", a.x);

  return 0;
}

Wynik:

in replace: 10
in main: 10

Ruby używa przekazywania przez wartość (w sensie C ++), jeśli argument nie jest obiektem. Ale w Ruby wszystko jest przedmiotem, więc tak naprawdę w Ruby nie ma przekazywania wartości w sensie C ++.

W Ruby używa się „pass by object reference” (w celu użycia terminologii Pythona):

  • Wewnątrz funkcji każdemu członkowi obiektu można przypisać nowe wartości, a zmiany te zostaną zachowane po powrocie funkcji. *
  • Wewnątrz funkcji przypisanie całego nowego obiektu do zmiennej powoduje, że zmienna przestaje odwoływać się do starego obiektu. Ale po powrocie funkcji oryginalna zmienna nadal będzie odwoływać się do starego obiektu.

Dlatego Ruby nie używa „przekaż przez referencję” w sensie C ++. Jeśli tak, to przypisanie nowego obiektu do zmiennej wewnątrz funkcji spowoduje, że stary obiekt zostanie zapomniany po powrocie funkcji.

class A
  attr_accessor :x
end

def inc(arg)
  arg.x += 1
  puts arg.x
end

def replace(arg)
  arg = A.new
  arg.x = 3
  puts arg.x
end

a = A.new
a.x = 1
puts a.x  # 1

inc a     # 2
puts a.x  # 2

replace a # 3
puts a.x  # 2

puts ''

def inc_var(arg)
  arg += 1
  puts arg
end

b = 1     # Even integers are objects in Ruby
puts b    # 1
inc_var b # 2
puts b    # 1

Wynik:

1
2
2
3
2

1
2
1

* Dlatego w Rubim, jeśli chcesz zmodyfikować obiekt wewnątrz funkcji, ale zapominasz o tych zmianach po powrocie funkcji, musisz jawnie wykonać kopię obiektu przed dokonaniem tymczasowych zmian w kopii.

David Winiecki
źródło
Twoja odpowiedź jest najlepsza. Chcę też opublikować prosty przykład def ch(str) str.reverse! end; str="abc"; ch(str); puts str #=> "cba"
fangxing
To poprawna odpowiedź! Jest to również bardzo dobrze wyjaśnione tutaj: robertheaton.com/2014/07/22/… . Ale co ja nadal nie rozumiem, to: def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}". Wypisuje „Ruby jest wartością przekazaną”. Ale zmienna wewnątrz foojest ponownie przypisana. Jeśli barbyłaby tablicą, zmiana przypisania nie miałaby wpływu baz. Czemu?
haffla
Nie rozumiem twojego pytania. Myślę, że powinieneś zadać zupełnie nowe pytanie zamiast zadawać tutaj komentarze.
David Winiecki
42

Czy Ruby przechodzi przez referencję czy wartość?

Ruby jest wartością przekazywaną. Zawsze. Bez wyjątków. Nie, jeśli. Żadnych ale.

Oto prosty program, który pokazuje ten fakt:

def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value
Jörg W Mittag
źródło
15
@DavidJ .: „Błąd polega na tym, że parametr lokalny został ponownie przypisany (wskazał na nowe miejsce w pamięci)” - To nie jest błąd, to jest definicja parametru pass-by-value . Gdyby Ruby była przekazywana przez odniesienie, wówczas ponowne przypisanie do argumentu metody lokalnej wiążącej w odbiorcy również przypisałoby przypisanie zmiennej lokalnej w wywołującym. Który nie zrobił. Ergo, Ruby jest wartością przekazywaną. Fakt, że jeśli zmienisz zmienną wartość, zmiana wartości jest zupełnie nieistotna, tak właśnie działa stan zmienny. Ruby nie jest czysto funkcjonalnym językiem.
Jörg W Mittag
5
Dziękujemy Jörgowi za obronę prawdziwej definicji „pass-by-value”. Wyraźnie topi nasz mózg, gdy wartość jest w rzeczywistości odniesieniem, chociaż rubin zawsze przechodzi przez wartość.
Douglas,
9
To jest sofistyka. Praktyczne rozróżnienie między „przekazywaniem przez wartość” a „przekazywaniem przez odniesienie” jest semantyczne, a nie składniowe. Czy powiedziałbyś, że tablice C są wartościami przekazywanymi? Oczywiście, że nie, nawet jeśli przekazujesz nazwę tablicy do funkcji, przekazujesz niezmienny wskaźnik i tylko dane, do których odnosi się wskaźnik, mogą być mutowane. Wyraźnie typy wartości w Rubim są przekazywane przez wartość, a typy referencyjne są przekazywane przez referencję.
dodgethesteamroller
3
@dodgethesteamroller: Zarówno Ruby, jak i C są wartościami przekazywanymi. Zawsze. Bez wyjątków, jeśli nie ma żadnych ale. Rozróżnienie między wartością przekazywaną a wartością referencyjną polega na tym, czy wartość jest wskazywana przez odniesienie, czy przekazywana przez odniesienie. C zawsze przekazuje wartość, nigdy referencję. Wartość może lub nie może być wskaźnikiem, ale co wartość to nie ma znaczenia, czy jest ono przekazywane w pierwszej kolejności. Ruby zawsze przekazuje wartość, nigdy referencję. Ta wartość jest zawsze wskaźnikiem, ale znowu jest to nieistotne.
Jörg W Mittag
44
Ta odpowiedź, a ściśle mówiąc prawda , nie jest bardzo przydatna . Fakt, że przekazywana wartość jest zawsze wskaźnikiem, nie jest bez znaczenia. Jest to źródłem zamieszania dla osób, które próbują się uczyć, a twoja odpowiedź absolutnie nic nie pomaga w tym zamieszaniu.
20

Ruby jest wartością przekazywaną w ścisłym tego słowa znaczeniu, ALE wartości są referencjami.

Można to nazwać „ pass-reference-by-value ”. Ten artykuł ma najlepsze wyjaśnienie, jakie przeczytałem: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/

Przekazywanie według wartości można krótko wyjaśnić w następujący sposób:

Funkcja otrzymuje odniesienie do (i uzyskuje dostęp) tego samego obiektu w pamięci, z którego korzysta osoba wywołująca. Jednak nie otrzymuje skrzynki, w której dzwoniący przechowuje ten obiekt; podobnie jak w przypadku przekazywania wartości po wartości, funkcja zapewnia własne pole i tworzy dla siebie nową zmienną.

Wynikowe zachowanie jest w rzeczywistości kombinacją klasycznych definicji pass-by-reference i pass-by-value.

Ari
źródło
„pass referencja przez wartość” to ta sama fraza, której używam do opisania argumentu Ruby. Myślę, że jest to najbardziej dokładne i zwięzłe zdanie.
Wayne Conrad,
16

Istnieją już świetne odpowiedzi, ale chcę opublikować definicję pary autorytetów na ten temat, ale mam również nadzieję, że ktoś może wyjaśnić, co autorytety Matz (twórca Ruby) i David Flanagan mieli na myśli w swojej doskonałej książce O'Reilly, Język programowania Ruby .

[z 3.8.1: Odwołania do obiektów]

Gdy przekazujesz obiekt do metody w Ruby, jest to odwołanie do obiektu przekazywane do metody. To nie jest sam obiekt i nie jest odniesieniem do odniesienia do obiektu. Innym sposobem na powiedzenie tego jest to, że argumenty metody są przekazywane raczej przez wartość niż przez referencję , ale przekazywane wartości są referencjami obiektowymi.

Ponieważ odwołania do obiektów są przekazywane do metod, metody mogą je wykorzystywać do modyfikowania obiektu bazowego. Te modyfikacje są następnie widoczne po powrocie metody.

To wszystko ma dla mnie sens aż do ostatniego akapitu, a zwłaszcza do ostatniego zdania. Jest to co najmniej wprowadzające w błąd, a co gorsza. W jaki sposób modyfikacje odwołania do wartości przekazywanej przez wartość mogłyby zmienić obiekt leżący u podstaw?

Dominick
źródło
1
Ponieważ referencja nie jest modyfikowana; podstawowym obiektem jest.
dodgethesteamroller
1
Ponieważ obiekt jest zmienny. Ruby nie jest językiem czysto funkcjonalnym. Jest to całkowicie ortogonalne względem przekazywania przez odniesienie w porównaniu do przekazywania przez wartość (z wyjątkiem tego, że w czysto funkcjonalnym języku przekazywanie przez wartość i przekazywanie przez odniesienie zawsze dają takie same wyniki, więc język mógłby użyj jednego lub obu bez Twojej wiedzy).
Jörg W Mittag,
Dobrym przykładem byłoby, gdyby zamiast przypisania zmiennej w funkcji, przyjrzeć się przypadkowi przekazania skrótu do funkcji i scalenia! na przekazanym skrócie. Oryginalny skrót zostaje zmodyfikowany.
elc
16

Czy Ruby przechodzi przez referencję czy wartość?

Ruby jest przekazywany przez odniesienie. Zawsze. Bez wyjątków. Nie, jeśli. Żadnych ale.

Oto prosty program, który pokazuje ten fakt:

def foo(bar)
  bar.object_id
end

baz = 'value'

puts "#{baz.object_id} Ruby is pass-by-reference #{foo(baz)} because object_id's (memory addresses) are always the same ;)"

=> 2279146940 Ruby jest referencją 2279146940, ponieważ identyfikator_obiektu (adresy pamięci) jest zawsze taki sam;)

def bar(babar)
  babar.replace("reference")
end

bar(baz)

puts "some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-#{baz}"

=> niektórzy ludzie nie zdają sobie sprawy, że jest to odniesienie, ponieważ przypisanie lokalne może mieć pierwszeństwo, ale wyraźnie jest to przejście przez odniesienie

Brett Allred
źródło
To jedyna poprawna odpowiedź i przedstawia kilka fajnych gotch: Spróbuj a = 'foobar'; b = a; b [5] = „z”, zarówno a i b zostaną zmodyfikowane.
Martijn
2
@Martijn: twój argument nie jest całkowicie poprawny. Przejrzyjmy twój kod po instrukcji. a = „foobar” tworzy nowe odniesienie wskazujące na „foobar”. b = a tworzy drugie odniesienie do tych samych danych co a. b [5] = „z” zmienia szósty znak wartości, do której odnosi się b, na „z” (wartość, która przypadkowo jest również określana przez a, zostaje zmieniona). Dlatego „oba modyfikują się”, a ściślej mówiąc, „wartość, do której odnoszą się obie zmienne, ulega modyfikacji”.
Lukas_Skywalker
2
Nic nie robisz z odniesieniem w barmetodzie. Po prostu modyfikujesz obiekt, na który wskazuje odwołanie , ale nie samo odwołanie. Jedynym sposobem modyfikowania referencji w Ruby jest przypisanie. Nie można modyfikować referencji, wywołując metody w Rubim, ponieważ metody można wywoływać tylko dla obiektów, a referencje nie są obiektami w Rubim. Twój przykładowy kod pokazuje, że Ruby ma wspólny stan mutable (który nie jest tutaj omawiany), nie robi to jednak nic, aby rozróżnić różnicę między wartością pass-by-pass-by-referencją.
Jörg W Mittag
1
Gdy ktoś pyta, czy język jest „przekazywany przez odniesienie”, zwykle chce wiedzieć, kiedy przekazujesz coś do funkcji, a funkcja ją modyfikuje, czy zostanie ona zmodyfikowana poza funkcją. Dla Ruby odpowiedź brzmi „tak”. Ta odpowiedź jest pomocna w wykazaniu, że odpowiedź @ JörgWMittag jest wyjątkowo nieprzydatna.
Toby 1 Kenobi,
@ Toby1Kenobi: Oczywiście możesz swobodnie korzystać z własnej definicji terminu „pass-by-value”, która różni się od powszechnie stosowanej definicji. Jeśli jednak to zrobisz, powinieneś być przygotowany na zamieszanie, szczególnie jeśli zaniedbujesz ujawnienie faktu, że mówisz o czymś zupełnie innym, w niektórych aspektach nawet przeciwnego pojęcia niż wszyscy inni. W szczególności „przekazanie przez odniesienie” nie dotyczy tego, czy „coś”, które jest przekazywane, może zostać zmodyfikowane, ale raczej tego , co to „coś” dotyczy, w szczególności, czy jest to odniesienie…
Jörg W Mittag,
8

Parametry są kopią oryginalnego odwołania. Możesz więc zmieniać wartości, ale nie możesz zmienić oryginalnego odwołania.

Rael Gugelmin Cunha
źródło
2

Spróbuj tego:--

1.object_id
#=> 3

2.object_id
#=> 5

a = 1
#=> 1
a.object_id
#=> 3

b = 2
#=> 2
b.object_id
#=> 5

identyfikator a zawiera identyfikator_obiektu 3 dla obiektu wartości 1, a identyfikator b zawiera identyfikator_obiektu 5 dla obiektu wartości 2.

Zrób to teraz:--

a.object_id = 5
#=> error

a = b
#value(object_id) at b copies itself as value(object_id) at a. value object 2 has object_id 5
#=> 2

a.object_id 
#=> 5

Teraz oba a i b zawierają ten sam identyfikator_obiektu 5, który odnosi się do obiektu wartości 2. Tak więc zmienna Ruby zawiera identyfikatory obiektu, które odnoszą się do obiektów wartości.

Wykonanie następujących czynności powoduje również błąd: -

c
#=> error

ale zrobienie tego nie spowoduje błędu: -

5.object_id
#=> 11

c = 5
#=> value object 5 provides return type for variable c and saves 5.object_id i.e. 11 at c
#=> 5
c.object_id
#=> 11 

a = c.object_id
#=> object_id of c as a value object changes value at a
#=> 11
11.object_id
#=> 23
a.object_id == 11.object_id
#=> true

a
#=> Value at a
#=> 11

Tutaj identyfikator zwraca wartość obiektu 11, którego identyfikator obiektu to 23, tj. ID_obiektu 23 znajduje się w identyfikatorze a, Teraz widzimy przykład za pomocą metody.

def foo(arg)
  p arg
  p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23

arg w foo ma przypisaną wartość x. Wyraźnie pokazuje, że argument jest przekazywany przez wartość 11, a sama wartość 11 będąca obiektem ma unikalny identyfikator obiektu 23.

Zobacz teraz także: -

def foo(arg)
  p arg
  p arg.object_id
  arg = 12
  p arg
  p arg.object_id
end

#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
#=> 12
#=> 25
x
#=> 11
x.object_id
#=> 23

W tym przypadku identyfikator arg najpierw zawiera identyfikator_obiektu 23 do odwołania 11, a po wewnętrznym przypisaniu do obiektu wartości 12 zawiera identyfikator_obiektu 25. Ale nie zmienia wartości, do której odwołuje się identyfikator x używany w metodzie wywoływania.

Dlatego Ruby jest przekazywany przez wartość, a zmienne Ruby nie zawierają wartości, ale zawierają odwołanie do obiektu wartości.

Alok Anand
źródło
1

Ruby jest interpretowany. Zmienne są odniesieniami do danych, ale nie do samych danych. Ułatwia to użycie tej samej zmiennej dla danych różnych typów.

Przypisanie lhs = rhs następnie kopiuje odwołanie do rhs, a nie danych. Różni się to w innych językach, takich jak C, gdzie przypisanie kopiuje dane do lhs z rhs.

Tak więc dla wywołania funkcji przekazana zmienna, powiedzmy x, jest rzeczywiście kopiowana do zmiennej lokalnej w funkcji, ale x jest referencją. Będą wtedy dwie kopie referencji, obie odwołujące się do tych samych danych. Jeden będzie w dzwoniącym, jeden w funkcji.

Przypisanie funkcji spowoduje skopiowanie nowego odwołania do wersji x funkcji. Po tym wersja x dzwoniącego pozostaje niezmieniona. Jest to nadal odniesienie do oryginalnych danych.

Natomiast użycie metody .replace na x spowoduje, że Ruby wykona kopię danych. Jeśli zastąpi się przed nowymi przypisaniami, to rzeczywiście dzwoniący zobaczy zmiany danych również w swojej wersji.

Podobnie, dopóki oryginalne odwołanie jest taktowane dla przekazywanej zmiennej, zmienne instancji będą takie same, jak wywołujący. W ramach obiektu zmienne instancji zawsze mają najbardziej aktualne wartości referencyjne, niezależnie od tego, czy są one dostarczane przez obiekt wywołujący, czy ustawione w funkcji, do której klasa została przekazana.

„Call by value” lub „call by reference” jest tu mętne z powodu zamieszania związanego z „=” W językach kompilowanych „=” to kopia danych. Tutaj w tym tłumaczonym języku „=” jest kopią referencyjną. W tym przykładzie przekazano referencję, a następnie kopię referencyjną „=”, która blokuje oryginał przekazany w referencji, a następnie ludzie mówią o tym, jakby „=” była kopią danych.

Aby zachować zgodność z definicjami, musimy zachować „.replace”, ponieważ jest to kopia danych. Z perspektywy „.replace” widzimy, że tak naprawdę jest to odniesienie. Ponadto, jeśli przejdziemy przez debugger, zobaczymy, że przekazywane są referencje, ponieważ zmienne są referencjami.

Jeśli jednak musimy zachować wartość „=” jako odniesienie, wówczas rzeczywiście widzimy przekazane dane aż do przypisania, a następnie nie widzimy go już po przypisaniu, podczas gdy dane dzwoniącego pozostają niezmienione. Na poziomie behawioralnym jest to przekazywanie wartości, o ile nie uważamy, że przekazana wartość jest złożona - ponieważ nie będziemy w stanie zachować jej części, zmieniając drugą część w jednym przydziale (jak to przydział) zmienia odniesienie, a oryginał wychodzi poza zakres). Będzie też brodawka, w tym przypadku zmienne w obiektach będą referencjami, podobnie jak wszystkie zmienne. W związku z tym będziemy zmuszeni mówić o przekazywaniu „referencji według wartości” i musimy używać powiązanych lokalizacji.


źródło
1

Należy zauważyć, że nie trzeba nawet używać metody „zamień”, aby zmienić pierwotną wartość wartości. Jeśli przypiszesz jedną z wartości skrótu do skrótu, zmienisz oryginalną wartość.

def my_foo(a_hash)
  a_hash["test"]="reference"
end;

hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"
Don Carr
źródło
Kolejna rzecz, którą znalazłem. Jeśli przekazujesz typ liczbowy, wszystkie typy numeryczne są niezmienne, a zatem SĄ przekazywane przez wartość. Funkcja zamiany, która działała z powyższym ciągiem, NIE działa dla żadnego z typów numerycznych.
Don Carr
1
Two references refer to same object as long as there is no reassignment. 

Wszelkie aktualizacje w tym samym obiekcie nie będą zawierać odniesień do nowej pamięci, ponieważ nadal znajduje się ona w tej samej pamięci. Oto kilka przykładów:

    a = "first string"
    b = a



    b.upcase! 
    => FIRST STRING
    a
    => FIRST STRING

    b = "second string"


a
    => FIRST STRING
    hash = {first_sub_hash: {first_key: "first_value"}}
first_sub_hash = hash[:first_sub_hash]
first_sub_hash[:second_key] = "second_value"

    hash
    => {first_sub_hash: {first_key: "first_value", second_key: "second_value"}}

    def change(first_sub_hash)
    first_sub_hash[:third_key] = "third_value"
    end

    change(first_sub_hash)

    hash
    =>  {first_sub_hash: {first_key: "first_value", second_key: "second_value", third_key: "third_value"}}
Ayman Hussain
źródło
0

Tak ale ....

Ruby przekazuje odwołanie do obiektu, a ponieważ wszystko w Rubim jest obiektem, można powiedzieć, że przekazuje je przez odwołanie.

Nie zgadzam się z zamieszczonymi tutaj postami, które twierdzą, że przekazuje wartość, co wydaje mi się pedantycznymi, sympatycznymi grami.

Jednak w efekcie „ukrywa” to zachowanie, ponieważ większość operacji ruby ​​zapewnia „po wyjęciu z pudełka” - na przykład operacje łańcuchowe tworzą kopię obiektu:

> astringobject = "lowercase"

> bstringobject = astringobject.upcase
> # bstringobject is a new object created by String.upcase

> puts astringobject
lowercase

> puts bstringobject
LOWERCASE

Oznacza to, że przez większość czasu oryginalny obiekt pozostaje niezmieniony, co sprawia, że ​​rubin jest „przekazywany przez wartość”.

Oczywiście przy projektowaniu własnych klas zrozumienie szczegółów tego zachowania jest ważne zarówno dla zachowania funkcjonalnego, wydajności pamięci, jak i wydajności.

Tomm P.
źródło