Ruby passe-t-il par référence ou par valeur?

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?

@userL'objet ajoute des erreurs à la lang_errorsvariable dans la update_lanugagesméthode. lorsque j'effectue une sauvegarde sur l' @userobjet, je perds les erreurs qui étaient initialement stockées dans la lang_errorsvariable.

Bien que ce que j'essaie de faire serait plus un hack (qui ne semble pas fonctionner). Je voudrais comprendre pourquoi les valeurs variables sont effacées. Je comprends passer par référence, donc je voudrais savoir comment la valeur peut être conservée dans cette variable sans être effacée.

Sid
la source
Je remarque également que je peux conserver cette valeur dans un objet cloné
Sid
1
Vous devriez regarder la réponse d'Abe Voelker. Mais après avoir parcouru le bloc à ce sujet, voici comment je le dirais. lorsque vous passez un objet Foo à une procédure, une copie de la référence à l'objet est passée, bar, Pass by value. vous ne pouvez pas modifier l'objet vers lequel pointe Foo, mais vous pouvez modifier le contenu de l'objet vers lequel il pointe. Donc, si vous passez un tableau, le contenu du tableau peut être modifié, mais vous ne pouvez pas changer le tableau référencé. agréable de pouvoir utiliser les méthodes de Foo sans avoir à se soucier de gâcher d'autres dépendances sur Foo.
bobbdelsol

Réponses:

244

Dans la terminologie traditionnelle, Ruby est strictement pass-by-value . Mais ce n'est pas vraiment ce que vous demandez ici.

Ruby n'a aucun concept de valeur pure et non référence, vous ne pouvez donc certainement pas en transmettre une à une méthode. Les variables sont toujours des références à des objets. Afin d'obtenir un objet qui ne changera pas sous vous, vous devez dupliquer ou cloner l'objet que vous avez passé, donnant ainsi un objet auquel personne d'autre n'a de référence. (Même cela n'est pas à l'épreuve des balles, cependant - les deux méthodes de clonage standard effectuent une copie superficielle, de sorte que les variables d'instance du clone pointent toujours vers les mêmes objets que les originaux. Si les objets référencés par les ivars mutent, cela apparaissent toujours dans la copie, car elle fait référence aux mêmes objets.)

Mandrin
la source
88
Ruby est une valeur de passage . Pas de si. Pas de mais. Aucune exception. Si vous voulez savoir si Ruby (ou toute autre langue) est passée par référence ou un passage par valeur , juste essayer: def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}".
Jörg W Mittag
95
@ JörgWMittag: Oui, mais la confusion de l'OP n'est en fait pas transmise par valeur ou par référence au sens strict des mots. Ce qui lui manquait, c'est que les "valeurs" que vous transmettez sont des références. Je sentais que le simple fait de dire «c'est une valeur de passage» serait pédant et ne rendrait pas service à l'OP, car ce n'est pas vraiment ce qu'il voulait dire. Mais merci pour la clarification, car elle est importante pour les futurs lecteurs et j'aurais dû l'inclure. (Je suis toujours partagé entre inclure plus d'informations et ne pas dérouter les gens.)
Chuck
16
En désaccord avec @Jorg. Ruby est passé par référence, il change juste la référence. Essayez plutôt ceci: def foo (bar) bar.replace 'reference' end; baz = 'valeur'; foo (baz); met "Ruby est passante - # {baz}"
pguardiario
15
@pguardiario: Je pense que c'est vraiment juste une question de définitions. Vous utilisez une définition de «passe-par-référence» que vous avez personnellement élaborée, tandis que Jörg utilise la définition informatique traditionnelle. Bien sûr, ce n'est pas mon affaire de vous dire comment utiliser des mots - je pense simplement qu'il est important d'expliquer ce que le terme signifie normalement . Dans la terminologie traditionnelle, Ruby est une valeur de passage, mais les valeurs elles-mêmes sont des références. Je comprends parfaitement pourquoi vous et le PO aimez considérer cela comme un passage par référence - ce n'est tout simplement pas le sens traditionnel du terme.
Chuck
7
Tout dans Ruby est un objet, donc Ruby n'est ni passé par valeur ni passé par référence, du moins dans le sens où ces termes sont utilisés en C ++. "passer par référence d'objet" pourrait être une meilleure façon de décrire ce que fait Ruby. En fin de compte, cependant, le meilleur pari pourrait être de ne pas donner trop de sens à l'un de ces termes, et simplement de bien comprendre le comportement qui se produit réellement.
David Winiecki
424

Les autres répondeurs ont tous raison, mais un ami m'a demandé de lui expliquer cela et ce que cela signifie vraiment, c'est comment Ruby gère les variables, alors j'ai pensé partager quelques images / explications simples que j'ai écrites pour lui (excuses pour la longueur et probablement une simplification excessive):


Q1: Que se passe-t-il lorsque vous affectez une nouvelle variable strà une valeur de 'foo'?

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

entrez la description de l'image ici

R: Une étiquette appelée strest créée qui pointe vers l'objet 'foo'qui, pour l'état de cet interpréteur Ruby, se trouve être à l'emplacement de la mémoire 2000.


Q2: Que se passe-t-il lorsque vous affectez la variable existante strà un nouvel objet à l'aide de =?

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

entrez la description de l'image ici

R: L'étiquette strpointe désormais vers un autre objet.


Q3: Que se passe-t-il lorsque vous affectez une nouvelle variable =à str?

str2 = str
str2.object_id # => 2002

entrez la description de l'image ici

R: Une nouvelle étiquette appelée str2est créée et pointe vers le même objet que str.


Q4: Que se passe-t-il si l'objet référencé par stret str2est modifié?

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

entrez la description de l'image ici

R: Les deux étiquettes pointent toujours vers le même objet, mais cet objet lui-même a muté (son contenu a changé pour être autre chose).


Quel est le rapport avec la question d'origine?

C'est fondamentalement la même chose que ce qui se passe dans Q3 / Q4; la méthode obtient sa propre copie privée de la variable / label ( str2) qui lui est transmise ( str). Il ne peut pas changer vers quel objet l'étiquette str pointe , mais il peut changer le contenu de l'objet qu'ils référencent tous les deux pour contenir d'autre:

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
la source
1
Robert Heaton a également blogué à ce sujet ces derniers temps: robertheaton.com/2014/07/22/…
Michael Renner
48

Ruby utilise "passer par référence d'objet"

(Utilisation de la terminologie de Python.)

Dire que Ruby utilise "passer par valeur" ou "passer par référence" n'est pas vraiment assez descriptif pour être utile. Je pense que comme la plupart des gens le savent de nos jours, cette terminologie ("valeur" vs "référence") vient de C ++.

En C ++, "passer par valeur" signifie que la fonction obtient une copie de la variable et que toute modification apportée à la copie ne change pas l'original. C'est vrai aussi pour les objets. Si vous transmettez une variable d'objet par valeur, l'objet entier (y compris tous ses membres) est copié et les modifications apportées aux membres ne modifient pas ces membres sur l'objet d'origine. (C'est différent si vous passez un pointeur par valeur mais Ruby n'a pas de pointeurs de toute façon, 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;
}

Production:

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

En C ++, "passer par référence" signifie que la fonction a accès à la variable d'origine. Il peut affecter un tout nouvel entier littéral et la variable d'origine aura alors également cette valeur.

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

Production:

in replace: 10
in main: 10

Ruby utilise le passage par valeur (au sens C ++) si l'argument n'est pas un objet. Mais dans Ruby, tout est un objet, donc il n'y a vraiment pas de valeur de passage au sens C ++ dans Ruby.

Dans Ruby, "passer par référence d'objet" (pour utiliser la terminologie de Python) est utilisé:

  • À l'intérieur de la fonction, n'importe quel membre de l'objet peut se voir attribuer de nouvelles valeurs et ces modifications persisteront après le retour de la fonction. *
  • À l'intérieur de la fonction, l'affectation d'un tout nouvel objet à la variable provoque l'arrêt de la référence à l'ancien objet par la variable. Mais après le retour de la fonction, la variable d'origine fera toujours référence à l'ancien objet.

Par conséquent, Ruby n'utilise pas "passe par référence" dans le sens C ++. Si tel était le cas, l'attribution d'un nouvel objet à une variable à l'intérieur d'une fonction entraînerait l'oubli de l'ancien objet après le retour de la fonction.

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

Production:

1
2
2
3
2

1
2
1

* C'est pourquoi, dans Ruby, si vous souhaitez modifier un objet à l'intérieur d'une fonction mais oublier ces changements lorsque la fonction revient, alors vous devez explicitement faire une copie de l'objet avant d'apporter vos modifications temporaires à la copie.

David Winiecki
la source
Votre réponse est la meilleure. Je veux aussi poster un exemple simple def ch(str) str.reverse! end; str="abc"; ch(str); puts str #=> "cba"
fangxing
Ceci est la bonne réponse! Ceci est également très bien expliqué ici: robertheaton.com/2014/07/22/… . Mais ce que je ne comprends pas encore est le suivant: def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}". Cela affiche "Ruby est une valeur de passage". Mais la variable à l'intérieur fooest réaffectée. S'il bars'agissait d'un tableau, la réaffectation n'aurait aucun effet baz. Pourquoi?
haffla
Je ne comprends pas ta question. Je pense que vous devriez poser une toute nouvelle question au lieu de la poser dans les commentaires ici.
David Winiecki
42

Ruby passe-t-il par référence ou par valeur?

Ruby est une valeur de passage. Toujours. Aucune exception. Pas de si. Pas de mais.

Voici un programme simple qui démontre ce fait:

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
la source
15
@DavidJ .: "L'erreur ici est que le paramètre local est réaffecté (pointé vers un nouvel endroit en mémoire)" - Ce n'est pas une erreur, c'est la définition de la valeur de passage . Si Ruby était passé par référence, la réaffectation à la liaison de l'argument de la méthode locale dans l'appelé aurait également réaffecté la liaison de la variable locale dans l'appelant. Ce qui n'a pas été le cas. Ergo, Ruby est pass-by-value. Le fait que si vous modifiez une valeur mutable, les changements de valeur sont complètement hors de propos, c'est ainsi que fonctionne l'état mutable. Ruby n'est pas un pur langage fonctionnel.
Jörg W Mittag
5
Merci à Jörg de défendre la vraie définition du "pass-by-value". Il fait clairement fondre notre cerveau lorsque la valeur est en fait une référence, bien que le rubis passe toujours par la valeur.
Douglas
9
C'est de la sophistique. La distinction pratique entre "passer par valeur" et "passer par référence" est sémantique et non syntaxique. Diriez-vous que les tableaux C sont des valeurs de passage? Bien sûr que non, même si lorsque vous passez le nom d'un tableau à une fonction, vous passez un pointeur immuable, et seules les données auxquelles le pointeur fait référence peuvent être mutées. De toute évidence, les types de valeur dans Ruby sont transmis par valeur et les types de référence sont transmis par référence.
dodgethesteamroller
3
@dodgethesteamroller: Ruby et C sont pass-by-value. Toujours. Pas d'exceptions, pas si ce n'est pas des mais. La distinction entre passe-par-valeur et passe-par-référence est de savoir si vous passez la valeur vers laquelle la référence pointe ou si vous passez la référence. C transmet toujours la valeur, jamais la référence. La valeur peut ou peut ne pas être un pointeur, mais ce que la valeur est sans pertinence en elle est passée en premier lieu. Ruby transmet également toujours la valeur, jamais la référence. Cette valeur est toujours un pointeur, mais encore une fois, cela n'a pas d'importance.
Jörg W Mittag
44
Cette réponse, bien que strictement vraie , n'est pas très utile . Le fait que la valeur transmise soit toujours un pointeur n'est pas sans importance. C'est une source de confusion pour les gens qui essaient d'apprendre, et votre réponse ne fait absolument rien pour aider à cette confusion.
20

Ruby est une valeur de passage au sens strict, MAIS les valeurs sont des références.

Cela pourrait être appelé " pass-reference-by-value ". Cet article a la meilleure explication que j'ai lue: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/

Pass-reference-by-value pourrait être brièvement expliqué comme suit:

Une fonction reçoit une référence (et accédera) au même objet en mémoire que celui utilisé par l'appelant. Toutefois, il ne reçoit pas la boîte dans laquelle l'appelant stocke cet objet; comme dans pass-value-by-value, la fonction fournit sa propre boîte et crée une nouvelle variable pour elle-même.

Le comportement qui en résulte est en fait une combinaison des définitions classiques de passe-par-référence et de passe-par-valeur.

Ari
la source
"passer la référence par valeur" est la même phrase que j'utilise pour décrire le passage de l'argument de Ruby. Je pense que c'est la phrase la plus précise et la plus succincte.
Wayne Conrad
16

Il y a déjà de bonnes réponses, mais je veux publier la définition d'une paire d'autorités sur le sujet, mais j'espère aussi que quelqu'un pourrait expliquer ce que les autorités Matz (créateur de Ruby) et David Flanagan ont voulu dire dans leur excellent livre O'Reilly, Le langage de programmation Ruby .

[de 3.8.1: Références d'objets]

Lorsque vous passez un objet à une méthode dans Ruby, c'est une référence d'objet qui est passée à la méthode. Ce n'est pas l'objet lui-même, et ce n'est pas une référence à la référence à l'objet. Une autre façon de le dire est que les arguments de méthode sont passés par valeur plutôt que par référence , mais que les valeurs passées sont des références d'objet.

Étant donné que les références d'objet sont transmises aux méthodes, les méthodes peuvent utiliser ces références pour modifier l'objet sous-jacent. Ces modifications sont alors visibles lorsque la méthode revient.

Tout cela a du sens pour moi jusqu'au dernier paragraphe, et surtout cette dernière phrase. C'est au mieux trompeur et au pire confondant. Comment, de quelque manière que ce soit, des modifications à cette référence transmise par valeur peuvent-elles changer l'objet sous-jacent?

Dominick
la source
1
Parce que la référence n'est pas modifiée; l'objet sous-jacent est.
dodgethesteamroller
1
Parce que l'objet est mutable. Ruby n'est pas un langage purement fonctionnel. Ceci est complètement orthogonal à passe-par-référence vs passe-par-valeur (sauf pour le fait que dans un langage purement fonctionnel, le passage par valeur et le passage par référence donnent toujours les mêmes résultats, de sorte que le langage pourrait utiliser l'un ou les deux sans que vous le sachiez).
Jörg W Mittag
Un bon exemple serait si, au lieu d'une affectation de variable dans une fonction, vous envisagez de passer un hachage à une fonction et de faire une fusion! sur le hachage passé. Le hachage d'origine finit par être modifié.
elc
16

Ruby passe-t-il par référence ou par valeur?

Ruby est passe-par-référence. Toujours. Aucune exception. Pas de si. Pas de mais.

Voici un programme simple qui démontre ce fait:

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 est un passe-par-référence 2279146940 car les object_id (adresses mémoire) sont toujours les mêmes;)

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

=> certaines personnes ne réalisent pas que c'est une référence car l'affectation locale peut avoir la priorité, mais c'est clairement une référence

Brett Allred
la source
Ceci est la seule bonne réponse et présente quelques bons pièges: Essayez a = 'foobar'; b = a; b [5] = 'z', a et b seront modifiés.
Martijn
2
@Martijn: votre argument n'est pas entièrement valide. Passons en revue votre code instruction par instruction. a = 'foobar' crée une nouvelle référence pointant vers 'foobar'. b = a crée une deuxième référence aux mêmes données que a. b [5] = 'z' change le sixième caractère de la valeur référencée par b en 'z' (la valeur qui est également référencée par coïncidence par a est modifiée). C'est pourquoi "les deux sont modifiés" selon vos termes, ou plus précisément, pourquoi "la valeur référencée par les deux variables est modifiée".
Lukas_Skywalker
2
Vous ne faites rien avec la référence dans votre barméthode. Vous modifiez simplement l'objet vers lequel la référence pointe , mais pas la référence elle-même. Ils ne peuvent modifier les références dans Ruby que par affectation. Vous ne pouvez pas modifier des références en appelant des méthodes dans Ruby car les méthodes ne peuvent être appelées que sur des objets et les références ne sont pas des objets dans Ruby. Votre exemple de code montre que Ruby a partagé un état mutable (ce qui n'est pas discuté ici), il ne fait cependant rien pour éclairer la distinction entre pass-by-value et pass-by-reference.
Jörg W Mittag
1
Lorsque quelqu'un demande si un langage est "passe-par-référence", il veut généralement savoir quand vous passez quelque chose à une fonction et que la fonction le modifie, sera-t-il modifié en dehors de la fonction. Pour Ruby, la réponse est «oui». Cette réponse est utile pour démontrer que la réponse de @ JörgWMittag est extrêmement inutile.
Toby 1 Kenobi
@ Toby1Kenobi: Vous êtes bien sûr libre d'utiliser votre propre définition personnelle du terme "valeur de passage" qui est différente de la définition courante et largement utilisée. Cependant, si vous le faites, vous devez être prêt à confondre les gens, surtout si vous négligez de divulguer le fait que vous parlez d'une notion très différente, à certains égards, même opposée que tout le monde. En particulier, le «passage par référence» ne vise pas à savoir si oui ou non le «quelque chose» qui est passé peut être modifié, mais plutôt ce que ce «quelque chose» est, en particulier, s'il s'agit de la référence…
Jörg W Mittag
8

Les paramètres sont une copie de la référence d'origine. Vous pouvez donc modifier les valeurs, mais pas la référence d'origine.

Rael Gugelmin Cunha
la source
2

Essaye ça:--

1.object_id
#=> 3

2.object_id
#=> 5

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

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

l'identifiant a contient id_objet 3 pour l'objet valeur 1 et l'identifiant b contient l'id_objet 5 pour l'objet valeur 2.

Maintenant, faites ceci: -

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

Maintenant, a et b contiennent tous deux le même object_id 5 qui fait référence à l'objet de valeur 2. Ainsi, la variable Ruby contient object_ids pour faire référence aux objets de valeur.

Faire ce qui suit donne également une erreur: -

c
#=> error

mais cela ne donnera pas d'erreur: -

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

Ici, l'identifiant a renvoie la valeur de l'objet 11 dont l'ID d'objet est 23, c'est-à-dire que object_id 23 est à l'identificateur a. Maintenant, nous voyons un exemple en utilisant la méthode.

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 dans foo est affecté avec une valeur de retour de x. Il montre clairement que l'argument est passé par la valeur 11, et la valeur 11 étant lui-même un objet a un identifiant d'objet unique 23.

Maintenant, voyez cela aussi: -

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

Ici, l'identifiant arg contient d'abord object_id 23 pour faire référence à 11 et après affectation interne avec la valeur objet 12, il contient object_id 25. Mais il ne change pas la valeur référencée par l'identifiant x utilisé dans la méthode d'appel.

Par conséquent, Ruby est passé par valeur et les variables Ruby ne contiennent pas de valeurs mais contiennent une référence à l'objet valeur.

Alok Anand
la source
1

Ruby est interprété. Les variables sont des références aux données, mais pas aux données elles-mêmes. Cela facilite l'utilisation de la même variable pour des données de types différents.

L'affectation de lhs = rhs copie alors la référence sur les rhs, pas les données. Cela diffère dans d'autres langages, tels que C, où l'affectation effectue une copie des données vers lhs à partir de rhs.

Donc, pour l'appel de fonction, la variable passée, disons x, est en effet copiée dans une variable locale dans la fonction, mais x est une référence. Il y aura alors deux copies de la référence, toutes deux faisant référence aux mêmes données. Un sera dans l'appelant, un dans la fonction.

L'affectation dans la fonction copiera alors une nouvelle référence à la version de x de la fonction. Après cela, la version de x de l'appelant reste inchangée. C'est toujours une référence aux données originales.

En revanche, en utilisant la méthode .replace sur x, Ruby effectuera une copie des données. Si replace est utilisé avant toute nouvelle affectation, alors l'appelant verra également les données changer dans sa version.

De même, tant que la référence d'origine est intacte pour la variable passée, les variables d'instance seront les mêmes que celles que l'appelant voit. Dans le cadre d'un objet, les variables d'instance ont toujours les valeurs de référence les plus à jour, qu'elles soient fournies par l'appelant ou définies dans la fonction à laquelle la classe a été transmise.

L'appel par valeur ou l'appel par référence est embrouillé ici à cause de la confusion sur '=' Dans les langues compilées '=' est une copie de données. Voici dans ce langage interprété '=' est une copie de référence. Dans l'exemple, vous avez transmis la référence suivie d'une copie de référence si '=' qui détruit l'original passé en référence, puis les gens qui en parlent comme si '=' étaient une copie de données.

Pour être cohérent avec les définitions, nous devons conserver «.replace» car il s'agit d'une copie de données. Du point de vue de «.replace», nous voyons que cela est effectivement passé par référence. De plus, si nous parcourons le débogueur, nous voyons des références transmises, car les variables sont des références.

Cependant, si nous devons conserver '=' comme cadre de référence, alors nous pouvons effectivement voir les données transmises jusqu'à une affectation, puis nous ne pouvons plus le voir après l'affectation alors que les données de l'appelant restent inchangées. Au niveau comportemental, il s'agit d'une valeur de passage tant que nous ne considérons pas la valeur transmise comme composite - car nous ne pourrons pas en conserver une partie tout en modifiant l'autre partie dans une seule affectation (comme cette affectation modifie la référence et l'original sort du cadre). Il y aura également une verrue, dans ce cas, les variables dans les objets seront des références, comme toutes les variables. Par conséquent, nous serons obligés de parler de passer des «références par valeur» et d'utiliser des locutions connexes.


la source
1

Il convient de noter que vous n'avez même pas besoin d'utiliser la méthode "replace" pour modifier la valeur d'origine. Si vous affectez l'une des valeurs de hachage à un hachage, vous modifiez la valeur d'origine.

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
la source
Une autre chose que j'ai trouvée. Si vous passez un type numérique, tous les types numériques sont immuables et sont donc transmis par valeur. La fonction de remplacement qui fonctionnait avec la chaîne ci-dessus, ne fonctionne pour aucun des types numériques.
Don Carr
1
Two references refer to same object as long as there is no reassignment. 

Toute mise à jour dans le même objet ne fera pas les références à la nouvelle mémoire car elle est toujours dans la même mémoire. Voici quelques exemples:

    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
la source
0

Oui mais ....

Ruby transmet une référence à un objet et puisque tout dans ruby ​​est un objet, vous pouvez dire qu'il est transmis par référence.

Je ne suis pas d'accord avec les publications ici prétendant que c'est une valeur de passage, cela me semble être des jeux pédants et symétriques.

Cependant, en effet, il "cache" le comportement car la plupart des opérations ruby ​​fournit "prêt à l'emploi" - par exemple, les opérations de chaîne, produisent une copie de l'objet:

> astringobject = "lowercase"

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

> puts astringobject
lowercase

> puts bstringobject
LOWERCASE

Cela signifie que la plupart du temps, l'objet d'origine reste inchangé donnant l'apparence que le rubis est "passer par la valeur".

Bien sûr, lors de la conception de vos propres classes, une compréhension des détails de ce comportement est importante pour le comportement fonctionnel, l'efficacité de la mémoire et les performances.

Tomm P
la source