McCarthy 1959 LISP
Pada awal 1959, John McCarthy menulis sebuah makalah yang mendefinisikan hanya sembilan fungsi primitif yang ketika disatukan masih membentuk dasar untuk semua bahasa seperti LISP saat ini. Makalah ini tersedia secara digital di sini:
http://www-formal.stanford.edu/jmc/recursive.pdf
Tugas Anda adalah untuk sepenuhnya menerapkan parser dan interpreter untuk LISP McCarthy persis seperti yang dijelaskan pada 1960 kertas: Artinya, fungsi QUOTE
, ATOM
, EQ
, CAR
, CDR
, CONS
, COND
, LAMBDA
, dan LABEL
semua harus fungsional. Makalah ini akan didahulukan dari teks tantangan ini ketika mempertimbangkan kebenaran jawaban, tetapi saya telah mencoba merangkum sembilan fungsi di bawah ini. Perhatikan bahwa bahasa akan dalam SEMUA CAPS dan tidak perlu pengecekan kesalahan, semua input harus dianggap valid.
Jenis
- Hanya ada dua jenis dalam LISP McCarthy: Sebuah atom, dan daftar terkait, yang secara rekursif didefinisikan sebagai kepala, yang mungkin daftar atau atom, dan daftar yang ditempelkan oleh kepala (ekor).
NIL
memiliki sifat khusus sebagai atom dan daftar. - Sesuai dengan makalahnya, nama atom hanya akan terdiri dari huruf kapital, angka, dan karakter spasi, meskipun string spasi berturut-turut harus dianggap hanya satu ruang dan semua karakter spasi awal dan akhir harus dihapus. Misalnya nama atom setara (ganti garis bawah dengan karakter spasi):
___ATOM__1__ = ATOM_1
. Contoh bukan nama atom yang setara:A_TOM_1 != ATOM_1
- Daftar dinotasikan dengan tanda kurung, dan tersirat
NIL
di akhir setiap daftar. Elemen dalam daftar dipisahkan oleh koma dan bukan spasi putih seperti di sebagian besar Lisps modern. Jadi daftarnya(ATOM 1, (ATOM 2))
akan menjadi{[ATOM 1] -> {[ATOM 2] -> NIL} -> NIL}
.
QUOTE
:
- Mengambil satu argumen yang bisa berupa atom (elemen tunggal) atau daftar tertaut. Mengembalikan argumen dengan tepat.
- Kasus uji:
(QUOTE, ATOM 1) -> ATOM 1
(QUOTE, (ATOM 1, ATOM 2)) -> (ATOM 1, ATOM 2)
ATOM
:
- Mengambil satu argumen yang bisa berupa atom (elemen tunggal) atau daftar tertaut. Mengembalikan
T
(benar) jika argumennya adalah atom, atauNIL
(salah) jika argumennya bukan atom. - Kasus uji:
(ATOM, (QUOTE, ATOM 1)) -> T
(ATOM, (QUOTE, (ATOM 1, ATOM 2))) -> NIL
EQ
:
- Membawa dua argumen yang harus berupa atom (perilaku tidak ditentukan jika salah satu argumen bukan atom). Mengembalikan
T
(benar) jika kedua atom itu setara, atauNIL
(salah) jika tidak. - Kasus uji:
(EQ, (QUOTE, ATOM 1), (QUOTE, ATOM 1)) -> T
(EQ, (QUOTE, ATOM 1), (QUOTE, ATOM 2)) -> NIL
CAR
:
- Mengambil satu argumen yang harus berupa daftar (perilaku tidak terdefinisi jika bukan daftar). Mengembalikan atom (kepala) pertama dari daftar itu.
- Kasus uji:
(CAR, (QUOTE, (ATOM 1, ATOM 2))) -> ATOM 1
CDR
:
- Mengambil satu argumen yang harus berupa daftar (perilaku tidak terdefinisi jika bukan daftar). Mengembalikan setiap atom kecuali atom pertama dari daftar, yaitu ekor. Perhatikan bahwa setiap daftar berakhir dengan tersirat
NIL
, jadi menjalankanCDR
daftar yang tampaknya hanya memiliki satu elemen akan kembaliNIL
. - Kasus uji:
(CDR, (QUOTE, (ATOM 1, ATOM 2))) -> (ATOM 2)
(CDR, (QUOTE, (ATOM 1))) -> NIL
CONS
:
- Membawa dua argumen. Yang pertama mungkin atom atau daftar tetapi yang kedua harus daftar atau
NIL
. Tambahkan argumen pertama ke argumen kedua dan kembalikan daftar yang baru dibuat. - Kasus uji:
(CONS, (QUOTE, ATOM 1), (QUOTE, NIL)) -> (ATOM 1)
(CONS, (QUOTE, ATOM 1), (CONS, (QUOTE, ATOM 2), (QUOTE, NIL))) -> (ATOM 1, ATOM 2)
COND
:
- Ini adalah semacam pernyataan "jika-lain" LISP. Membawa jumlah variabel-panjang argumen, yang masing-masing harus daftar panjang tepat 2. Untuk setiap daftar argumen secara berurutan, mengevaluasi istilah pertama dan jika itu benar (T), kembalikan istilah kedua yang terkait dan keluar dari fungsi . Jika istilah pertama tidak benar, lanjutkan ke argumen berikutnya dan uji kondisinya, dan seterusnya hingga kondisi benar pertama tercapai. Setidaknya salah satu syarat argumen dapat dianggap benar - jika semuanya salah, ini adalah perilaku yang tidak terdefinisi. Lihat halaman 4 untuk contoh yang baik tentang perilaku fungsi ini.
- Kasus uji:
(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
:
- Menentukan fungsi anonim. Membawa dua argumen, yang pertama adalah daftar atom yang mewakili argumen untuk fungsi dan yang kedua adalah setiap ekspresi-S (fungsi tubuh), yang biasanya akan menggunakan argumen.
- Kasus uji:
- Menentukan dan menggunakan fungsi "isNull" anonim:
((LAMBDA, (ATOM 1), (EQ, ATOM 1, (QUOTE, NIL))), (QUOTE, NIL)) -> T
((LAMBDA, (ATOM 1), (EQ, ATOM 1, (QUOTE, NIL))), (QUOTE, ATOM 1)) -> NIL
LABEL
:
- Memberikan nama ke
LAMBDA
fungsi anonim , yang juga memungkinkan fungsi itu disebut secara rekursif di tubuhLAMBDA
. Membawa dua argumen, yang pertama menjadi label dan yang kedua adalahLAMBDA
fungsi yang harus diikat label. Mengembalikan nama yang diberikan. Cakupan semuaLABEL
nama adalah global, dan mendefinisikan ulangLABEL
perilaku tidak terdefinisi. - Kenyataan yang menyenangkan,
LABEL
sebenarnya tidak diperlukan untuk membuat fungsi rekursif seperti yang kita ketahui sekarangLAMBDA
dapat digunakan dengan 'Y-Combinator' untuk menyelesaikan tugas ini, tetapi McCarthy tidak mengetahui metode ini ketika menulis makalah aslinya. Itu membuat program lebih mudah untuk menulis. - Kasus uji:
(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
- (setelah menjalankan hal di atas)
(SUBST, (QUOTE, A), (QUOTE, B), (QUOTE, (A, B, C))) -> (A, A, C)
Untuk membantu memvisualisasikan SUBST
fungsi di atas, bisa direpresentasikan sebagai pseudocode seperti-Python ini:
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:])
KASUS UJI AKHIR:
Jika saya telah menyalinnya dengan benar, penerjemah Anda harus dapat mengartikannya EVAL
dengan kode ini:
(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))))))
Setelah menjalankan raksasa itu, baris ini akan kembali (A, B, C)
:
(EVAL, (QUOTE, (CONS, X, (QUOTE, (B, C)))), (QUOTE, ((X, A), (Y, B))))
Namun, mengutip John McCarthy sendiri di halaman 16, sepertinya dia kehabisan karakter di komputernya:
Jika lebih banyak karakter tersedia di komputer, itu dapat ditingkatkan ...
Oleh karena itu, tantangan ini ditandai dengan kode-golf dan jawaban terpendek dalam karakter akan menjadi pemenang. Celah standar berlaku. Semoga berhasil!
Catatan tentang String Evals : Saya mengerti bahwa beberapa berpikir mungkin untuk memenangkan tantangan ini dengan menggunakan Lisp dan memodifikasi sintaks agar sesuai dengan bahasa host dan kemudian menggunakan string (eval)
. Saya tidak terlalu yakin bahwa pendekatan ini akan selalu menang terutama dengan aturan penamaan pengidentifikasi, dan bahkan jika saya pikir larangan string eval
dalam semua bahasa akan menjadi kemiringan subyektif dan licin. Tetapi saya tidak ingin menghukum orang karena melakukan tantangan ini dengan cara yang 'benar', jadi saya dapat mengizinkan dua pemenang untuk tantangan ini, satu dalam bahasa mirip Lisp dan satu dalam bahasa non-Lispy, jika ini menjadi masalah .
sumber
((LAMBDA, (ATOM 1), (EQ, ATOM 1, (QUOTE, NIL))), (QUOTE, NIL)) -> NIL
Di mana(QUOTE NIL)
pada akhirnya adalah input, jadi ini harus kembaliT
?-> NIL
CONS
Anda, ucapkan "Tambahkan argumen pertama ke argumen kedua dan kembalikan daftar yang baru dibuat," tetapi kasus uji menunjukkan argumen kedua ditambahkan ke argumen pertama. Yang mana yang benar?Jawaban:
Python 3, 770 byte
Ini adalah REPL di stdin / stdout. Diharapkan setiap baris menjadi pernyataan lengkap atau kosong.
eval
digunakan untuk mempersingkat implementasi, tetapi sebaliknya tidak diperlukan untuk logika.sumber
SUBST
contohnya masih rusak (setahu saya) sebagai testcase. Salah satu dariCOND
mencapai akhir sebelum menemukan aT
.EVAL
(sangat terkejut saya mendapatkannya tepat pada percobaan pertama!) Saya akan memberi Anda hadiah dan jawaban yang diterima sekarang!R(E(P(l)
pengaturan ;-)repr
, E =eval
, P =parse
, l =line
.