2009-02-08 10 views
14

Questo è solo un po 'di cronistoria rimasta dagli anni '50 o c'è qualche ragione sintattica sul perché i corpi di espressione multipla delle (se) forme richiedono (progn)? Perché non si può avvolgere le molteplici espressioni in un insieme di parentesi come con (lasciare):In Common Lisp, perché i corpi di espressioni multiple di (if) richiedono istruzioni (progn)?

(if some-cond 
    ((exp1) (exp2) (exp3)) ; multi exp "then" 
    (exp4)) ; single exp "else" 

Sembra che sarebbe banale scrivere una macro per testare ogni corpo per vedere prima se si tratta di una lista e allora se lo è, se il suo primo elemento è anche una lista (e quindi non una funzione invocata) e poi avvolgere di conseguenza i suoi sottocomponenti all'interno di un (progn).

risposta

4

Non tutte le espressioni sono elenchi. Per (let ((a 42)) (if some-cond (a b c) (d e f))) non si saprebbe se (a b c) debba essere interpretato come una chiamata alla funzione a o come progn implicito.

+0

No, secondo la mia descrizione poiché (a b c) è una lista e il suo primo elemento non è anche una lista, è una chiamata di funzione e non richiede la progn. –

1

In Lisp, le parentesi indicano l'applicazione della funzione, non il raggruppamento. Che cosa significherebbe la tua espressione se exp1 fosse una funzione che ha restituito una funzione? Verrebbe chiamato con gli argomenti (exp2) (exp3) oppure no?

+0

Se anche le prime espressioni secondarie erano una lista, tutte sarebbero state racchiuse in (progn) –

3

Se non si dispone di un ramo "altro", macro standard when e unless aiuto. Altrimenti è meglio usare cond se si hanno più espressioni in qualsiasi ramo.

6

c'è qualche ragione sintatticamente perché i corpi a più espressioni di (if) forme richiedono (progn)?

La risposta è "sì", anche se forse non per il motivo che ci si aspetta. Perché Common Lisp (diversamente da Scheme e altri Lisps) richiede funcall, la tua proposta non è ambigua. Anche se fosse ambiguo, purché i tuoi utenti sappiano che le parentesi implicano progn qui, funzionerebbe.

Tuttavia, nessun'altra struttura * nella lingua ha parentesi singole/doppie opzionali. Un sacco di costruzioni hanno implicito progn s, ma la loro sintassi parentetica è sempre la stessa.

Per esempio, cond ha un implicito progn per ogni ramo:

(cond (test1 body1) (test2 body2) ...) 

Non si può passare avanti e indietro:

(cond test1 exp1 (test2 body2) t exp3) 

Quindi, anche se la vostra proposta non è ambiguo, non si adatta alla sintassi del resto della lingua. PERÒ! Come hai detto tu, la macro è banale da implementare. Dovresti farlo tu stesso e vedere se funziona bene. Potrei facilmente sbagliare; Sono abbastanza di parte perché quasi tutto il mio Lisping è in Scheme.

* Eccetto case. Hmf. Ora penso che potrebbero essercene altri.

11

Common Lisp non è perfetto perché è perfetto, è perfetto perché è perfetto.

L'intero linguaggio è costruito su 25 special operators; if è uno di quelli, progn è un altro.

if fornisce solo il meccanismo di base di testare una condizione, quindi saltare a uno o l'altro indirizzo di codice. progn fornisce il meccanismo di base per fare diverse cose e restituire il valore dell'ultimo.

Ci sono diversi macro nello standard di linguaggio che si basano su questo - ad es. when, unless, cond, case.

Se si vuole, si hanno diverse opzioni per rendere qualcosa di simile a quello che immaginate: per uno, si potrebbe scrivere un ifm macro che si aspetta implicite progn s come l'allora e il resto, clausole, o si potrebbe scrivere come hai detto tu , in modo che rilevi l'intento, oppure potresti anche scrivere una macro di lettura per aggiungere zucchero sintattico per progn.

17

In Common Lisp, questo codice:

(if t 
    ((lambda (x) (+ x 5)) 10) 
    20) 

tornerà 15. Con la sua proposta, penso che sarebbe vedere che la vera clausola è una lista, e automaticamente convertirlo in:

(if t 
    (progn (lambda (x) (+ x 5)) 10) 
    20) 

che restituirebbe 10. È giusto?

Non sono sicuro che sia "banale" distinguere tra "elenco" e "invocazione funzione" in CL. Intendi che questo cambiamento non sia retrocompatibile? (I nuovi e interessanti dialoghi Lisp sono sempre fantastici, ma in questo caso non è Common Lisp.) Oppure puoi dare un esempio di ciò che hai in mente?

4

Poiché la sintassi per IF (< - HyperSpec link) è definita come:

if test-form then-form [else-form] => result* 

Non esistono iniziano o marcatori finali. Esiste un ALLEGATO e non ALLORA-FORM *. PROGN è un meccanismo per definire una sequenza di moduli, in cui i moduli vengono eseguiti da sinistra a destra e vengono restituiti i valori dell'ultima forma.

avrebbe potuto essere definita in questo modo:

my-if test-form (then-form*) [(else-form*)] => result* 

(defmacro my-if (test then &optional else) 
    (assert (and (listp then) (listp else)) (then else)) 
    `(if ,test (progn ,@then) (progn ,@else))) 

(my-if (> (random 10) 5) 
     ((print "high") 
     :high) 
     ((print "low") 
     :low)) 

bene, c'è già un costrutto che supporta molteplici forme: COND.

(cond ((> (random 10) 5) 
     (print "high") 
     :high) 
     (t 
     (print "low") 
     :low)) 

Lo stile tipico è quello di utilizzare COND quando più alternative devono essere provato e quando ci sono più then/else-forme. IF viene usato quando c'è un solo test e sia un modulo then che un altro. Per altri casi c'è WHEN e UNLESS. WHEN e UNLESS supportano solo una o THEN forme (nessuna altra forma (s)).

Immagino sia meglio avere almeno una forma condizionale (SE in questo caso) che non abbia aggiunto livelli di parentesi. Scrivere

(if (> (random 10) 5) 
    (progn 
     (print "high") 
     :high) 
    (progn 
     (print "low") 
     :low)) 

è quindi un piccolo prezzo da pagare. Scrivi i PROGN aggiuntivi o passa alla variante COND. Se il tuo codice trarrebbe vantaggio da IF con più moduli then e else, allora scrivi quella macro (vedi sopra). Lisp lo ha, in modo che tu possa essere il tuo designer di lingue. È importante però pensare all'introduzione di una macro: la mia macro è corretta? controlla gli errori? ne vale la pena? è leggibile (per gli altri?)?

3

Nota che nelle "{} lingue" (C, C++, Java ...) hai un PROGN sotto forma di istruzione composta in parentesi graffe.

Così notare queste equivalenze/analogie:

(if antecedent consequent alternative) 
if (antecedent) consequent; else alternative; 

(if antecedent (progn consequent1 consequent2) alternative) 
if (antecedent) { consequent1; consequent2; } else alternative; 

Il PROGN operatore (e dei suoi cugini) sono "bretelle" di Lisp. Le parentesi in Lisp non sono le sue "parentesi"!

Ora considerare Perl. Perl ha un valore if completo. Ciò potrebbe essere fatto in Lisp anche:

(if antecedent (consequent1 consequent2 ...) (alternative1 alternative2 ...)) 

Es .:

(if (< foo 0) 
    ((format t "foo is less than zero") 
    (- foo)) 
    ((format t "foo is not less than zero") 
    foo)) 

ho potuto vivere con questo, credo, ma alcune persone si lamentano le parentesi in più, soprattutto nei casi più semplici.