Ruby passa per riferimento o per valore?


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'oggetto aggiunge errori alla lang_errorsvariabile nel update_lanugagesmetodo quando eseguo un salvataggio @usersull'oggetto perdo gli errori che erano inizialmente memorizzati nella lang_errorsvariabile.

Anche se quello che sto tentando di fare sarebbe più di un hack (che non sembra funzionare). Vorrei capire perché i valori delle variabili sono sbiaditi. Comprendo il passaggio per riferimento, quindi vorrei sapere come il valore può essere mantenuto in quella variabile senza essere slavato.


Noto anche che sono in grado di conservare quel valore in un oggetto clonato
Sid

1
Dovresti guardare la risposta di Abe Voelker. Ma dopo aver eseguito il blocco su questo, ecco come lo direi. quando si passa un oggetto Foo a una procedura, viene passata una copia del riferimento all'oggetto, barra, Passa per valore. non puoi cambiare l'oggetto a cui punta Foo, ma puoi cambiare il contenuto dell'oggetto a cui punta. Pertanto, se si passa a un array, è possibile modificare il contenuto dell'array, ma non è possibile modificare a quale array viene fatto riferimento. bello poter usare i metodi di Foo senza doversi preoccupare di incasinare altre dipendenze da Foo.
Bobbdelsol,

Risposte:


244

Nella terminologia tradizionale, Ruby è rigorosamente pass-by-value . Ma non è proprio quello che stai chiedendo qui.

Ruby non ha alcun concetto di un valore puro, non di riferimento, quindi sicuramente non puoi passarne uno a un metodo. Le variabili sono sempre riferimenti ad oggetti. Per ottenere un oggetto che non cambierà da sotto di te, devi duplicare o clonare l'oggetto che hai passato, dando così un oggetto a cui nessun altro ha riferimento. (Anche se questo non è a prova di proiettile, entrambi i metodi di clonazione standard eseguono una copia superficiale, quindi le variabili di istanza del clone puntano ancora agli stessi oggetti degli originali. Se gli oggetti a cui fanno riferimento gli ivar mutano, viene comunque visualizzato nella copia, poiché fa riferimento agli stessi oggetti.)


88
Ruby è pass-by-value . No se. No ma. Nessuna eccezione. Se volete sapere se Rubino (o qualsiasi altra lingua) è pass-by-di riferimento o di passare per valore , basta provare: def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}".
Jörg W Mittag,

95
@ JörgWMittag: Sì, ma la confusione dell'OP non è in realtà pass-by-value o pass-by-reference nel senso stretto del termine CS. Quello che gli mancava è che i "valori" che stai passando sono riferimenti. Ho sentito che il solo dire "È un valore per passatempo" sarebbe stato pedante e avrebbe fatto un disservizio all'OP, poiché in realtà non era quello che voleva dire. Ma grazie per il chiarimento, perché è importante per i futuri lettori e avrei dovuto includerlo. (Sono sempre diviso tra l'inclusione di maggiori informazioni e il non confondere le persone.)
Chuck,

16
In disaccordo con @Jorg. Ruby viene passato per riferimento, cambia solo il riferimento. Prova invece: def foo (bar) bar.replace 'reference' end; baz = 'valore'; foo (baz); mette "Passa il rubino - # {baz}"
pguardiario il

15
@pguardiario: penso che sia davvero solo una questione di definizioni. Stai usando una definizione di "pass-by-reference" che hai trovato personalmente, mentre Jörg sta usando la tradizionale definizione di informatica. Certo, non è affar mio per dirti come usare le parole - penso solo che sia importante spiegare cosa significa normalmente il termine . Nella terminologia tradizionale, Ruby è pass-by-value, ma i valori stessi sono riferimenti. Capisco perfettamente perché a te e al PO piace pensare a questo come pass-by-reference - non è proprio il significato tradizionale del termine.
Chuck,

7
Tutto in Ruby è un oggetto, quindi Ruby non è né passa per valore né passa per riferimento, almeno nel senso in cui quei termini sono usati in C ++. "Passa per riferimento oggetto" potrebbe essere un modo migliore per descrivere cosa fa Ruby. Alla fine, però, la scommessa migliore potrebbe essere quella di non dare troppo significato a nessuno di questi termini, e solo di avere una buona comprensione del comportamento che accade realmente.
David Winiecki,

424

Gli altri risponditori sono tutti corretti, ma un amico mi ha chiesto di spiegarglielo e ciò a cui si riduce davvero è come Ruby gestisce le variabili, quindi ho pensato di condividere alcune semplici foto / spiegazioni che ho scritto per lui (scuse per la lunghezza e probabilmente qualche semplificazione eccessiva):


Q1: cosa succede quando si assegna una nuova variabile stra un valore di 'foo'?

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

inserisci qui la descrizione dell'immagine

A: Viene strcreata un'etichetta chiamata che punta sull'oggetto 'foo', che per lo stato di questo interprete Ruby si trova nella posizione di memoria 2000.


Q2: cosa succede quando si assegna la variabile esistente stra un nuovo oggetto usando =?

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

inserisci qui la descrizione dell'immagine

A: L'etichetta strora punta a un oggetto diverso.


D3: Cosa succede quando si assegna una nuova variabile =a str?

str2 = str
str2.object_id # => 2002

inserisci qui la descrizione dell'immagine

A: Viene str2creata una nuova etichetta chiamata che punta allo stesso oggetto di str.


Q4: Cosa succede se l'oggetto a cui fa riferimento stre str2viene modificato?

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

inserisci qui la descrizione dell'immagine

A: Entrambe le etichette puntano ancora sullo stesso oggetto, ma quell'oggetto stesso è mutato (il suo contenuto è cambiato in qualcos'altro).


In che modo questo si collega alla domanda originale?

È sostanzialmente lo stesso di quello che succede in Q3 / Q4; il metodo ottiene la propria copia privata della variabile / etichetta ( str2) che gli viene passata ( str). Non può cambiare l'oggetto a cui str punta l'etichetta , ma può cambiare il contenuto dell'oggetto a cui entrambi fanno riferimento per contenere altro:

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

1
Anche Robert Heaton ha recentemente scritto un
Michael Renner,

48

Ruby usa "passa per riferimento oggetto"

(Usando la terminologia di Python.)

Dire che Ruby usa "passa per valore" o "passa per riferimento" non è abbastanza descrittivo per essere utile. Penso che la maggior parte delle persone lo sappia in questi giorni, che la terminologia ("valore" vs "riferimento") provenga dal C ++.

In C ++, "passa per valore" significa che la funzione ottiene una copia della variabile e qualsiasi modifica alla copia non cambia l'originale. Questo vale anche per gli oggetti. Se si passa una variabile oggetto per valore, l'intero oggetto (inclusi tutti i suoi membri) viene copiato e qualsiasi modifica apportata ai membri non modifica tali membri sull'oggetto originale. (È diverso se passi un puntatore per valore ma Ruby non ha comunque puntatori, 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;
}

Produzione:

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

In C ++, "passa per riferimento" significa che la funzione ottiene l'accesso alla variabile originale. Può assegnare un intero letterale completamente nuovo e anche la variabile originale avrà quel valore.

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

Produzione:

in replace: 10
in main: 10

Ruby usa pass by value (nel senso C ++) se l'argomento non è un oggetto. Ma in Ruby tutto è un oggetto, quindi in Ruby non c'è davvero alcun passaggio per valore nel senso C ++.

In Ruby viene utilizzato "passa per riferimento oggetto" (per usare la terminologia di Python):

  • All'interno della funzione, a qualsiasi membro dell'oggetto possono essere assegnati nuovi valori e queste modifiche persistono dopo il ritorno della funzione. *
  • All'interno della funzione, l'assegnazione di un oggetto completamente nuovo alla variabile fa smettere di fare riferimento al vecchio oggetto. Ma dopo che la funzione ritorna, la variabile originale farà ancora riferimento al vecchio oggetto.

Pertanto Ruby non usa "passa per riferimento" in senso C ++. In tal caso, l'assegnazione di un nuovo oggetto a una variabile all'interno di una funzione causerebbe la dimenticanza del vecchio oggetto dopo la restituzione della funzione.

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

Produzione:

1
2
2
3
2

1
2
1

* Ecco perché, in Ruby, se vuoi modificare un oggetto all'interno di una funzione ma dimentica quelle modifiche quando la funzione ritorna, allora devi fare esplicitamente una copia dell'oggetto prima di apportare le modifiche temporanee alla copia.


La tua risposta è la migliore Voglio anche pubblicare un semplice esempio def ch(str) str.reverse! end; str="abc"; ch(str); puts str #=> "cba"
fangxing del

Questa è la risposta corretta! Questo è anche molto ben spiegato qui: robertheaton.com/2014/07/22/… . Ma quello che ancora non capisco è questo: def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}". Questo stampa "Ruby è pass-by-value". Ma la variabile interna fooè stata riassegnata. Se barfosse un array, la riassegnazione non avrebbe effetto baz. Perché?
Haffla,

Non capisco la tua domanda. Penso che dovresti fare una domanda completamente nuova invece di fare commenti qui.
David Winiecki,

42

Ruby passa per riferimento o per valore?

Ruby è pass-by-value. Sempre. Nessuna eccezione. No se. No ma.

Ecco un semplice programma che dimostra questo fatto:

def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value

15
@DavidJ .: "L'errore qui è che il parametro locale è stato riassegnato (indicato in una nuova posizione in memoria)" - Non è un errore, è la definizione di pass-by-value . Se Ruby fosse pass-by-reference, la riassegnazione all'associazione dell'argomento del metodo locale nella chiamata avrebbe anche riassegnato l'associazione della variabile locale nel chiamante. Che non ha fatto. Ergo, Ruby è pass-by-value. Il fatto che se si modifica un valore modificabile, il valore cambia è completamente irrilevante, è così che funziona lo stato mutabile. Ruby non è un linguaggio puramente funzionale.
Jörg W Mittag,

5
Grazie a Jörg per difendere la vera definizione di "pass-by-value". Si sta chiaramente sciogliendo il cervello quando il valore è in realtà un riferimento, anche se il rubino passa sempre per valore.
Douglas,

9
Questo è sofisma. La distinzione pratica tra "passa per valore" e "passa per riferimento" è semantica, non sintattica. Diresti che le matrici C sono pass-by-value? Certo che no, anche se quando si passa il nome di un array a una funzione si passa un puntatore immutabile, e solo i dati a cui si riferisce il puntatore possono essere mutati. Chiaramente i tipi di valore in Ruby vengono passati per valore e i tipi di riferimento vengono passati per riferimento.
dodgethesteamroller,

3
@dodgethesteamroller: sia Ruby che C sono pass-by-value. Sempre. Nessuna eccezione, non se no, ma. La distinzione tra pass-by-value e pass-by-reference è se si passa il valore a cui punta il riferimento o si passa il riferimento. C passa sempre il valore, mai il riferimento. Il valore può essere o meno un puntatore, ma quale sia il valore è irrilevante se viene passato in primo luogo. Anche Ruby passa sempre il valore, mai il riferimento. Quel valore è sempre un puntatore, ma di nuovo è irrilevante.
Jörg W Mittag,

44
Questa risposta, mentre in senso stretto vero , non è molto utile . Il fatto che il valore passato sia sempre un puntatore non è irrilevante. È una fonte di confusione per le persone che stanno cercando di imparare e la tua risposta non fa assolutamente nulla per aiutare con quella confusione.

20

Ruby è pass-by-value in senso stretto, MA i valori sono riferimenti.

Questo potrebbe essere chiamato " pass-reference-by-value ". Questo articolo ha la migliore spiegazione che ho letto: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/

Il pass-riferimento per valore potrebbe essere brevemente spiegato come segue:

Una funzione riceve un riferimento (e accederà) allo stesso oggetto in memoria utilizzato dal chiamante. Tuttavia, non riceve la casella in cui il chiamante sta memorizzando questo oggetto; come nel pass-value-by-value, la funzione fornisce la propria casella e crea una nuova variabile per sé.

Il comportamento risultante è in realtà una combinazione delle definizioni classiche di pass-by-reference e pass-by-value.


"passa riferimento per valore" è la stessa frase che uso per descrivere il passaggio degli argomenti di Ruby. Penso che sia la frase più accurata e succinta.
Wayne Conrad,

16

Ci sono già delle ottime risposte, ma voglio pubblicare la definizione di una coppia di autorità sull'argomento, ma anche sperare che qualcuno possa spiegare cosa hanno detto le autorità Matz (creatore di Ruby) e David Flanagan nel loro eccellente libro di O'Reilly, Il linguaggio di programmazione Ruby .

[dal 3.8.1: Riferimenti agli oggetti]

Quando si passa un oggetto a un metodo in Ruby, si tratta di un riferimento a un oggetto che viene passato al metodo. Non è l'oggetto stesso e non è un riferimento al riferimento all'oggetto. Un altro modo per dirlo è che gli argomenti del metodo vengono passati per valore anziché per riferimento , ma che i valori passati sono riferimenti a oggetti.

Poiché i riferimenti agli oggetti vengono passati ai metodi, i metodi possono utilizzare tali riferimenti per modificare l'oggetto sottostante. Queste modifiche sono quindi visibili quando il metodo ritorna.

Tutto questo ha senso per me fino all'ultimo paragrafo, e in particolare a quest'ultima frase. Questo è nella migliore delle ipotesi fuorviante e, nel peggiore dei casi, confuso. In che modo le modifiche al riferimento passato per valore potrebbero cambiare l'oggetto sottostante?


1
Perché il riferimento non viene modificato; l'oggetto sottostante è.
dodgethesteamroller,

1
Perché l'oggetto è mutevole. Ruby non è un linguaggio puramente funzionale. Questo è completamente ortogonale al riferimento pass-by vs pass-by-value (tranne per il fatto che in un linguaggio puramente funzionale, pass-by-value e pass-by-reference producono sempre gli stessi risultati, quindi il linguaggio potrebbe usa uno o entrambi senza che tu lo sappia).
Jörg W Mittag,

Un buon esempio sarebbe se invece di un'assegnazione variabile in una funzione, osservi il caso di passare un hash a una funzione e fare una fusione! sull'hash passato. L'hash originale viene modificato.
elc

16

Ruby passa per riferimento o per valore?

Ruby è pass-by-reference. Sempre. Nessuna eccezione. No se. No ma.

Ecco un semplice programma che dimostra questo fatto:

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 è riferimento pass-by 2279146940 perché gli object_id (indirizzi di memoria) sono sempre gli stessi;)

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

=> alcune persone non si rendono conto del riferimento perché l'assegnazione locale può avere la precedenza, ma è chiaramente pass-by-reference


Questa è l'unica risposta corretta e presenta alcuni simpatici gotcha: Prova a = 'foobar'; b = a; b [5] = 'z', sia a che b verranno modificati.
Martijn,

2
@Martijn: il tuo argomento non è del tutto valido. Esaminiamo la tua dichiarazione di codice per dichiarazione. a = 'foobar' crea un nuovo riferimento che punta a 'foobar'. b = a crea un secondo riferimento agli stessi dati di a. b [5] = 'z' cambia il sesto carattere del valore a cui fa riferimento b in una 'z' (il valore a cui fa riferimento casualmente anche a viene modificato). Ecco perché "entrambi vengono modificati" nei termini o, più precisamente, perché "viene modificato il valore a cui fanno riferimento entrambe le variabili".
Lukas_Skywalker,

2
Non stai facendo nulla con il riferimento nel tuo barmetodo. Stai semplicemente modificando l'oggetto a cui punta il riferimento , ma non il riferimento stesso. L'unico modo per modificare i riferimenti in Ruby è per incarico. Non è possibile modificare i riferimenti chiamando i metodi in Ruby perché i metodi possono essere chiamati solo su oggetti e i riferimenti non sono oggetti in Ruby. Il tuo esempio di codice dimostra che Ruby ha condiviso lo stato mutabile (che non è in discussione qui), ma non fa nulla per chiarire la distinzione tra valore per passaggio e passaggio per riferimento.
Jörg W Mittag,

1
Quando qualcuno chiede se una lingua è "pass-by-reference", di solito vogliono sapere quando si passa qualcosa a una funzione e la funzione la modifica, verrà modificata al di fuori della funzione. Per Ruby la risposta è "sì". Questa risposta è utile nel dimostrare che, la risposta di @ JörgWMittag è estremamente inutile.
Toby 1 Kenobi,

@ Toby1Kenobi: sei ovviamente libero di usare la tua definizione personale del termine "pass-by-value", che è diversa dalla definizione comune e ampiamente usata. Tuttavia, se lo fai, dovresti essere pronto a confondere le persone, specialmente se trascuri di rivelare il fatto che stai parlando di un concetto molto diverso, in alcuni aspetti persino opposto rispetto a tutti gli altri. In particolare, "pass-by-reference" non si preoccupa se il "qualcosa" che viene passato può essere modificato o meno, ma piuttosto di cosa sia quel "qualcosa", in particolare, se è il riferimento ...
Jörg W Mittag,

8

I parametri sono una copia del riferimento originale. Pertanto, è possibile modificare i valori, ma non è possibile modificare il riferimento originale.


2

Prova questo:--

1.object_id
#=> 3

2.object_id
#=> 5

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

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

l'identificatore a contiene object_id 3 per l'oggetto valore 1 e l'identificatore b contiene object_id 5 per l'oggetto valore 2.

Ora fai questo: -

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

Ora, aeb contengono entrambi lo stesso object_id 5 che si riferisce all'oggetto valore 2. Quindi, la variabile Ruby contiene object_ids per fare riferimento agli oggetti valore.

Effettuare le seguenti operazioni dà anche errori: -

c
#=> error

ma fare questo non darà errore: -

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

Qui l'identificatore restituisce un oggetto valore 11 il cui ID oggetto è 23, ovvero object_id 23 è nell'identificatore a, Ora vediamo un esempio usando il metodo.

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 è assegnato con un valore di ritorno di x. Mostra chiaramente che l'argomento è passato dal valore 11 e che il valore 11 stesso è un oggetto con ID oggetto univoco 23.

Ora vedi anche questo: -

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

Qui, l'identificatore arg contiene inizialmente object_id 23 per fare riferimento a 11 e dopo l'assegnazione interna con valore oggetto 12, contiene object_id 25. Ma non cambia il valore a cui fa riferimento l'identificatore x usato nel metodo di chiamata.

Quindi, Ruby viene passato per valore e le variabili Ruby non contengono valori ma contengono riferimenti all'oggetto valore.


1

Il rubino viene interpretato. Le variabili sono riferimenti ai dati, ma non ai dati stessi. Ciò facilita l'utilizzo della stessa variabile per dati di tipi diversi.

L'assegnazione di lhs = rhs quindi copia il riferimento su rhs, non sui dati. Ciò differisce in altre lingue, come ad esempio C, in cui il compito esegue una copia dei dati in lhs da rhs.

Quindi per la chiamata di funzione, la variabile passata, diciamo x, viene effettivamente copiata in una variabile locale nella funzione, ma x è un riferimento. Ci saranno quindi due copie del riferimento, entrambe riferite agli stessi dati. Uno sarà nel chiamante, uno nella funzione.

L'assegnazione nella funzione copia quindi un nuovo riferimento alla versione di x della funzione. Dopo questo la versione di x del chiamante rimane invariata. È ancora un riferimento ai dati originali.

Al contrario, usando il metodo .replace su x, Ruby eseguirà una copia dei dati. Se la sostituzione viene utilizzata prima di qualsiasi nuova assegnazione, in effetti il ​​chiamante vedrà anche i dati cambiare nella sua versione.

Allo stesso modo, fintanto che il riferimento originale è intatto per la variabile passata, le variabili dell'istanza saranno le stesse che vede il chiamante. All'interno della struttura di un oggetto, le variabili di istanza hanno sempre i valori di riferimento più aggiornati, indipendentemente dal fatto che siano forniti dal chiamante o impostati nella funzione a cui è stata passata la classe.

'Call by value' o 'call by reference' è confuso qui a causa della confusione su '=' Nelle lingue compilate '=' è una copia dei dati. Qui in questa lingua interpretata '=' è una copia di riferimento. Nell'esempio hai il riferimento passato seguito da una copia di riferimento attraverso '=' che blocca l'originale passato come riferimento, e quindi le persone che ne parlano come se '=' fossero una copia di dati.

Per essere coerenti con le definizioni, dobbiamo mantenere ".replace" in quanto è una copia di dati. Dal punto di vista di ".replace" vediamo che questo è davvero un riferimento. Inoltre, se esaminiamo il debugger, vediamo i riferimenti passare, poiché le variabili sono riferimenti.

Tuttavia, se dobbiamo mantenere '=' come frame di riferimento, in effetti possiamo vedere i dati passati fino a un compito, e quindi non possiamo più vederli dopo il compito, mentre i dati del chiamante rimangono invariati. A livello comportamentale, questo valore viene trasmesso per valore purché non consideriamo il valore trasferito come composito, poiché non saremo in grado di mantenerne una parte modificando l'altra parte in un singolo compito (poiché tale compito cambia il riferimento e l'originale esce dal campo di applicazione). Ci sarà anche una verruca, in tal caso le variabili negli oggetti saranno riferimenti, così come tutte le variabili. Quindi saremo costretti a parlare del passaggio di "riferimenti per valore" e dovremo usare locuzioni correlate.


1

Va notato che non è nemmeno necessario utilizzare il metodo "sostituisci" per modificare il valore originale del valore. Se si assegna uno dei valori di hash per un hash, si sta modificando il valore originale.

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

hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"

Un'altra cosa che ho trovato. Se si passa un tipo numerico, tutti i tipi numerici sono immutabili e quindi ARE passano per valore. La funzione di sostituzione che ha funzionato con la stringa sopra, NON funziona per nessuno dei tipi numerici.
Don Carr,

1
Two references refer to same object as long as there is no reassignment. 

Qualsiasi aggiornamento nello stesso oggetto non farà riferimento alla nuova memoria poiché è ancora nella stessa memoria. Ecco alcuni esempi:

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

0

Si ma ....

Ruby passa un riferimento a un oggetto e poiché tutto in ruby ​​è un oggetto, allora potresti dire che passa per riferimento.

Non sono d'accordo con le pubblicazioni qui che sostengono che è un passaggio per valore, che mi sembrano giochi pedanti e sinergici.

Tuttavia, in effetti "nasconde" il comportamento perché la maggior parte delle operazioni ruby ​​fornisce "out of the box" - ad esempio operazioni su stringhe, produce una copia dell'oggetto:

> astringobject = "lowercase"

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

> puts astringobject
lowercase

> puts bstringobject
LOWERCASE

Ciò significa che per la maggior parte del tempo, l'oggetto originale rimane invariato, dando l'impressione che il rubino sia "passa per valore".

Naturalmente quando si progettano le proprie classi, la comprensione dei dettagli di questo comportamento è importante sia per il comportamento funzionale, sia per l'efficienza della memoria e le prestazioni.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.