Wird Ruby als Referenz oder als Wert übergeben?

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?

@userObjekt fügt der lang_errorsVariablen in der update_lanugagesMethode Fehler hinzu . Wenn ich das @userObjekt speichere, verliere ich die Fehler, die ursprünglich in der lang_errorsVariablen gespeichert waren .

Aber was ich versuche zu tun, wäre eher ein Hack (der nicht zu funktionieren scheint). Ich würde gerne verstehen, warum die variablen Werte verwaschen werden. Ich verstehe Pass by Reference und möchte wissen, wie der Wert in dieser Variablen gehalten werden kann, ohne verwaschen zu werden.

Sid
quelle
Ich bemerke auch, dass ich diesen Wert in einem geklonten Objekt behalten kann
Sid
1
Sie sollten sich die Antwort von Abe Voelker ansehen. Aber nachdem ich den Block umrundet habe, würde ich es so sagen. Wenn Sie ein Objekt Foo an eine Prozedur übergeben, wird eine Kopie des Verweises auf das Objekt übergeben, Balken, Wert übergeben. Sie können das Objekt, auf das Foo zeigt, nicht ändern, aber Sie können den Inhalt des Objekts ändern, auf das es zeigt. Wenn Sie also ein Array übergeben, kann der Inhalt des Arrays geändert werden, Sie können jedoch nicht ändern, auf welches Array verwiesen wird. Es ist schön, die Methoden von Foo anwenden zu können, ohne sich darum kümmern zu müssen, andere Abhängigkeiten von Foo durcheinander zu bringen.
Bobbdelsol

Antworten:

244

In der traditionellen Terminologie ist Ruby streng pass-by-value . Aber das ist nicht wirklich das, was Sie hier fragen.

Ruby hat kein Konzept für einen reinen Wert ohne Referenz, daher können Sie keinen Wert an eine Methode übergeben. Variablen sind immer Verweise auf Objekte. Um ein Objekt zu erhalten, das sich unter Ihnen nicht ändert, müssen Sie das übergebene Objekt dupen oder klonen und so ein Objekt angeben, auf das niemand sonst verweist. (Auch dies ist nicht kugelsicher - beide Standardklonmethoden führen eine flache Kopie durch, sodass die Instanzvariablen des Klons immer noch auf dieselben Objekte verweisen wie die Originale. Wenn die Objekte, auf die die Ivars verweisen, mutieren, wird dies mutieren wird immer noch in der Kopie angezeigt, da sie auf dieselben Objekte verweist.)

Futter
quelle
88
Ruby ist ein Pass-by-Value . Kein Wenn. Kein Aber. Keine Ausnahmen. Wenn Sie wissen möchten, ob Ruby (oder eine andere Sprache) als Referenz oder als Wert übergeben wird , probieren Sie es einfach aus : def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}".
Jörg W Mittag
95
@ JörgWMittag: Ja, aber die Verwirrung des OP ist eigentlich kein Pass-by-Value oder Pass-by-Reference im strengen CS-Sinne der Wörter. Was er fehlt , ist , dass die „Werte“ Sie vorbei sind Referenzen. Ich hatte das Gefühl, dass es pedantisch wäre, nur "It's Pass-by-Value" zu sagen und dem OP einen schlechten Dienst zu erweisen, da er das eigentlich nicht meinte. Aber danke für die Klarstellung, denn es ist wichtig für zukünftige Leser und ich hätte es aufnehmen sollen. (Ich bin immer hin und her gerissen zwischen mehr Informationen und nicht verwirrenden Menschen.)
Chuck
16
Nicht einverstanden mit @Jorg. Ruby wird als Referenz übergeben, er ändert nur die Referenz. Versuchen Sie stattdessen Folgendes: def foo (bar) bar.replace 'reference' end; baz = 'Wert'; foo (baz); setzt "Ruby ist vorbei - # {baz}"
pguardiario
15
@pguardiario: Ich denke, es ist wirklich nur eine Frage der Definitionen. Sie verwenden eine Definition von "Pass-by-Reference", die Sie sich persönlich ausgedacht haben, während Jörg die traditionelle Definition der Informatik verwendet. Natürlich geht es mich nichts an, Ihnen zu sagen, wie man Wörter verwendet - ich denke nur, dass es wichtig ist, zu erklären, was der Begriff normalerweise bedeutet. In der traditionellen Terminologie ist Ruby ein Pass-by-Wert, aber die Werte selbst sind Referenzen. Ich verstehe vollkommen, warum Sie und das OP dies gerne als Referenz betrachten - es ist einfach nicht die traditionelle Bedeutung des Begriffs.
Chuck
7
Alles in Ruby ist ein Objekt, daher wird Ruby weder als Wert noch als Referenz übergeben, zumindest in dem Sinne, dass diese Begriffe in C ++ verwendet werden. "Pass by Object Reference" könnte eine bessere Möglichkeit sein, um zu beschreiben, was Ruby tut. Am Ende könnte es jedoch die beste Wahl sein, keinem dieser Begriffe zu viel Bedeutung beizumessen und nur ein gutes Verständnis für das Verhalten zu erlangen, das wirklich passiert.
David Winiecki
424

Die anderen Antwortenden sind alle korrekt, aber ein Freund hat mich gebeten, ihm dies zu erklären, und es läuft wirklich darauf hinaus, wie Ruby mit Variablen umgeht. Deshalb dachte ich, ich würde einige einfache Bilder / Erklärungen teilen, die ich für ihn geschrieben habe (Entschuldigung für die Länge und wahrscheinlich eine gewisse Vereinfachung):


Q1: Was passiert, wenn Sie streinem Wert von eine neue Variable zuweisen 'foo'?

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

Geben Sie hier die Bildbeschreibung ein

A: Es wird eine Bezeichnung mit dem Namen strerstellt, die auf das Objekt zeigt 'foo', das sich für den Status dieses Ruby-Interpreters zufällig im Speicher befindet 2000.


F2: Was passiert, wenn Sie die vorhandene Variable strmit einem neuen Objekt zuweisen =?

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

Geben Sie hier die Bildbeschreibung ein

A: Die Beschriftung zeigt strjetzt auf ein anderes Objekt.


Q3: Was passiert , wenn Sie eine neue Variable zuweisen =zu str?

str2 = str
str2.object_id # => 2002

Geben Sie hier die Bildbeschreibung ein

A: Es wird eine neue Bezeichnung mit dem Namen str2erstellt, die auf dasselbe Objekt wie zeigt str.


Q4: Was passiert , wenn das Objekt referenziert durch strund str2wird geändert?

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

Geben Sie hier die Bildbeschreibung ein

A: Beide Beschriftungen zeigen immer noch auf dasselbe Objekt, aber dieses Objekt selbst ist mutiert (sein Inhalt hat sich geändert, um etwas anderes zu sein).


In welcher Beziehung steht dies zur ursprünglichen Frage?

Es ist im Grunde dasselbe wie in Q3 / Q4. Die Methode erhält eine eigene private Kopie der Variablen / label ( str2), die an sie übergeben wird ( str). Es kann nicht geändert werden, auf welches Objekt die Beschriftung str verweist , aber es kann den Inhalt des Objekts ändern, auf das beide verweisen, um Folgendes zu enthalten:

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
quelle
1
Robert Heaton hat in letzter Zeit auch darüber gebloggt
Michael Renner
48

Ruby verwendet "Objektreferenz übergeben"

(Verwenden der Python-Terminologie.)

Zu sagen, dass Ruby "Wert übergeben" oder "Referenz übergeben" verwendet, ist nicht beschreibend genug, um hilfreich zu sein. Ich denke, wie die meisten Leute heutzutage wissen, stammt diese Terminologie ("Wert" vs "Referenz") aus C ++.

In C ++ bedeutet "Wert übergeben", dass die Funktion eine Kopie der Variablen erhält und Änderungen an der Kopie das Original nicht ändern. Das gilt auch für Objekte. Wenn Sie eine Objektvariable als Wert übergeben, wird das gesamte Objekt (einschließlich aller seiner Mitglieder) kopiert, und Änderungen an den Mitgliedern ändern diese Mitglieder des ursprünglichen Objekts nicht. (Es ist anders, wenn Sie einen Zeiger als Wert übergeben, aber Ruby hat sowieso keine Zeiger, 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;
}

Ausgabe:

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

In C ++ bedeutet "Referenzübergabe", dass die Funktion Zugriff auf die ursprüngliche Variable erhält. Es kann eine ganz neue Literal-Ganzzahl zuweisen, und die ursprüngliche Variable hat dann auch diesen Wert.

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

Ausgabe:

in replace: 10
in main: 10

Ruby verwendet den Pass-by-Wert (im C ++ - Sinne), wenn das Argument kein Objekt ist. Aber in Ruby ist alles ein Objekt, so dass es in Ruby wirklich keinen Wertübergang im C ++ - Sinne gibt.

In Ruby wird "Objektreferenz übergeben" (um die Terminologie von Python zu verwenden) verwendet:

  • Innerhalb der Funktion können jedem Mitglied des Objekts neue Werte zugewiesen werden. Diese Änderungen bleiben auch nach der Rückkehr der Funktion bestehen. *
  • Wenn Sie der Variablen innerhalb der Funktion ein ganz neues Objekt zuweisen, hört die Variable auf, auf das alte Objekt zu verweisen. Nachdem die Funktion zurückgegeben wurde, verweist die ursprüngliche Variable weiterhin auf das alte Objekt.

Daher verwendet Ruby kein "Pass by Reference" im C ++ - Sinne. Wenn dies der Fall wäre, würde das Zuweisen eines neuen Objekts zu einer Variablen innerhalb einer Funktion dazu führen, dass das alte Objekt nach der Rückgabe der Funktion vergessen wird.

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

Ausgabe:

1
2
2
3
2

1
2
1

* Wenn Sie in Ruby ein Objekt innerhalb einer Funktion ändern möchten, diese Änderungen jedoch bei der Rückkehr der Funktion vergessen möchten, müssen Sie explizit eine Kopie des Objekts erstellen, bevor Sie Ihre temporären Änderungen an der Kopie vornehmen.

David Winiecki
quelle
Ihre Antwort ist die beste. Ich möchte auch ein einfaches Beispiel def ch(str) str.reverse! end; str="abc"; ch(str); puts str #=> "cba"
posten
Das ist die richtige Antwort! Dies wird auch hier sehr gut erklärt: robertheaton.com/2014/07/22/… . Was ich aber immer noch nicht verstehe, ist Folgendes : def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}". Dies gibt "Ruby is pass-by-value" aus. Die Variable im Inneren foowird jedoch neu zugewiesen. Wenn bares sich um ein Array handeln würde, würde sich die Neuzuweisung nicht auswirken baz. Warum?
Haffla
Ich verstehe deine Frage nicht. Ich denke, Sie sollten eine ganz neue Frage stellen, anstatt hier Kommentare abzugeben.
David Winiecki
42

Wird Ruby als Referenz oder als Wert übergeben?

Ruby ist ein Pass-by-Wert. Immer. Keine Ausnahmen. Kein Wenn. Kein Aber.

Hier ist ein einfaches Programm, das diese Tatsache demonstriert:

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
quelle
15
@DavidJ.: "Der Fehler hier ist, dass der lokale Parameter neu zugewiesen wird (auf eine neue Stelle im Speicher verwiesen)" - Das ist kein Fehler, das ist die Definition von Wertübergabe . Wenn Ruby als Referenz übergeben würde, hätte die Neuzuweisung an das lokale Methodenargument, das im Angerufenen gebunden ist, auch die lokale Variablenbindung im Aufrufer neu zugewiesen. Was es nicht tat. Ergo ist Ruby ein Pass-by-Wert. Die Tatsache, dass sich der Wert ändert, wenn Sie einen veränderlichen Wert ändern, ist völlig irrelevant. So funktioniert der veränderbare Zustand. Ruby ist keine reine Funktionssprache.
Jörg W Mittag
5
Vielen Dank an Jörg für die Verteidigung der wahren Definition von "Pass-by-Value". Es schmilzt eindeutig unser Gehirn, wenn der Wert tatsächlich eine Referenz ist, obwohl Rubin immer an Wert vorbeigeht.
Douglas
9
Das ist Sophistik. Die praktische Unterscheidung zwischen "Pass by Value" und "Pass by Reference" ist eine semantische, keine syntaktische. Würden Sie sagen, dass C-Arrays einen Wert übergeben? Natürlich nicht, obwohl Sie, wenn Sie den Namen eines Arrays an eine Funktion übergeben, einen unveränderlichen Zeiger übergeben und nur die Daten, auf die sich der Zeiger bezieht, mutiert werden können. Werttypen in Ruby werden eindeutig als Wert übergeben, und Referenztypen werden als Referenz übergeben.
Dodgethesteamroller
3
@dodgethesteamroller: Sowohl Ruby als auch C sind Wertübergabe. Immer. Keine Ausnahmen, nicht wenn kein aber. Die Unterscheidung zwischen Pass-by-Value und Pass-by-Reference besteht darin, ob Sie den Wert übergeben, auf den die Referenz verweist, oder die Referenz übergeben. C übergibt immer den Wert, niemals die Referenz. Der Wert kann ein Zeiger sein oder nicht, aber was der Wert ist, spielt keine Rolle, ob er überhaupt übergeben wird. Ruby übergibt auch immer den Wert, niemals die Referenz. Dieser Wert ist immer ein Zeiger, aber auch das ist irrelevant.
Jörg W Mittag
44
Diese Antwort ist zwar streng genommen wahr , aber nicht sehr nützlich . Die Tatsache, dass der übergebene Wert immer ein Zeiger ist, spielt keine Rolle. Es ist eine Quelle der Verwirrung für Menschen, die versuchen zu lernen, und Ihre Antwort hilft absolut nichts bei dieser Verwirrung.
20

Ruby ist im engeren Sinne ein Pass-by-Wert, ABER die Werte sind Referenzen.

Dies könnte als " Pass-Reference-by-Value " bezeichnet werden. Dieser Artikel hat die beste Erklärung, die ich gelesen habe: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/

Pass-Reference-by-Value könnte kurz wie folgt erklärt werden:

Eine Funktion empfängt einen Verweis auf dasselbe Objekt im Speicher (und greift darauf zu), das vom Aufrufer verwendet wird. Es wird jedoch nicht die Box empfangen, in der der Anrufer dieses Objekt speichert. Wie bei Pass-Value-by-Value stellt die Funktion eine eigene Box bereit und erstellt eine neue Variable für sich.

Das resultierende Verhalten ist tatsächlich eine Kombination der klassischen Definitionen von Referenzübergabe und Wertübergabe.

Ari
quelle
"Referenz nach Wert übergeben" ist der gleiche Ausdruck, mit dem ich Rubys Argumentübergabe beschreibe. Ich denke, es ist die genaueste und prägnanteste Formulierung.
Wayne Conrad
16

Es gibt bereits einige gute Antworten, aber ich möchte die Definition eines Paares von Autoritäten zu diesem Thema veröffentlichen, aber auch hoffen, dass jemand erklären könnte, was die Autoritäten Matz (Schöpfer von Ruby) und David Flanagan in ihrem ausgezeichneten O'Reilly-Buch gemeint haben. Die Ruby-Programmiersprache .

[ab 3.8.1: Objektreferenzen]

Wenn Sie ein Objekt in Ruby an eine Methode übergeben, handelt es sich um eine Objektreferenz, die an die Methode übergeben wird. Es ist nicht das Objekt selbst und es ist kein Verweis auf den Verweis auf das Objekt. Eine andere Möglichkeit, dies zu sagen, besteht darin, dass Methodenargumente eher als Wert als als Referenz übergeben werden, die übergebenen Werte jedoch Objektreferenzen sind.

Da Objektreferenzen an Methoden übergeben werden, können Methoden diese Referenzen verwenden, um das zugrunde liegende Objekt zu ändern. Diese Änderungen sind dann sichtbar, wenn die Methode zurückgegeben wird.

Dies alles macht für mich bis zu diesem letzten Absatz und insbesondere bis zu diesem letzten Satz Sinn . Dies ist bestenfalls irreführend und schlimmer noch verwirrend. Wie können Änderungen an dieser Referenz für übergebene Werte das zugrunde liegende Objekt in irgendeiner Weise ändern?

Dominick
quelle
1
Weil die Referenz nicht geändert wird; das zugrunde liegende Objekt ist.
Dodgethesteamroller
1
Weil das Objekt veränderlich ist. Ruby ist keine rein funktionale Sprache. Dies ist völlig orthogonal zu Referenzübergabe und Wertübergabe (mit Ausnahme der Tatsache, dass in einer rein funktionalen Sprache Übergabewert und Referenzübergabe immer die gleichen Ergebnisse liefern, sodass die Sprache dies könnte Verwenden Sie eines oder beide, ohne dass Sie es jemals wissen.
Jörg W Mittag
Ein gutes Beispiel wäre, wenn Sie anstelle einer Variablenzuweisung in einer Funktion den Fall betrachten, einen Hash an eine Funktion zu übergeben und eine Zusammenführung durchzuführen! auf dem übergebenen Hash. Der ursprüngliche Hash wird geändert.
Elc
16

Wird Ruby als Referenz oder als Wert übergeben?

Ruby ist eine Referenz. Immer. Keine Ausnahmen. Kein Wenn. Kein Aber.

Hier ist ein einfaches Programm, das diese Tatsache demonstriert:

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 ist eine Referenz 2279146940, da die Objekt-IDs (Speicheradressen) immer gleich sind;)

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

=> Einige Leute erkennen nicht, dass es sich um eine Referenz handelt, da die lokale Zuweisung Vorrang haben kann, aber es handelt sich eindeutig um eine Referenzübergabe

Brett Allred
quelle
Dies ist die einzig richtige Antwort und bietet einige nette Fallstricke: Try a = 'foobar'; b = a; b [5] = 'z', sowohl a als auch b werden geändert.
Martijn
2
@Martijn: Ihr Argument ist nicht ganz gültig. Lassen Sie uns Ihren Code Anweisung für Anweisung durchgehen. a = 'foobar' erstellt eine neue Referenz, die auf 'foobar' verweist. b = a erstellt einen zweiten Verweis auf dieselben Daten wie a. b [5] = 'z' ändert das sechste Zeichen des Wertes, auf den b verweist, in ein 'z' (der Wert, auf den zufällig auch a verweist, wird geändert). Das ist der Grund, warum "beide geändert werden" in Ihren Begriffen oder genauer gesagt, warum "der Wert, auf den beide Variablen verweisen, geändert wird".
Lukas_Skywalker
2
Sie tun nichts mit der Referenz in Ihrer barMethode. Sie ändern lediglich das Objekt, auf das die Referenz verweist , nicht jedoch die Referenz selbst. Sie können Referenzen in Ruby nur durch Zuweisung ändern. Sie können Referenzen nicht ändern, indem Sie Methoden in Ruby aufrufen, da Methoden nur für Objekte aufgerufen werden können und Referenzen keine Objekte in Ruby sind. Ihr Codebeispiel zeigt, dass Ruby einen veränderlichen Status hat (der hier nicht diskutiert wird), jedoch nichts, um die Unterscheidung zwischen Pass-by-Value und Pass-by-Reference zu beleuchten.
Jörg W Mittag
1
Wenn jemand fragt, ob eine Sprache "Referenzübergabe" ist, möchte er normalerweise wissen, wann Sie etwas an eine Funktion übergeben und die Funktion es ändert. Wird es außerhalb der Funktion geändert? Für Ruby lautet die Antwort "Ja". Diese Antwort ist hilfreich, um zu demonstrieren, dass die Antwort von @ JörgWMittag äußerst wenig hilfreich ist.
Toby 1 Kenobi
@ Toby1Kenobi: Es steht Ihnen natürlich frei, Ihre persönliche Definition des Begriffs "Pass-by-Value" zu verwenden, die sich von der gebräuchlichen, weit verbreiteten Definition unterscheidet. Wenn Sie dies tun, sollten Sie jedoch darauf vorbereitet sein, dass die Menschen verwirrt sind, insbesondere wenn Sie es versäumen, die Tatsache offenzulegen, dass Sie über eine ganz andere, in einigen Aspekten sogar entgegengesetzte Vorstellung sprechen als alle anderen. Insbesondere geht es bei "Pass-by-Reference" nicht darum, ob das übergebene "Etwas" geändert werden kann oder nicht, sondern vielmehr darum, was dieses "Etwas" ist, insbesondere, ob es sich um die Referenz handelt ...
Jörg W. Mittag
8

Parameter sind eine Kopie der Originalreferenz. Sie können also Werte ändern, aber nicht die ursprüngliche Referenz.

Rael Gugelmin Cunha
quelle
2

Versuche dies:--

1.object_id
#=> 3

2.object_id
#=> 5

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

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

Bezeichner a enthält Objekt_ID 3 für Wertobjekt 1 und Bezeichner b enthält Objekt_ID 5 für Wertobjekt 2.

Mach das jetzt:--

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

Jetzt enthalten a und b beide dieselbe object_id 5, die sich auf das Wertobjekt 2 bezieht. Die Ruby-Variable enthält also object_ids, um auf Wertobjekte zu verweisen.

Wenn Sie Folgendes tun, wird auch ein Fehler angezeigt: -

c
#=> error

Dies führt jedoch nicht zu einem Fehler: -

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

Hier gibt der Bezeichner a ein Wertobjekt 11 zurück, dessen Objekt-ID 23 ist, dh die Objekt-ID 23 befindet sich am Bezeichner a. Nun sehen wir ein Beispiel unter Verwendung der Methode.

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 in foo wird mit dem Rückgabewert x zugewiesen. Es zeigt deutlich, dass das Argument vom Wert 11 übergeben wird und der Wert 11, der selbst ein Objekt ist, eine eindeutige Objekt-ID 23 hat.

Nun sehen Sie dies auch: -

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

Hier enthält der Bezeichner arg zuerst object_id 23, um auf 11 zu verweisen, und nach der internen Zuweisung mit dem Wert object 12 enthält er object_id 25. Der Wert, auf den durch den in der aufrufenden Methode verwendeten Bezeichner x verwiesen wird, wird jedoch nicht geändert.

Daher wird Ruby als Wert übergeben, und Ruby-Variablen enthalten keine Werte, sondern Verweise auf Wertobjekte.

Alok Anand
quelle
1

Ruby wird interpretiert. Variablen sind Verweise auf Daten, jedoch nicht auf die Daten selbst. Dies erleichtert die Verwendung derselben Variablen für Daten unterschiedlicher Typen.

Die Zuweisung von lhs = rhs kopiert dann die Referenz auf die rhs, nicht die Daten. Dies unterscheidet sich in anderen Sprachen, wie z. B. C, wo die Zuweisung eine Datenkopie von rhs nach lhs durchführt.

Für den Funktionsaufruf wird die übergebene Variable, z. B. x, zwar in eine lokale Variable in der Funktion kopiert, aber x ist eine Referenz. Es gibt dann zwei Kopien der Referenz, die beide auf dieselben Daten verweisen. Einer wird im Anrufer sein, einer in der Funktion.

Die Zuweisung in der Funktion würde dann einen neuen Verweis auf die Funktionsversion von x kopieren. Danach bleibt die Aufruferversion von x unverändert. Es ist immer noch ein Verweis auf die Originaldaten.

Im Gegensatz dazu führt die Verwendung der .replace-Methode für x dazu, dass Ruby eine Datenkopie erstellt. Wenn Ersetzen vor neuen Zuweisungen verwendet wird, sieht der Anrufer die Datenänderung auch in seiner Version.

In ähnlicher Weise sind die Instanzvariablen dieselben, die der Aufrufer sieht, solange die ursprüngliche Referenz für die übergebene Variable gültig ist. Im Rahmen eines Objekts haben die Instanzvariablen immer die aktuellsten Referenzwerte, unabhängig davon, ob diese vom Aufrufer bereitgestellt oder in der Funktion festgelegt wurden, an die die Klasse übergeben wurde.

Der 'Aufruf nach Wert' oder 'Aufruf nach Referenz' ist hier aufgrund der Verwirrung über '=' In kompilierten Sprachen '=' ist eine Datenkopie durcheinander. Hier in dieser interpretierten Sprache ist '=' eine Referenzkopie. In dem Beispiel haben Sie die Referenz übergeben, gefolgt von einer Referenzkopie durch '=', die das als Referenz übergebene Original blockiert, und dann wird darüber gesprochen, als wäre '=' eine Datenkopie.

Um mit den Definitionen übereinzustimmen, müssen wir '.replace' beibehalten, da es sich um eine Datenkopie handelt. Aus der Perspektive von '.replace' sehen wir, dass dies tatsächlich als Referenz gilt. Wenn wir im Debugger durchgehen, sehen wir außerdem, dass Referenzen übergeben werden, da Variablen Referenzen sind.

Wenn wir jedoch '=' als Referenzrahmen beibehalten müssen, können wir die übergebenen Daten tatsächlich bis zu einer Zuweisung sehen, und dann können wir sie nach der Zuweisung nicht mehr sehen, während die Daten des Anrufers unverändert bleiben. Auf Verhaltensebene wird dies als Wert übergeben, solange wir den übergebenen Wert nicht als zusammengesetzt betrachten, da wir nicht in der Lage sind, einen Teil davon beizubehalten, während wir den anderen Teil in einer einzelnen Zuweisung ändern (wie diese Zuweisung) ändert die Referenz und das Original verlässt den Geltungsbereich). Es wird auch eine Warze geben, in diesem Fall sind Variablen in Objekten Referenzen, ebenso wie alle Variablen. Daher werden wir gezwungen sein, über das Übergeben von 'Referenzen nach Wert' zu sprechen und verwandte Positionen zu verwenden.


quelle
1

Es ist zu beachten, dass Sie nicht einmal die "Ersetzen" -Methode verwenden müssen, um den ursprünglichen Wert des Werts zu ändern. Wenn Sie einen Hashwert für einen Hash zuweisen, ändern Sie den ursprünglichen Wert.

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
quelle
Eine andere Sache, die ich gefunden habe. Wenn Sie einen numerischen Typ übergeben, sind alle numerischen Typen unveränderlich und werden daher als Wert übergeben. Die Ersetzungsfunktion, die mit der obigen Zeichenfolge funktioniert hat, funktioniert NICHT für einen der numerischen Typen.
Don Carr
1
Two references refer to same object as long as there is no reassignment. 

Bei Aktualisierungen im selben Objekt wird nicht auf den neuen Speicher verwiesen, da sich dieser noch im selben Speicher befindet. Hier einige Beispiele:

    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
quelle
0

Ja aber ....

Ruby übergibt einen Verweis auf ein Objekt, und da alles in Ruby ein Objekt ist, kann man sagen, dass es als Verweis übergeben wird.

Ich bin nicht mit den Postings hier einverstanden, in denen behauptet wird, dass es sich um einen Wert handelt, der mir wie pedantische, symantische Spiele erscheint.

Tatsächlich "verbirgt" es jedoch das Verhalten, da die meisten Operationen, die Ruby "out of the box" bereitstellt - beispielsweise Zeichenfolgenoperationen, eine Kopie des Objekts erstellen:

> astringobject = "lowercase"

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

> puts astringobject
lowercase

> puts bstringobject
LOWERCASE

Dies bedeutet, dass das ursprüngliche Objekt die meiste Zeit unverändert bleibt, was den Anschein erweckt, dass Ruby "Wert übergeben" ist.

Natürlich ist beim Entwerfen eigener Klassen ein Verständnis der Details dieses Verhaltens sowohl für das Funktionsverhalten als auch für die Speichereffizienz und die Leistung wichtig.

Tomm P.
quelle