2015-04-09 22 views
5

Qual è la differenza tra values e list o cons in Racket o Scheme? Quando è meglio usare l'uno sull'altro? Ad esempio, quale sarebbe lo svantaggio se quotient/remainder restituisce (cons _ _) anziché (values _ _)?Differenza tra più valori e tuple semplici in Racket?

+2

La risposta più obiettiva è che "valori" è probabilmente leggermente più veloce perché esegue meno allocazione. Le funzioni che restituiscono più valori possono anche essere composte (con 'compose') in modi interessanti, ma raramente l'ho trovato utile. Altrimenti, penso che sia probabilmente piuttosto soggettivo, ma io sono del campo che i "valori" dovrebbero di solito essere evitati. –

+4

Jay McCarthy ha un [ottimo post sul blog sulla simmetria delle chiamate e dei ritorni delle funzioni] (https://jeapostrophe.github.io/2013-07-15-values-post.html). Le funzioni possono essere chiamate con zero o più valori e anche restituire zero o più valori. Infatti, "return" è una sorta di "call": "Quando una funzione normalmente ritorna, puoi pensare ad essa come" chiamando la sua continuazione "con il valore di ritorno.Tutti i valori lo chiama tale continuazione con più argomenti". Prosegue mostrando come renderlo completamente simmetrico, inclusi i valori di ritorno opzionale e di parole chiave. –

+0

@GregHendershott Sì, 'call-with-values' è l'ultimo sistema plug-in-your-own-continuation. In realtà ho usato 'case-lambda' con' call-with-values', a volte, anche se non ho provato a usare gli argomenti delle parole chiave! Penso che la mia ragione per non provare il secondo è che non credo che "valori" lo sostengano: dovresti chiamare direttamente la continuazione per usare le parole chiave. –

risposta

7

Nel 2002 George Caswell ha fatto questa domanda in comp.lang.scheme. Il thread seguente è lungo, ma ha molte intuizioni. La discussione rivela che le opinioni sono divise.

https://groups.google.com/d/msg/comp.lang.scheme/ruhDvI9utVc/786ztruIUNYJ

La mia risposta a quei tempi:

> What are the motivations behind Scheme's multiple return values feature? 
> Is it meant to reflect the difference in intent, or is there a 
> runtime-practical reason? 

I imagine the reason being this. 

Let's say that need f is called by g. g needs several values from f. 
Without multiple value return, f packs the values in a list (or vector), 
which is passed to g. g then immediately unpacks the list. 

With multple values, the values are just pushed on the stack. Thus no 
packing and unpacking is done. 

Whether this should be called an optimization hack or not, is up to you. 

-- 
Jens Axel Søgaard 

We don't need no side-effecting   We don't need no allocation 
We don't need no flow control   We don't need no special-nodes 
No global variables for execution  No dark bit-flipping for debugging 
Hey! did you leave the args alone?  Hey! did you leave those bits alone? 
(Chorus)     -- "Another Glitch in the Call", a la Pink Floyd 
3

Si riferisce anche al tuo stile di programmazione. Se si utilizza values, significa in genere che si desidera restituire esplicitamente i valori n. L'utilizzo di cons, list o vector di solito significa che si desidera restituire un valore che contiene qualcosa.

Ci sono sempre dei pro/contro. Per values: può utilizzare meno memoria su alcune funzionalità. Il chiamante deve utilizzare la sintassi let-values o altri valori specifici. (Vorrei poter usare solo let come CL.)

Per cons o di altro tipo: È possibile utilizzare let o lambda a ricevere il valore di ritorno. È necessario decostruirlo esplicitamente per ottenere il valore desiderato utilizzando car o altre procedure.

Quale usare e quando? Sempre a seconda del tuo stile di programmazione e caso per caso, ma se il valore restituito non può essere rappresentato in un oggetto (ad esempio quoziente e resto), allora potrebbe essere meglio utilizzare values per rendere più chiaro il significato della procedura. Se il valore restituito è un oggetto (ad es. Nome ed età per una persona), allora potrebbe essere meglio usare cons o un altro costruttore (ad es. Record).

4

Sono semanticamente uguali in Schema e Racket. In entrambi devi sapere come appare il ritorno.

values è collegato a call-with-values e forme speciali come let-values sono solo sintassi zucchero con questa chiamata di procedura. L'utente deve conoscere la forma del risultato per utilizzare call-with-values per utilizzare il risultato. Un ritorno è spesso fatto su una pila e una chiamata è anche in pila. L'unico motivo per favorire lo values in Scheme sarebbe che non ci sono overhead tra il ritorno del produttore e la chiamata del consumatore.

Con cons (o list) l'utente deve sapere come si presenta la struttura dati del reso. Come con lo values è possibile utilizzare apply invece di call-with-values per fare la stessa cosa. In sostituzione di let-values (e altro) è facile creare una macro destructuring-bind.

In Common Lisp è piuttosto diverso. È possibile utilizzare i valori sempre se si dispone di più informazioni da fornire e l'utente può ancora utilizzarlo come procedura normale se desidera utilizzare solo il primo valore. Quindi per CL non sarebbe necessario fornire quotient come variante dal quotient/remainder funzionerebbe altrettanto bene. Solo quando si utilizzano moduli speciali o procedure che richiedono più valori, il fatto che la procedura restituisca più valori funziona come con Scheme. Questo rende values una scelta migliore in CL rispetto a Scheme dato che se ne scappa scrivendo uno anziché più procedure.

In CL è possibile accedere ad un hash come questo:

(gethash 'key *hash* 't) 
; ==> T; NIL 

Se non si utilizza il secondo valore restituito non si sa se T era il valore predefinito o il valore effettivo trovato.Qui vedi il secondo valore che indica che la chiave non è stata trovata nell'hash. Spesso non si usa quel valore se si sa che ci sono solo numeri il cui valore predefinito sarebbe già un'indicazione che la chiave non è stata trovata. In Racket:

(hash-ref hash 'key #t) 
; ==> #t 

In racchetta failure-result può essere un tonfo in modo da ottenere da parte, ma scommetto che sarebbe restituire più valori, invece, se i valori hanno funzionato come in CL. Presumo che ci sia più servizio di pulizia con la versione CL e Scheme, essendo un linguaggio minimalista, forse non volevo dare agli implementatori il lavoro extra.

4

Edit: Missed commento Alexis' sullo stesso argomento prima di pubblicare questo

Un spesso trascurato vantaggio pratico di utilizzo di più valori di ritorno sopra elenca che il rack compose "funziona solo" con funzioni che restituiscono più valori:

(define (hello-goodbye name) 
    (values (format "Hello ~a! " name) 
      (format "Goodbye ~a." name))) 

(define short-conversation (compose string-append hello-goodbye)) 

> (short-conversation "John") 
"Hello John! Goodbye John." 

La funzione prodotta da compose passerà i due valori restituiti da hello-goodbye come due argomenti a string-append. Se stai scrivendo il codice in uno stile funzionale con molte composizioni, questo è molto utile, ed è molto più naturale del passaggio esplicito di valori intorno a te con call-with-values e simili.