Czy Ruby przechodzi przez odniesienie czy przez wartość?

@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?

@user obiekt dodaje błędy do zmiennej lang_errors w metodzie update_lanugages. podczas wykonywania zapisu na obiekcie @user tracę błędy, które początkowo były przechowywane w zmiennej lang_errors.

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

Author: isomorphismes, 2009-12-09

12 answers

W tradycyjnej terminologii, Ruby jest ściśle pass-by-value . Ale nie o to pytasz.

Ruby nie ma żadnej koncepcji czystej, niereferencyjnej wartości, więc z pewnością nie możesz przekazać jej do metody. Zmienne są zawsze odniesieniami do obiektów. Aby uzyskać obiekt, który nie zmieni się spod ciebie, musisz wykonać dup lub sklonować przekazany obiekt, dając w ten sposób obiekt, do którego nikt inny nie ma odniesienia. (Nawet to nie jest kuloodporne, choć - obie standardowe metody klonowania wykonują płytką kopię, więc zmienne instancji klonu nadal wskazują na te same obiekty, co oryginały. Jeśli obiekty, do których odwołuje się ivars, ulegną mutacji, będą one nadal widoczne w kopii, ponieważ odwołują się do tych samych obiektów.)

 221
Author: Chuck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:29

Inne odpowiedzi są poprawne, ale przyjaciel poprosił mnie o wyjaśnienie mu tego i do czego tak naprawdę sprowadza się to, jak Ruby radzi sobie ze zmiennymi, więc pomyślałem, że podzielę się prostymi obrazkami / wyjaśnieniami, które napisałem dla niego (przepraszam za długość i prawdopodobnie pewne uproszczenie): {]}


P1: co się dzieje, gdy przypisujesz nową zmienną str do wartości 'foo'?

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

Tutaj wpisz opis obrazka

A: tworzona jest etykieta o nazwie str, która wskazuje na obiekt 'foo', który dla stanu tego interpretera Ruby jest w Miejscu Pamięci 2000.


Q2: co się dzieje, gdy przypisujesz istniejącą zmienną str do nowego obiektu za pomocą =?

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

Tutaj wpisz opis obrazka

A: etykieta str wskazuje teraz na inny obiekt.


P3: co się dzieje, gdy przypisujesz nową zmienną = do str?

str2 = str
str2.object_id # => 2002

Tutaj wpisz opis obrazka

A: powstaje nowa etykieta o nazwie str2, która wskazuje na to samo obiekt jako str.


P4: co się stanie, jeśli obiekt, do którego odnoszą się str i str2 zostanie zmieniony?

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

Tutaj wpisz opis obrazka

A: obie etykiety nadal wskazują na ten sam obiekt, ale sam obiekt uległ mutacji(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 / etykiety (str2), która przechodzi do niego (str). Nie można zmienić obiektu etykiety str wskazuje na , ale może zmienić zawartość obiektu, do którego oba odwołują się zawierają else:

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
 398
Author: Abe Voelker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-11 03:03:24

Czy Ruby przechodzi przez odniesienie czy przez wartość?

Ruby jest wartością pass-by-value. Zawsze. Bez wyjątków. Nie, Nie. Żadnych ale.

Oto prosty program, który demonstruje ten fakt:

def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value
 45
Author: Jörg W Mittag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-26 09:22:23

Ruby używa "pass by object reference"

(używając terminologii Pythona.)

Mówienie, że Ruby używa " pass by value "lub" pass by reference " nie jest wystarczająco opisowe, aby być pomocne. Myślę, że jak większość ludzi to dziś wie, że terminologia ("wartość "vs" odniesienie") pochodzi z C++.

W C++, "pass by value" oznacza, że funkcja otrzymuje kopię zmiennej i wszelkie zmiany w kopii nie zmieniają oryginału. Dotyczy to również przedmiotów. Jeśli przekazujesz zmienną obiektową według wartości wtedy cały obiekt (łącznie ze wszystkimi jego członkami) zostanie skopiowany i wszelkie zmiany w składnikach nie zmienią tych członków w oryginalnym obiekcie. (Jest inaczej, jeśli przekazujesz wskaźnik przez wartość, 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;
}

Wyjście:

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

W C++, "pass by reference" oznacza, że funkcja otrzymuje dostęp do oryginalnej zmiennej. Może przypisać zupełnie nową literalną liczbę całkowitą, a oryginalna zmienna będzie miała tę wartość też.

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;
}

Wyjście:

in replace: 10
in main: 10

Ruby używa wartości pass by (w sensie C++), jeśli argument nie jest obiektem. Ale w Rubim wszystko jest obiektem, więc naprawdę nie ma wartości pass by w sensie C++ w Rubim.

W Rubim," pass by object reference " (aby użyć terminologii Pythona) jest używane:

  • wewnątrz funkcji, każdy z członków obiektu może mieć przypisane nowe wartości i te zmiany będą trwać po powrocie funkcji.*
  • wewnątrz funkcja, przypisanie do zmiennej zupełnie nowego obiektu powoduje, że zmienna przestaje odwoływać się do starego obiektu. Ale po powrocie funkcji, oryginalna zmienna będzie nadal odwoływać się do starego obiektu.

Dlatego Ruby nie używa "pass by reference" w sensie C++. Jeśli tak, to przypisanie nowego obiektu do zmiennej wewnątrz funkcji spowoduje, że stary obiekt zostanie zapomniany po zwróceniu 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

Wyjście:

1
2
2
3
2

1
2
1

* dlatego w Ruby, jeśli chcesz zmodyfikować obiekt wewnątrz funkcji, ale zapomnij o tych zmianach, gdy funkcja powróci, musisz jawnie utworzyć kopię obiektu przed dokonaniem tymczasowych zmian w kopii.

 37
Author: David Winiecki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-17 15:54:26

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

[od 3.8.1: odniesienia do obiektów]

Kiedy przekazujesz obiekt do metody w Ruby, jest to odniesienie do obiektu, które jest przekazywane do metody. To nie jest obiekt siebie, i nie jest to odniesienie do odniesienia do obiektu. Innym sposobem jest to, że argumenty metody są przekazywane przez wartośćzamiast przez referencję, ale przekazywane wartości są odniesieniami do obiektów.

Ponieważ odwołania do obiektów są przekazywane do metod, metody mogą używać tych odwołań do modyfikowania bazowego obiektu. Modyfikacje te są wtedy widoczne po powrocie metody.

To wszystko ma sens do ostatniego akapitu, i szczególnie to ostatnie zdanie. Jest to w najlepszym przypadku mylące, a w najgorszym mylące. Jak, w jakikolwiek sposób, modyfikacje tego odniesienia przekazywane przez wartość mogą zmienić podstawowy obiekt?

 16
Author: Dominick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-05 22:42:24

Ruby jest pass-by-value w ścisłym sensie, 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/

Pass-reference-by-value można krótko wyjaśnić w następujący sposób:

Funkcja otrzymuje odniesienie do (i uzyska dostęp) tego samego obiektu w pamięci, którego używa wywołujący. Jednak to nie odbiera pola, w którym wywołujący przechowuje ten obiekt; podobnie jak w pass-value-by-value, funkcja dostarcza 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.

 15
Author: Ari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-28 16:36:00

Czy Ruby przechodzi przez odniesienie czy przez wartość?

Ruby jest odniesieniem. Zawsze. Bez wyjątków. Nie, Nie. Żadnych ale.

Oto prosty program, który demonstruje 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 pass-by-reference 2279146940 ponieważ object_id (adresy pamięci) są zawsze takie same;)

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 nie zdają sobie sprawy, że jest to odniesienie, ponieważ lokalne przypisanie może mieć pierwszeństwo, ale jest to wyraźnie pass-by-reference

 14
Author: Brett Allred,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-17 23:03:48

Parametry są kopią oryginalnego odniesienia. Można więc zmieniać wartości, ale nie można zmienić oryginalnego odniesienia.

 8
Author: Rael Gugelmin Cunha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-21 16:06:46

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

Przypisanie LHS = rhs następnie kopiuje odniesienie na rhs, a nie dane. Różni się to w innych językach, takich jak C, gdzie assignment wykonuje kopię danych do LHS z rhs.

Więc dla wywołania funkcji, przekazywana zmienna, powiedzmy x, jest rzeczywiście kopiowana do zmiennej lokalnej w funkcji, ale x jest referencją. Będzie następnie dwie kopie odniesienia, obie odwołujące się do tych samych danych. Jeden będzie w rozmówcy, drugi w funkcji.

Przypisanie w funkcji spowoduje skopiowanie nowego odniesienia do wersji X funkcji. po tym wywołująca wersja X pozostaje niezmieniona. Jest to nadal odniesienie do oryginalnych danych.

Natomiast, używającmetoda replace na x spowoduje, że ruby zrobi kopię danych. Jeśli replace jest używane przed jakimikolwiek nowymi przydziałami, to rzeczywiście wywołujący zobaczy zmiana danych w jego wersji również.

Podobnie, tak długo, jak oryginalne odniesienie jest w takcie dla zmiennej przekazanej, zmienne instancji będą takie same, jakie widzi wywołujący. W ramach obiektu zmienne instancji zawsze mają najbardziej aktualne wartości referencyjne, niezależnie od tego, czy są one dostarczane przez wywołujący, czy ustawione w funkcji, do której została przekazana Klasa.

' call by value 'lub' call by reference 'jest tutaj mylone z powodu zamieszania nad' = ' W skompilowane Języki ' = ' jest kopią danych. W tym języku interpretowanym ' = ' jest kopią odniesienia. W przykładzie masz przekazaną referencję, a następnie kopię referencyjną, chociaż'=', która blokuje oryginał przekazany w referencji, a następnie ludzie mówią o tym, jakby ' = ' były kopią danych.

Aby być spójnym z definicjami musimy trzymać się".replace", ponieważ jest to kopia danych. Z perspektywy".zastąp" widzimy, że jest to rzeczywiście przejść przez odniesienie. Ponadto, jeśli przechodzimy przez debugger, widzimy referencje przekazywane, ponieważ zmienne są referencjami.

Jeśli jednak musimy zachować ' = ' jako ramkę odniesienia, to rzeczywiście widzimy przekazane dane aż do przypisania, a następnie nie widzimy ich już po przypisaniu, podczas gdy dane wywołującego pozostają niezmienione. Na poziomie behawioralnym jest to wartość przekazywana, o ile nie uważamy wartości przekazywanej za złożoną - ponieważ nie będziemy w stanie zachować jej części podczas zmiana drugiej części w pojedynczym zadaniu (ponieważ to zadanie zmienia odniesienie i Oryginał wychodzi poza zakres). Będzie też brodawka, w tym przypadku zmienne w obiektach będą referencjami, podobnie jak wszystkie zmienne. Dlatego będziemy zmuszeni mówić o przekazywaniu "referencji według wartości" i będziemy musieli użyć powiązanych lokalizacji.

 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-21 13:01:30

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 object_id 3 dla value object 1, A Identyfikator b zawiera object_id 5 dla value object 2.

A teraz zrób to:--

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, a i b zawierają ten sam object_id 5, który odnosi się do wartości object 2. Tak więc zmienna Ruby zawiera identyfikatory obiektów, które odnoszą się do obiektów wartości.

Wykonywanie następujących czynności również daje 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 object id to 23 tzn. object_id 23 jest przy identyfikatorze a, teraz widzimy przykład przy użyciu 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 jest przypisany z wartością zwracaną x. Jasno pokazuje, że argument jest przekazywany przez wartość 11, a wartość 11 sama w sobie jest obiektem o unikalnym ID obiektu 23.

Zobacz też:]}
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

Tutaj identyfikator arg najpierw zawiera object_id 23, aby odnieść się do 11, a po wewnętrznym przypisaniu z wartością object 12, zawiera object_id 25. Ale nie zmienia wartości odwołuje się do identyfikatora x użytego w wywołaniu metody.

dlatego Ruby jest przekazywana przez wartość, A zmienne Ruby nie zawierają wartości, ale zawierają odniesienie do obiektu value.

 1
Author: Alok Anand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-25 06:03:53

Należy zauważyć, że nie trzeba nawet użyć metody "replace", aby zmienić wartość pierwotną. Jeśli przypisasz jedną z wartości skrótu dla 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"]}"
 1
Author: Don Carr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-28 03:11:59
Two references refer to same object as long as there is no reassignment. 

Jakiekolwiek aktualizacje w tym samym obiekcie nie spowodują odwołania 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"}}
 1
Author: Ayman Hussain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-20 20:09:37