McCarthy 1959 LISP
Đầu năm 1959, John McCarthy đã viết một bài báo đột phá xác định chỉ chín chức năng nguyên thủy mà khi kết hợp lại với nhau vẫn là cơ sở cho tất cả các ngôn ngữ giống như LISP ngày nay. Bài viết có sẵn được số hóa tại đây:
http://www-formal.stanford.edu/jmc/recursive.pdf
Công việc của bạn là để thực hiện đầy đủ một phân tích cú pháp và thông dịch viên cho LISP McCarthy chính xác như được mô tả trong bài báo năm 1960: Đó là, các chức năng QUOTE
, ATOM
, EQ
, CAR
, CDR
, CONS
, COND
, LAMBDA
, và LABEL
nên tất cả được chức năng. Bài viết sẽ được ưu tiên hơn văn bản thách thức này khi xem xét tính chính xác của câu trả lời, nhưng tôi đã cố gắng tóm tắt chín chức năng dưới đây. Lưu ý rằng ngôn ngữ sẽ ở TẤT CẢ CAPS và không cần kiểm tra lỗi, tất cả các đầu vào nên được coi là hợp lệ.
Các loại
- Chỉ có hai loại trong LISP của McCarthy: Một nguyên tử và một danh sách được liên kết, được định nghĩa đệ quy là một cái đầu, có thể là một danh sách hoặc một nguyên tử, và một danh sách mà đầu được gắn vào (đuôi).
NIL
có tài sản đặc biệt là cả một nguyên tử và một danh sách. - Theo bài báo, tên nguyên tử sẽ chỉ bao gồm chữ in hoa, số và ký tự khoảng trắng, mặc dù các chuỗi không gian liên tiếp nên được coi là chỉ một khoảng trắng và tất cả các ký tự khoảng trắng ở đầu và cuối phải được loại bỏ. Ví dụ tên nguyên tử tương đương (thay thế dấu gạch dưới bằng ký tự khoảng trắng) :
___ATOM__1__ = ATOM_1
. Ví dụ không phải tên nguyên tử tương đương:A_TOM_1 != ATOM_1
- Danh sách được biểu thị bằng dấu ngoặc đơn và ngụ ý
NIL
ở cuối mỗi danh sách. Các thành phần trong danh sách được phân tách bằng dấu phẩy và không phải khoảng trắng như trong hầu hết các Lisps hiện đại. Vì vậy, danh sách(ATOM 1, (ATOM 2))
sẽ được{[ATOM 1] -> {[ATOM 2] -> NIL} -> NIL}
.
QUOTE
:
- Đưa ra một đối số có thể là nguyên tử (phần tử đơn) hoặc danh sách được liên kết. Trả về chính xác đối số.
- Các trường hợp thử nghiệm:
(QUOTE, ATOM 1) -> ATOM 1
(QUOTE, (ATOM 1, ATOM 2)) -> (ATOM 1, ATOM 2)
ATOM
:
- Đưa ra một đối số có thể là nguyên tử (phần tử đơn) hoặc danh sách được liên kết. Trả về
T
(true) nếu đối số là nguyên tử hoặcNIL
(sai) nếu đối số không phải là nguyên tử. - Các trường hợp thử nghiệm:
(ATOM, (QUOTE, ATOM 1)) -> T
(ATOM, (QUOTE, (ATOM 1, ATOM 2))) -> NIL
EQ
:
- Có hai đối số phải là nguyên tử (hành vi không xác định nếu một trong hai đối số không phải là nguyên tử). Trả về
T
(đúng) nếu hai nguyên tử tương đương hoặcNIL
(sai) nếu không. - Các trường hợp thử nghiệm:
(EQ, (QUOTE, ATOM 1), (QUOTE, ATOM 1)) -> T
(EQ, (QUOTE, ATOM 1), (QUOTE, ATOM 2)) -> NIL
CAR
:
- Đưa ra một đối số phải là một danh sách (hành vi không được xác định nếu nó không phải là danh sách). Trả về nguyên tử đầu tiên (đầu) của danh sách đó.
- Các trường hợp thử nghiệm:
(CAR, (QUOTE, (ATOM 1, ATOM 2))) -> ATOM 1
CDR
:
- Đưa ra một đối số phải là một danh sách (hành vi không được xác định nếu nó không phải là danh sách). Trả về mọi nguyên tử trừ nguyên tử đầu tiên của danh sách, tức là đuôi. Lưu ý rằng mọi danh sách đều kết thúc trong một hàm ý
NIL
, do đó, việc chạyCDR
trên một danh sách dường như chỉ có một phần tử sẽ trả vềNIL
. - Các trường hợp thử nghiệm:
(CDR, (QUOTE, (ATOM 1, ATOM 2))) -> (ATOM 2)
(CDR, (QUOTE, (ATOM 1))) -> NIL
CONS
:
- Đưa ra hai lập luận. Đầu tiên có thể là một nguyên tử hoặc một danh sách nhưng thứ hai phải là một danh sách hoặc
NIL
. Chuẩn bị đối số thứ nhất cho đối số thứ hai và trả về danh sách mới được tạo. - Các trường hợp thử nghiệm:
(CONS, (QUOTE, ATOM 1), (QUOTE, NIL)) -> (ATOM 1)
(CONS, (QUOTE, ATOM 1), (CONS, (QUOTE, ATOM 2), (QUOTE, NIL))) -> (ATOM 1, ATOM 2)
COND
:
- Đây là tuyên bố sắp xếp "nếu khác" của LISP. Lấy số lượng đối số có độ dài thay đổi, mỗi đối số phải là danh sách có độ dài chính xác 2. Đối với mỗi danh sách đối số theo thứ tự, hãy đánh giá thuật ngữ đầu tiên và nếu nó đúng (T), hãy trả về thuật ngữ thứ hai được liên kết và thoát khỏi hàm . Nếu thuật ngữ đầu tiên không đúng, hãy chuyển sang đối số tiếp theo và kiểm tra điều kiện của nó, và cứ thế cho đến khi đạt được điều kiện đúng đầu tiên. Ít nhất một trong các điều kiện đối số có thể được coi là đúng - nếu tất cả chúng đều sai, đây là hành vi không xác định. Xem trang 4 để biết ví dụ hay về hành vi của chức năng này.
- Các trường hợp thử nghiệm:
(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
:
- Xác định một hàm ẩn danh. Có hai đối số, đầu tiên là danh sách các nguyên tử đại diện cho các đối số cho hàm và thứ hai là bất kỳ biểu thức S nào (thân hàm), thường sử dụng các đối số.
- Các trường hợp thử nghiệm:
- Xác định và sử dụng hàm "isNull" ẩn danh:
((LAMBDA, (ATOM 1), (EQ, ATOM 1, (QUOTE, NIL))), (QUOTE, NIL)) -> T
((LAMBDA, (ATOM 1), (EQ, ATOM 1, (QUOTE, NIL))), (QUOTE, ATOM 1)) -> NIL
LABEL
:
- Đặt tên cho một
LAMBDA
hàm ẩn danh , cũng cho phép hàm đó được gọi đệ quy trong phần thân củaLAMBDA
. Có hai đối số, đầu tiên là nhãn và thứ hai làLAMBDA
hàm mà nhãn phải được ràng buộc. Trả về tên được cung cấp. Phạm vi của tất cả cácLABEL
tên là toàn cầu và xác định lại aLABEL
là hành vi không xác định. - Thực tế thú vị,
LABEL
thực sự không cần thiết để tạo các hàm đệ quy vì hiện tại chúng ta biếtLAMBDA
có thể được sử dụng với 'Bộ kết hợp Y' để thực hiện nhiệm vụ này, nhưng McCarthy không biết về phương pháp này khi viết bài báo gốc. Nó làm cho các chương trình dễ dàng hơn nhiều để viết anyways. - Các trường hợp thử nghiệm:
(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
- (sau khi chạy ở trên)
(SUBST, (QUOTE, A), (QUOTE, B), (QUOTE, (A, B, C))) -> (A, A, C)
Để giúp trực quan hóa SUBST
chức năng ở trên, nó có thể được biểu diễn dưới dạng mã giả giống như Python này:
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:])
TRƯỜNG HỢP KIỂM TRA CUỐI CÙNG:
Nếu tôi đã phiên âm nó một cách chính xác, trình thông dịch của bạn sẽ có thể diễn giải EVAL
với mã này:
(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))))))
Sau khi chạy chế độ đó, dòng này sẽ trở lại (A, B, C)
:
(EVAL, (QUOTE, (CONS, X, (QUOTE, (B, C)))), (QUOTE, ((X, A), (Y, B))))
Tuy nhiên, để trích dẫn chính John McCarthy ở trang 16, có vẻ như anh ta đã hết nhân vật trên máy tính của mình:
Nếu có nhiều ký tự hơn trên máy tính, nó có thể được cải thiện đáng kể ...
Do đó, thử thách này được gắn thẻ mã golf và câu trả lời ngắn nhất trong các nhân vật sẽ là người chiến thắng. Tiêu chuẩn áp dụng. Chúc may mắn!
Lưu ý về Chuỗi bằng chứng : Tôi hiểu rằng một số người nghĩ rằng có thể chiến thắng thử thách này bằng cách sử dụng Lisp và sửa đổi cú pháp để phù hợp với ngôn ngữ máy chủ và sau đó sử dụng chuỗi (eval)
. Tôi không đặc biệt tin rằng cách tiếp cận này nhất thiết sẽ giành chiến thắng đặc biệt là với các quy tắc đặt tên định danh và ngay cả khi tôi nghĩ rằng việc cấm chuỗi eval
s trong tất cả các ngôn ngữ sẽ là một độ dốc chủ quan và trơn trượt. Nhưng tôi không muốn trừng phạt mọi người vì đã thực hiện thử thách này theo cách 'đúng', vì vậy tôi có thể cho phép hai người chiến thắng cho thử thách này, một bằng ngôn ngữ giống Lisp và một bằng ngôn ngữ không phải là Lispy, nếu điều này trở thành vấn đề .
((LAMBDA, (ATOM 1), (EQ, ATOM 1, (QUOTE, NIL))), (QUOTE, NIL)) -> NIL
Trường hợp (QUOTE NIL)
ở cuối là đầu vào, vì vậy điều này sẽ trở lại T
?
-> NIL
CONS
bạn, bạn nói "Nối đối số thứ nhất vào đối số thứ hai và trả về danh sách mới được tạo", nhưng các trường hợp thử nghiệm cho thấy đối số thứ hai được thêm vào đối số thứ nhất. Cái nào đúng?