McCarthy's LISP 2502
ในต้นปี พ.ศ. 2502 จอห์นแม็คคาร์ธีได้เขียนบทความที่แปลกใหม่ซึ่งกำหนดฟังก์ชั่นดั้งเดิมเพียงเก้าอย่างที่เมื่อรวมเข้าด้วยกันยังคงเป็นพื้นฐานสำหรับภาษา LISP ที่เหมือนกันทุกวันนี้ กระดาษมีดิจิทัลที่นี่:
http://www-formal.stanford.edu/jmc/recursive.pdf
งานของคุณคือการรองรับการใช้ตัวแยกวิเคราะห์และล่าม LISP แมคคาร์ของตรงตามที่อธิบายไว้ในกระดาษ 1960: นั่นคือฟังก์ชั่นQUOTE
, ATOM
, EQ
, CAR
, CDR
, CONS
, COND
, LAMBDA
และLABEL
ทั้งหมดควรจะทำงาน กระดาษจะมีความสำคัญเหนือข้อความท้าทายนี้เมื่อพิจารณาความถูกต้องของคำตอบ แต่ฉันได้พยายามสรุปฟังก์ชั่นทั้งเก้าด้านล่าง โปรดทราบว่าภาษาจะอยู่ใน CAPS ทั้งหมดและไม่จำเป็นต้องตรวจสอบข้อผิดพลาดอินพุตทั้งหมดควรถูกสันนิษฐานว่าใช้งานได้
ประเภท
- มีอยู่สองประเภทใน LISP ของ McCarthy: อะตอมและลิสต์ที่เชื่อมโยงซึ่งนิยามไว้แบบวนซ้ำซึ่งอาจเป็นลิสต์หรืออะตอมและลิสต์ที่แนบกับหัว (หาง)
NIL
มีคุณสมบัติพิเศษของการเป็นทั้งอะตอมและรายการ - ตามชื่อกระดาษชื่ออะตอมจะประกอบด้วยตัวอักษรพิมพ์ใหญ่ตัวเลขและอักขระเว้นวรรคเท่านั้นแม้ว่าสตริงของช่องว่างที่ต่อเนื่องกันควรได้รับการพิจารณาว่าเป็นเพียงหนึ่งช่องว่างและควรลบอักขระช่องว่างด้านหน้าและด้านหลังทั้งหมด ตัวอย่างชื่อเทียบเท่าอะตอม
___ATOM__1__ = ATOM_1
(แทนที่ขีดด้วยอักขระช่องว่าง): ตัวอย่างชื่ออะตอมที่ไม่เทียบเท่า:A_TOM_1 != ATOM_1
- รายการถูกแสดงด้วยวงเล็บและโดยนัย
NIL
อยู่ที่ท้ายรายการทุกรายการ องค์ประกอบในรายการคั่นด้วยเครื่องหมายจุลภาคและไม่ใช่ช่องว่างเหมือนกับใน Lisps ที่ทันสมัยที่สุด ดังนั้นรายการจะเป็น(ATOM 1, (ATOM 2))
{[ATOM 1] -> {[ATOM 2] -> NIL} -> NIL}
QUOTE
:
- รับอาร์กิวเมนต์หนึ่งตัวซึ่งอาจเป็นอะตอม (องค์ประกอบเดียว) หรือรายการที่เชื่อมโยง ส่งคืนอาร์กิวเมนต์อย่างแน่นอน
- กรณีทดสอบ:
(QUOTE, ATOM 1) -> ATOM 1
(QUOTE, (ATOM 1, ATOM 2)) -> (ATOM 1, ATOM 2)
ATOM
:
- รับอาร์กิวเมนต์หนึ่งตัวซึ่งอาจเป็นอะตอม (องค์ประกอบเดียว) หรือรายการที่เชื่อมโยง ส่งคืน
T
(จริง) ถ้าอาร์กิวเมนต์เป็นอะตอมหรือNIL
(เท็จ) หากอาร์กิวเมนต์ไม่ใช่อะตอม - กรณีทดสอบ:
(ATOM, (QUOTE, ATOM 1)) -> T
(ATOM, (QUOTE, (ATOM 1, ATOM 2))) -> NIL
EQ
:
- รับอาร์กิวเมนต์สองตัวที่ต้องเป็นอะตอม (พฤติกรรมไม่ได้กำหนดไว้หากอาร์กิวเมนต์ตัวใดตัวหนึ่งไม่ใช่อะตอม) ส่งคืน
T
(จริง) หากทั้งสองอะตอมมีค่าเท่ากันหรือNIL
(เท็จ) หากไม่เป็นเช่นนั้น - กรณีทดสอบ:
(EQ, (QUOTE, ATOM 1), (QUOTE, ATOM 1)) -> T
(EQ, (QUOTE, ATOM 1), (QUOTE, ATOM 2)) -> NIL
CAR
:
- รับอาร์กิวเมนต์หนึ่งอันซึ่งต้องเป็นรายการ (พฤติกรรมไม่ได้กำหนดหากไม่ใช่รายการ) ส่งคืนอะตอม (หัว) แรกของรายการนั้น
- กรณีทดสอบ:
(CAR, (QUOTE, (ATOM 1, ATOM 2))) -> ATOM 1
CDR
:
- รับอาร์กิวเมนต์หนึ่งอันซึ่งต้องเป็นรายการ (พฤติกรรมไม่ได้กำหนดหากไม่ใช่รายการ) ส่งคืนทุกอะตอมยกเว้นอะตอมแรกของรายการนั่นคือหาง หมายเหตุว่าทุกรายการในปลายนัย
NIL
เพื่อให้การทำงานในรายการที่ปรากฏมีเพียงองค์ประกอบหนึ่งจะกลับมาCDR
NIL
- กรณีทดสอบ:
(CDR, (QUOTE, (ATOM 1, ATOM 2))) -> (ATOM 2)
(CDR, (QUOTE, (ATOM 1))) -> NIL
CONS
:
- รับอาร์กิวเมนต์สองตัว ครั้งแรกอาจจะเป็นอะตอมหรือรายการ
NIL
แต่ที่สองจะต้องมีรายการหรือ ผนวกอาร์กิวเมนต์แรกเข้ากับอาร์กิวเมนต์ที่สองและส่งกลับรายการที่สร้างขึ้นใหม่ - กรณีทดสอบ:
(CONS, (QUOTE, ATOM 1), (QUOTE, NIL)) -> (ATOM 1)
(CONS, (QUOTE, ATOM 1), (CONS, (QUOTE, ATOM 2), (QUOTE, NIL))) -> (ATOM 1, ATOM 2)
COND
:
- นี่คือคำสั่ง "if-else" ของ LISP รับจำนวนอากิวเมนต์ที่มีความยาวผันแปรซึ่งแต่ละรายการต้องเป็นรายการความยาวที่แน่นอน 2 สำหรับแต่ละรายการอาร์กิวเมนต์ตามลำดับประเมินคำแรกและถ้าเป็นจริง (T) ให้ส่งคืนคำที่สองที่เกี่ยวข้องและออกจากฟังก์ชัน . หากคำแรกไม่เป็นจริงให้ย้ายไปยังอาร์กิวเมนต์ถัดไปและทดสอบเงื่อนไขของมันและต่อไปเรื่อย ๆ จนกว่าจะถึงเงื่อนไขจริงแรก อย่างน้อยหนึ่งในเงื่อนไขการโต้แย้งสามารถสันนิษฐานว่าเป็นจริง - หากพวกเขาเป็นเท็จทั้งหมดนี่คือพฤติกรรมที่ไม่ได้กำหนด ดูหน้า 4 สำหรับตัวอย่างที่ดีเกี่ยวกับพฤติกรรมของฟังก์ชันนี้
- กรณีทดสอบ:
(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
:
- กำหนดฟังก์ชั่นที่ไม่ระบุชื่อ รับอาร์กิวเมนต์สองตัวสิ่งแรกคือรายการของอะตอมซึ่งแสดงถึงข้อโต้แย้งของฟังก์ชันและอันดับที่สองคือ S-expression (ตัวฟังก์ชัน) ซึ่งโดยทั่วไปจะใช้อาร์กิวเมนต์
- กรณีทดสอบ:
- การกำหนดและใช้ฟังก์ชัน "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
:
- ให้ชื่อไปยังไม่ระบุชื่อฟังก์ชั่นซึ่งยังช่วยให้ฟังก์ชั่นที่จะถูกเรียกซ้ำในร่างกายของ
LAMBDA
LAMBDA
รับอาร์กิวเมนต์สองตัวสิ่งแรกคือเลเบลและอันดับที่สองเป็นLAMBDA
ฟังก์ชันที่เลเบลควรถูกโยง ส่งคืนชื่อที่ให้มา ขอบเขตของLABEL
ชื่อทั้งหมดเป็นแบบโกลบอลและการนิยามใหม่LABEL
คือพฤติกรรมที่ไม่ได้กำหนด - ความจริงแล้วสนุก
LABEL
ไม่จำเป็นต้องสร้างฟังก์ชั่นแบบเรียกซ้ำเนื่องจากเรารู้ว่าLAMBDA
สามารถใช้กับ'Y-Combinator'เพื่อทำงานนี้ให้สำเร็จ แต่ McCarthy ไม่ทราบวิธีนี้เมื่อเขียนบทความต้นฉบับ มันทำให้โปรแกรมเขียนได้ง่ายขึ้นมาก - กรณีทดสอบ:
(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
- (หลังจากทำงานด้านบน)
(SUBST, (QUOTE, A), (QUOTE, B), (QUOTE, (A, B, C))) -> (A, A, C)
เพื่อช่วยให้เห็นภาพของSUBST
ฟังก์ชั่นด้านบนมันอาจถูกแสดงเป็น pseudocode เหมือน 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:])
กรณีทดสอบสุดท้าย:
หากฉันถอดความอย่างถูกต้องล่ามของคุณควรสามารถตีความEVAL
ด้วยรหัสนี้ได้:
(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))))))
หลังจากเรียกใช้ behemoth แล้วบรรทัดนี้ควรกลับมา(A, B, C)
:
(EVAL, (QUOTE, (CONS, X, (QUOTE, (B, C)))), (QUOTE, ((X, A), (Y, B))))
อย่างไรก็ตามในการอ้างถึง John McCarthy ในหน้า 16 ดูเหมือนว่าตัวละครของเขาจะหมดในคอมพิวเตอร์:
หากมีตัวละครเพิ่มเติมในคอมพิวเตอร์ก็จะสามารถปรับปรุงได้มาก ...
ดังนั้นความท้าทายนี้จึงถูกติดแท็กcode-golfและคำตอบที่สั้นที่สุดในตัวละครจะเป็นผู้ชนะ ช่องโหว่มาตรฐานใช้ โชคดี!
หมายเหตุเกี่ยวกับ String Evals : (eval)
ผมเข้าใจว่าบางคนคิดว่ามันอาจจะเป็นไปได้ที่จะชนะความท้าทายนี้โดยใช้เสียงกระเพื่อมไวยากรณ์และการปรับเปลี่ยนเพื่อให้พอดีกับภาษาโฮสต์แล้วใช้สตริง ฉันไม่มั่นใจเป็นอย่างยิ่งว่าวิธีการนี้จะต้องชนะโดยเฉพาะอย่างยิ่งกับกฎการตั้งชื่อตัวระบุและแม้ว่าฉันจะคิดว่าการห้ามใช้สตริงeval
s ในทุกภาษาจะเป็นความชันส่วนตัวและลื่น แต่ฉันไม่ต้องการที่จะลงโทษผู้คนที่ทำสิ่งนี้อย่างถูกวิธีดังนั้นฉันอาจอนุญาตให้ผู้ชนะสองคนสำหรับความท้าทายนี้หนึ่งในภาษาที่เหมือนเสียงกระเพื่อม .
((LAMBDA, (ATOM 1), (EQ, ATOM 1, (QUOTE, NIL))), (QUOTE, NIL)) -> NIL
ที่ไหน(QUOTE NIL)
ในตอนท้ายคือการป้อนข้อมูลเช่นนี้ควรจะกลับT
?
-> NIL
CONS
คุณคุณจะพูดว่า "ผนวกอาร์กิวเมนต์แรกกับอาร์กิวเมนต์ที่สองและส่งกลับรายการที่สร้างขึ้นใหม่" แต่กรณีทดสอบแสดงอาร์กิวเมนต์ที่สองที่ผนวกเข้ากับอาร์กิวเมนต์แรก อันไหนถูกต้อง?