LISP del 1959 di McCarthy
All'inizio del 1959, John McCarthy scrisse un innovativo documento che definiva solo nove funzioni primitive che, quando messe insieme, costituiscono ancora oggi la base per tutte le lingue simili a quelle del LISP. Il documento è disponibile digitalizzato qui:
http://www-formal.stanford.edu/jmc/recursive.pdf
Il vostro compito è quello di attuare pienamente un parser e interprete per LISP di McCarthy esattamente come descritto nel documento 1960: Cioè, le funzioni QUOTE
, ATOM
, EQ
, CAR
, CDR
, CONS
, COND
, LAMBDA
, e LABEL
dovrebbero essere funzionale. Il documento avrà la precedenza su questo testo di sfida se si considera la correttezza delle risposte, ma ho cercato di riassumere le nove funzioni di seguito. Si noti che la lingua sarà in TUTTI MAIUSCOLI e non è necessario il controllo degli errori, si presume che tutti gli input siano validi.
tipi
- Esistono solo due tipi nel LISP di McCarthy: un atomo e un elenco collegato, che è definito ricorsivamente come una testa, che può essere un elenco o un atomo, e un elenco a cui è collegata la testa (coda).
NIL
ha la proprietà speciale di essere sia un atomo che un elenco. - Come per la carta, i nomi degli atomi saranno composti solo da lettere maiuscole, numeri e caratteri spaziali, sebbene le stringhe di spazi consecutivi debbano essere considerate come un solo spazio e tutti i caratteri spaziali iniziali e finali dovrebbero essere rimossi. Esempio nomi atomo equivalente (sostituzione di sottolineatura con carattere di spazio):
___ATOM__1__ = ATOM_1
. Esempio di nomi di atomi non equivalenti:A_TOM_1 != ATOM_1
- Gli elenchi sono indicati da parentesi e un implicito
NIL
è alla fine di ogni elenco. Gli elementi in un elenco sono separati da virgole e non spazi bianchi come nella maggior parte dei moderni Lisps. Quindi l'elenco(ATOM 1, (ATOM 2))
sarebbe{[ATOM 1] -> {[ATOM 2] -> NIL} -> NIL}
.
QUOTE
:
- Accetta un argomento che può essere un atomo (singolo elemento) o un elenco collegato. Restituisce esattamente l'argomento.
- Casi test:
(QUOTE, ATOM 1) -> ATOM 1
(QUOTE, (ATOM 1, ATOM 2)) -> (ATOM 1, ATOM 2)
ATOM
:
- Accetta un argomento che può essere un atomo (singolo elemento) o un elenco collegato. Restituisce
T
(true) se l'argomento è un atomo oNIL
(false) se l'argomento non è un atomo. - Casi test:
(ATOM, (QUOTE, ATOM 1)) -> T
(ATOM, (QUOTE, (ATOM 1, ATOM 2))) -> NIL
EQ
:
- Accetta due argomenti che devono essere atomi (il comportamento è indefinito se uno degli argomenti non è un atomo). Restituisce
T
(vero) se i due atomi sono equivalenti oNIL
(falso) se non lo sono. - Casi test:
(EQ, (QUOTE, ATOM 1), (QUOTE, ATOM 1)) -> T
(EQ, (QUOTE, ATOM 1), (QUOTE, ATOM 2)) -> NIL
CAR
:
- Accetta un argomento che deve essere un elenco (il comportamento non è definito se non è un elenco). Restituisce il primo atomo (head) di quell'elenco.
- Casi test:
(CAR, (QUOTE, (ATOM 1, ATOM 2))) -> ATOM 1
CDR
:
- Accetta un argomento che deve essere un elenco (il comportamento non è definito se non è un elenco). Restituisce ogni atomo tranne il primo atomo dell'elenco, ovvero la coda. Si noti che ogni elenco termina in modo implicito
NIL
, quindiCDR
verrà restituito l' esecuzione su un elenco che sembra avere solo un elementoNIL
. - Casi test:
(CDR, (QUOTE, (ATOM 1, ATOM 2))) -> (ATOM 2)
(CDR, (QUOTE, (ATOM 1))) -> NIL
CONS
:
- Accetta due argomenti. Il primo può essere un atomo o un elenco, ma il secondo deve essere un elenco o
NIL
. Prepara il primo argomento al secondo argomento e restituisce l'elenco appena creato. - Casi test:
(CONS, (QUOTE, ATOM 1), (QUOTE, NIL)) -> (ATOM 1)
(CONS, (QUOTE, ATOM 1), (CONS, (QUOTE, ATOM 2), (QUOTE, NIL))) -> (ATOM 1, ATOM 2)
COND
:
- Questa è una sorta di dichiarazione "if-else" di LISP. Accetta una quantità di argomenti di lunghezza variabile, ognuno dei quali deve essere esattamente un elenco di lunghezza 2. Per ogni elenco di argomenti in ordine, valuta il primo termine e, se è vero (T), restituisce il secondo termine associato ed esce dalla funzione . Se il primo termine non è vero, passare all'argomento successivo e verificarne la condizione, e così via fino al raggiungimento della prima condizione vera. Almeno una delle condizioni dell'argomento può essere considerata vera - se sono tutte false, si tratta di un comportamento indefinito. Vedi pagina 4 per un buon esempio del comportamento di questa funzione.
- Casi test:
(COND, ((ATOM, (QUOTE, ATOM 1)), (QUOTE, 1)), ((ATOM, (QUOTE, (ATOM 1, ATOM 2))), (QUOTE, 2))) -> 1
(COND, ((ATOM, (QUOTE, (ATOM 1, ATOM 2))), (QUOTE, 2)), ((ATOM, (QUOTE, ATOM 1)), (QUOTE, 1))) -> 1
LAMBDA
:
- Definisce una funzione anonima. Accetta due argomenti, il primo è un elenco di atomi che rappresentano gli argomenti della funzione e il secondo è qualsiasi espressione S (il corpo della funzione), che in genere utilizza gli argomenti.
- Casi test:
- Definizione e utilizzo di una funzione anonima "isNull":
((LAMBDA, (ATOM 1), (EQ, ATOM 1, (QUOTE, NIL))), (QUOTE, NIL)) -> T
((LAMBDA, (ATOM 1), (EQ, ATOM 1, (QUOTE, NIL))), (QUOTE, ATOM 1)) -> NIL
LABEL
:
- Dà un nome a una
LAMBDA
funzione anonima , che consente anche a quella funzione di essere chiamata ricorsivamente nel corpo diLAMBDA
. Accetta due argomenti, il primo è un'etichetta e il secondo è laLAMBDA
funzione alla quale l'etichetta dovrebbe essere associata. Restituisce il nome fornito. L'ambito di tutti iLABEL
nomi è globale e ridefinire unLABEL
comportamento non definito. - Curiosità, in
LABEL
realtà non è necessario creare funzioni ricorsive, poiché ora sappiamo cheLAMBDA
può essere utilizzato con un "Y-Combinator" per svolgere questo compito, ma McCarthy non era a conoscenza di questo metodo durante la stesura del documento originale. Rende comunque i programmi molto più facili da scrivere. - Casi test:
(LABEL, SUBST, (LAMBDA, (X, Y, Z), (COND, ((ATOM, Z), (COND, ((EQ, Y, Z), X), ((QUOTE, T), Z))), ((QUOTE, T), (CONS, (SUBST, X, Y, (CAR, Z)), (SUBST, X, Y, (CDR, Z))))))) -> SUBST
- (dopo aver eseguito quanto sopra)
(SUBST, (QUOTE, A), (QUOTE, B), (QUOTE, (A, B, C))) -> (A, A, C)
Per aiutare a visualizzare la SUBST
funzione sopra, potrebbe essere rappresentata come questo pseudocodice simile a Python:
def substitute(x, y, z): # substitute all instances of y (an atom) with x (any sexp) in z
if isAtom(z):
if y == z:
return x
elif True:
return z
elif True:
return substitute(x,y,z[0]) + substitute(x,y,z[1:])
CASO DI PROVA FINALE:
Se l'ho trascritto correttamente, il tuo interprete dovrebbe essere in grado di interpretare EVAL
con questo codice:
(LABEL, CAAR, (LAMBDA, (X), (CAR, (CAR, X))))
(LABEL, CDDR, (LAMBDA, (X), (CDR, (CDR, X))))
(LABEL, CADR, (LAMBDA, (X), (CAR, (CDR, X))))
(LABEL, CDAR, (LAMBDA, (X), (CDR, (CAR, X))))
(LABEL, CADAR, (LAMBDA, (X), (CAR, (CDR, (CAR, X)))))
(LABEL, CADDR, (LAMBDA, (X), (CAR, (CDR, (CDR, X)))))
(LABEL, CADDAR, (LAMBDA, (X), (CAR, (CDR, (CDR, (CAR, X))))))
(LABEL, ASSOC, (LAMBDA, (X, Y), (COND, ((EQ, (CAAR, Y), X), (CADAR, Y)), ((QUOTE, T), (ASSOC, X, (CDR, Y))))))
(LABEL, AND, (LAMBDA, (X, Y), (COND, (X, (COND, (Y, (QUOTE, T)), ((QUOTE, T), (QUOTE, NIL)))), ((QUOTE, T), (QUOTE, NIL)))))
(LABEL, NOT, (LAMBDA, (X), (COND, (X, (QUOTE, NIL)), ((QUOTE, T), (QUOTE, T)))))
(LABEL, NULL, (LAMBDA, (X), (AND, (ATOM, X), (EQ, X, (QUOTE, NIL)))))
(LABEL, APPEND, (LAMBDA, (X, Y), (COND, ((NULL, X), Y), ((QUOTE, T), (CONS, (CAR, X), (APPEND, (CDR, X), Y))))))
(LABEL, LIST, (LAMBDA, (X, Y), (CONS, X, (CONS, Y, (QUOTE, NIL)))))
(LABEL, PAIR, (LAMBDA, (X, Y), (COND, ((AND, (NULL, X), (NULL, Y)), (QUOTE, NIL)), ((AND, (NOT, (ATOM, X)), (NOT, (ATOM, Y))), (CONS, (LIST, (CAR, X), (CAR, Y)), (PAIR, (CDR, X), (CDR, Y)))))))
(LABEL, EVAL, (LAMBDA, (E, A), (COND, ((ATOM, E), (ASSOC, E, A)), ((ATOM, (CAR, E)), (COND, ((EQ, (CAR, E), (QUOTE, QUOTE)), (CADR, E)), ((EQ, (CAR, E), (QUOTE, ATOM)), (ATOM, (EVAL, ((CADR, E), A)))), ((EQ, (CAR, E), (QUOTE, EQ)), (EQ, (EVAL, (CADR, E, A)), (EVAL, (CADDR, E, A)))), ((EQ, (CAR, E), (QUOTE, COND)), (EVCON, (CDR, E), A)), ((EQ, (CAR, E), (QUOTE, CAR)), (CAR, (EVAL, (CADR, E), A))), ((EQ, (CAR, E), (QUOTE, CDR)), (CDR, (EVAL, (CADR, E), A))), ((EQ, (CAR, E), (QUOTE, CONS)), (CONS, (EVAL, (CADR, E), A), (EVAL, (CADDR, E), A))), ((QUOTE, T), (EVAL, (CONS, (ASSOC, (CAR, E), A), (EVLIS, (CDR, E), A)), A)))), ((EQ, (CAAR, E), (QUOTE, LABEL)), (EVAL, (CONS, (CADDAR, E), (CDR, E)), (CONS, (CONS, (CADAR, E), (CONS, (CAR, E), (CONS, A, (QUOTE, NIL))))))), ((EQ, (CAAR, E), (QUOTE, LAMBDA)), (EVAL, (CADDAR, E), (APPEND, (PAIR, (CADAR, E), (EVLIS, (CDR, E), A)), A))))))
(LABEL, EVCON, (LAMBDA, (C, A), (COND, ((EVAL, (CAAR, C), A), (EVAL, (CADAR, C), A)), ((QUOTE, T), (EVCON, (CDR, C), A)))))
(LABEL, EVLIS, (LAMBDA, (M, A), (COND, ((NULL, M), (QUOTE, NIL)), ((QUOTE, T), (CONS, (EVAL, (CAR, M), A), (EVLIS, (CDR, M), A))))))
Dopo aver eseguito quel colosso, questa riga dovrebbe restituire (A, B, C)
:
(EVAL, (QUOTE, (CONS, X, (QUOTE, (B, C)))), (QUOTE, ((X, A), (Y, B))))
Tuttavia, per citare John McCarthy stesso a pagina 16, sembra che stia esaurendo i personaggi sul suo computer:
Se sul computer fossero disponibili più personaggi, potrebbe essere notevolmente migliorato ...
Pertanto, questa sfida è contrassegnata da code-golf e la risposta più breve in caratteri sarà il vincitore. Si applicano scappatoie standard. In bocca al lupo!
Nota su String Evals : capisco che alcuni pensano che sia possibile vincere questa sfida usando un Lisp e modificando la sintassi per adattarla alla lingua host e quindi usando una stringa (eval)
. Non sono particolarmente convinto che questo approccio vincerà necessariamente soprattutto con le regole di denominazione dell'identificatore, e anche se pensasse che vietare le stringhe eval
in tutte le lingue sarebbe una pendenza soggettiva e sfuggente. Ma non voglio punire le persone per aver fatto questa sfida nel modo "giusto", quindi potrei consentire a due vincitori di questa sfida, uno in un linguaggio simile a Lisp e uno in un linguaggio non Lispy, se questo diventa un problema .
((LAMBDA, (ATOM 1), (EQ, ATOM 1, (QUOTE, NIL))), (QUOTE, NIL)) -> NIL
Dove (QUOTE NIL)
alla fine è l'input, quindi questo dovrebbe tornare T
?
-> NIL
CONS
te dire "Aggiunge il primo argomento al secondo argomento e restituisce l'elenco appena creato", ma i casi di test mostrano che il secondo argomento è stato aggiunto al primo. Che è corretto?