ruby on rails - given - Ruby passe-t-il par référence ou par valeur?



content_tag content (8)

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 passé par la référence 2279146940 car les ID d'objet (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 parce que l'assignation locale peut avoir préséance, mais c'est clairement passer-par-référence

https://ffff65535.com

@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 objet @user ajoute des erreurs à la variable lang_errors dans la méthode update_lanugages . lorsque @user une sauvegarde sur l'objet @user , je perds les erreurs initialement stockées dans la variable lang_errors .

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


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

Ruby est valeur-passe. 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

Dans la terminologie traditionnelle, Ruby est strictement valeur de passage . Mais ce n'est pas vraiment ce que vous demandez ici.

Ruby n'a aucun concept d'une valeur pure, sans référence, donc vous ne pouvez certainement pas en passer une à une méthode. Les variables sont toujours des références aux objets. Afin d'obtenir un objet qui ne vous quittera pas, vous devez dupliquer ou cloner l'objet qui vous a été transmis, donnant ainsi un objet auquel personne d'autre n'a de référence. (Même si ce n'est pas à l'épreuve des balles, les deux méthodes de clonage standard font une copie superficielle, donc les variables d'instance du clone pointent toujours vers les mêmes objets que les originaux. apparaissent toujours dans la copie, car il fait référence aux mêmes objets.)


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'identificateur a contient l'ID d'objet 3 pour l'objet de valeur 1 et l'identificateur b contient l'ID d'objet 5 pour l'objet de 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 les deux le même object_id 5 qui fait référence à l'objet de valeur 2. Donc, la variable Ruby contient object_ids pour faire référence aux objets de valeur.

Faire suivre donne aussi une erreur: -

c
#=> error

mais faire 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 renvoie un objet de valeur 11 dont l'identifiant d'objet est 23, c'est-à-dire que l'identificateur d'objet 23 est à l'identificateur a. Nous voyons maintenant 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 assigné avec la valeur de retour de x. Il montre clairement que l'argument est passé par la valeur 11, et la valeur 11 étant elle-même un objet a un identifiant d'objet unique 23.

Maintenant, voyez aussi ceci: -

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 l'object_id 23 pour se référer à 11 et après l'affectation interne à l'objet value 12, il contient l'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.


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

[à partir de 3.8.1: Références d'objet]

Lorsque vous passez un objet à une méthode dans Ruby, il s'agit d'une référence d'objet transmise à 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 dire ceci 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 ensuite visibles lorsque la méthode retourne.

Tout cela a du sens pour moi jusqu'à ce dernier paragraphe, et surtout cette dernière phrase. Ceci est au mieux trompeur, et au pire déconcertant. Comment, de quelque façon que ce soit, les modifications de cette référence passée par valeur pourraient-elles changer l'objet sous-jacent?


Les autres répondeurs sont tous corrects, mais un ami m'a demandé de lui expliquer cela et ce à quoi il se résume 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

R: Une étiquette nommée str est créée qui pointe sur l'objet 'foo' , qui se trouve à l'emplacement mémoire 2000 pour l'état de cet interpréteur Ruby.

Q2: Que se passe-t-il lorsque vous affectez la variable existante à un nouvel objet en utilisant = ?

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

R: L'étiquette str pointe maintenant vers un objet différent.

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

str2 = str
str2.object_id # => 2002

R: Une nouvelle étiquette appelée str2 est créée et pointe sur le même objet que str .

Q4: Que se passe-t-il si l'objet référencé par str et str2 est modifié?

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

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

Comment cela se rapporte-t-il à la question originale?

C'est fondamentalement la même chose que ce qui se passe au T3 / T4; la méthode obtient sa propre copie privée de la variable / label ( str2 ) qui lui est passée ( str ). Il ne peut pas changer l'objet sur lequel pointe le label, mais il peut changer le contenu de l'objet auquel ils font référence.

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

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

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

Ainsi, pour l'appel de la fonction, la variable passée, disons x, est en effet copiée dans une variable locale de la fonction, mais x est une référence. Il y aura alors deux copies de la référence, les deux référençant les mêmes données. L'un sera dans l'appelant, l'autre dans la fonction.

L'affectation dans la fonction copiera alors une nouvelle référence à la version de la fonction de x. Après cela, la version de l'appelant de x 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 fera une copie de données. Si remplacer est utilisé avant toute nouvelle assignation, 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 transmise, les variables d'instance seront les mêmes que celles de l'appelant. 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.

Le 'appel par valeur' ​​ou 'appel par référence' est confus ici en raison de la confusion sur '=' Dans les langages compilés '=' est une copie de données. Ici, dans ce langage interprété '=' est une copie de référence. Dans l'exemple, vous avez la référence transmise suivie d'une copie de référence mais '=' qui clobffe l'original passé en référence, et les gens 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 c'est en effet passer par référence. En outre, si nous parcourons le débogueur, nous voyons des références qui sont passées, car les variables sont des références.

Cependant, si nous devons conserver '=' comme cadre de référence, nous pouvons effectivement voir les données transmises jusqu'à une affectation, et nous ne pouvons plus les voir après l'affectation alors que les données de l'appelant restent inchangées. Au niveau comportemental, cette valeur est transmise par valeur tant que nous ne considérons pas que la valeur transmise est composite - car nous ne serons pas en mesure de conserver une partie de celle-ci en changeant l'autre partie dans une seule affectation (comme cette affectation change la référence et l'original sort du champ d'application). Il y aura aussi une verrue, dans ce cas les variables dans les objets seront des références, comme toutes les variables. Nous serons donc obligés de parler de «références par valeur» et d'utiliser des locutions connexes.


Ruby est une valeur 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 lu: robertheaton.com/2014/07/22/…

Pass-reference-by-value pourrait brièvement être 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. Cependant, 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 réalité une combinaison des définitions classiques de référence par passage et de valeur par passe.





pass-by-reference