Perché le lingue sono così tante per valore?

36

Anche le lingue in cui hai una manipolazione puntata esplicita come C viene sempre passata per valore (puoi puoi passarle per riferimento ma non è il comportamento predefinito).

Qual è il vantaggio di questo, perché sono passate così tante lingue dai valori e perché gli altri vengono passati per riferimento ? (Capisco che Haskell è passato per riferimento anche se non ne sono sicuro).

    
posta My code has no bugs 19.06.2012 - 21:33
fonte

7 risposte

58

Il passaggio per valore è spesso più sicuro del passaggio per riferimento, poiché non è possibile modificare accidentalmente i parametri nel metodo / funzione. Questo rende il linguaggio più semplice da utilizzare, poiché non devi preoccuparti delle variabili che assegni a una funzione. Sai che non saranno cambiati, e questo è spesso ciò che ti aspetti .

Tuttavia, se vuoi per modificare i parametri, devi avere qualche operazione esplicita per renderlo chiaro (passare un puntatore). Ciò costringerà tutti i chiamanti a effettuare la chiamata in modo leggermente diverso ( &variable , in C) e ciò rende esplicito che il parametro variabile può essere modificato.

Quindi ora puoi assumere che una funzione non cambierà il parametro della variabile, a meno che non sia esplicitamente contrassegnato per farlo (richiedendo di passare un puntatore). Questa è una soluzione più sicura e pulita rispetto all'alternativa: supponi che tutto possa modificare i tuoi parametri, a meno che non affermino specificamente che non possono.

    
risposta data 19.06.2012 - 21:43
fonte
55

Call-by-value e call-by-reference sono tecniche di implementazione che sono state scambiate per modalità di passaggio dei parametri molto tempo fa.

All'inizio c'era FORTRAN. FORTRAN aveva solo una chiamata per riferimento, poiché le subroutine dovevano essere in grado di modificare i loro parametri, ei cicli di calcolo erano troppo costosi per consentire più modalità di passaggio dei parametri, in più non si sapeva abbastanza sulla programmazione quando FORTRAN veniva definito per la prima volta.

ALGOL ha trovato call-by-name e call-by-value. Call-by-value era per cose che non dovevano essere cambiate (parametri di input). Call-by-name era per i parametri di output. Call-by-name si è rivelato essere un grosso coccio e ALGOL 68 l'ha abbandonato.

PASCAL ha fornito call-by-value e call-by-reference. Non ha fornito alcun modo al programmatore di dire al compilatore che stava passando un oggetto di grandi dimensioni (di solito un array) per riferimento, per evitare di soffiare lo stack dei parametri, ma che l'oggetto non dovrebbe essere modificato.

PASCAL ha aggiunto dei puntatori al lessico del linguaggio design.

C ha fornito call-by-value e ha simulato call-by-reference definendo un operatore kludge per restituire un puntatore a un oggetto arbitrario in memoria.

Le lingue successive hanno copiato C, soprattutto perché i progettisti non avevano mai visto altro. Questo è probabilmente il motivo per cui call-by-value è così popolare.

C ++ ha aggiunto un kludge in cima al kludge C per fornire call-by-reference.

Ora, come risultato diretto di call-by-value vs call-by-reference vs. call-by-pointer-kludge, C e C ++ (programmatori) hanno orribili mal di testa con puntatori const e puntatori a const (leggi -only) oggetti.

Ada è riuscita a evitare tutto questo incubo.

Ada non ha call-by-value esplicito rispetto a call-by-reference. Piuttosto, Ada ha parametri (che possono essere letti ma non scritti), parametri out (che DEVONO essere scritti prima che possano essere letti), e parametri out, che possono essere letti e scritti in qualsiasi ordine. Il compilatore decide se un particolare parametro viene passato per valore o per riferimento: è trasparente per il programmatore.

    
risposta data 20.06.2012 - 02:22
fonte
13

Il passaggio per riferimento consente effetti collaterali indesiderati molto sottili che sono molto difficili da individuare quasi impossibile quando iniziano a causare il comportamento involontario.

Il valore passato, in particolare i parametri di input final , static o const fa scomparire l'intera classe di errori.

I linguaggi immutabili sono ancora più deterministici e più facili da ragionare e capire cosa sta succedendo e cosa ci si aspetta che esca da una funzione.

    
risposta data 19.06.2012 - 21:48
fonte
8

Why are so many languages passed by value?

Il punto in cui si suddividono i programmi di grandi dimensioni in piccole subroutine è che è possibile ragionare in modo indipendente sulle subroutine. Pass-by-reference rompe questa proprietà. (Come lo stato mutabile condiviso.)

Even languages where you have explicit pointer manipulation like C it's always passed by value (you can pass them by reference but that's not the default behavior).

In realtà, C è sempre valore pass-by, mai pass-per-riferimento. Puoi prendere un indirizzo di qualcosa e passare quell'indirizzo, ma l'indirizzo sarà comunque passato per valore.

What is the benefit of this, why are so many languages passed by values and why are others passed by reference ?

Esistono due motivi principali per l'utilizzo del riferimento pass-by:

  1. simulazione di più valori di ritorno
  2. efficienza

Personalmente, penso che il n. 1 sia falso: è quasi sempre una scusa per una cattiva API e / o design della lingua:

  1. Se hai bisogno di più valori di ritorno, non simularli, usa solo un linguaggio che li supporta.
  2. Si può anche simulare più valori di ritorno confezionandoli in una struttura di dati leggera come una tupla. Ciò funziona particolarmente bene se la lingua supporta la corrispondenza del modello o il binding distruttivo. Per esempio. Rubino:

    def foo
      # This is actually just a single return value, an array: [1, 2, 3]
      return 1, 2, 3
    end
    
    # Ruby supports destructuring bind for arrays: a, b, c = [1, 2, 3]
    one, two, three = foo
    
  3. Spesso non hai nemmeno bisogno di più valori di ritorno. Ad esempio, uno schema comune è che la subroutine restituisce un codice di errore e il risultato effettivo viene scritto per riferimento. Invece, dovresti semplicemente lanciare un'eccezione se l'errore è inaspettato o restituire un Either<Exception, T> se è previsto un errore. Un altro pattern consiste nel restituire un valore booleano che indica se l'operazione ha avuto esito positivo e restituire il risultato effettivo per riferimento. Di nuovo, se l'errore è inaspettato, dovresti invece lanciare un'eccezione, se l'errore è previsto, ad es. quando cerchi un valore in un dizionario, devi invece restituire Maybe<T> .

Il pass-by-reference potrebbe essere più efficiente del pass-by-value, perché non devi copiare il valore.

(I understand Haskell is passed by reference though I'm not sure).

No, Haskell non è un riferimento pass-by. Né è pass-by-value. Pass-by-reference e pass-by-value sono strategie di valutazione rigorose, ma Haskell non è severo.

In effetti, la Specifica Haskell non specifica nessuna particolare strategia di valutazione. La maggior parte delle implementazioni di Hakell utilizzano una combinazione di call-by-name e call-by-need (una variante di call-by-name con memoization), ma lo standard non lo impone.

Si noti che anche per linguaggi rigorosi, non ha senso distinguere tra pass-by-reference o pass-by-value per i linguaggi funzionali poiché la differenza tra loro può essere osservata solo se si modifica il riferimento. Pertanto, l'implementatore è libero di scegliere tra i due, senza rompere la semantica del linguaggio.

    
risposta data 20.06.2012 - 02:22
fonte
3

C'è un comportamento diverso a seconda del modello chiamante della lingua, del tipo di argomenti e dei modelli di memoria della lingua.

Con semplici tipi nativi, il passaggio per valore ti consente di passare il valore dai registri. Questo può essere molto veloce poiché il valore non deve essere caricato dalla memoria né salvato indietro. L'ottimizzazione simile è anche possibile semplicemente riutilizzando la memoria usata dall'argomento una volta che il callee ha finito con esso, senza rischiare di fare confusione con la copia del chiamante dell'oggetto. Se l'argomento fosse un oggetto temporaneo, avresti probabilmente salvato una copia completa (C ++ 11 rende questo tipo di ottimizzazione ancora più ovvio con il nuovo riferimento di destra e il suo movimento semantico).

In molti linguaggi OO (C ++ è più un'eccezione in questo contesto), non puoi passare un oggetto per valore. Sei costretto a passarlo per riferimento. Questo rende il codice polimorfico di default e sono più in linea con la nozione di istanze proprie di OO. Inoltre, se vuoi passare per valore, devi crearne una copia, riconoscendo il costo delle prestazioni che ha generato tale azione. In questo caso, la lingua sceglie per te l'approccio che è più propenso a darti la migliore performance.

In caso di linguaggi funzionali, suppongo che il passaggio per valore o riferimento sia semplicemente una questione di ottimizzazione. Dal momento che le funzioni in questo linguaggio sono pure e quindi prive di effetti collaterali, non c'è davvero alcun motivo per copiare il valore ad eccezione della velocità. Sono persino abbastanza sicuro che tale linguaggio spesso condividesse la stessa copia di oggetti con gli stessi valori, una possibilità disponibile solo se si utilizzava una passata (const) di riferimento semantica. Python ha anche usato questo trucco per interi e stringhe comuni (come metodi e nomi di classi), spiegando perché interi e stringhe sono oggetti costanti in Python. Ciò aiuta anche a ottimizzare di nuovo il codice, consentendo per esempio la comparazione del puntatore invece del confronto dei contenuti, e facendo una valutazione pigra di alcuni dati interni.

    
risposta data 20.06.2012 - 00:22
fonte
2

Puoi passare un'espressione in base al valore, è naturale. Il passaggio dell'espressione mediante riferimento (temporaneo) è ... strano.

    
risposta data 19.06.2012 - 23:17
fonte
2

Se passi per riferimento, allora lavori in modo efficace sempre con i valori globali, con tutti i problemi dei globals (vale a dire ambito e effetti collaterali non voluti).

I riferimenti, proprio come i globals, a volte sono utili, ma non dovrebbero essere la tua prima scelta.

    
risposta data 19.06.2012 - 22:29
fonte

Leggi altre domande sui tag