it-swarm-vi.com

Lược đồ so với LISP chung: Những đặc điểm nào tạo nên sự khác biệt trong dự án của bạn?

Không thiếu những câu hỏi "Scheme vs Common LISP" mơ hồ trên cả StackOverflow và trên trang web này, vì vậy tôi muốn làm cho câu hỏi này tập trung hơn. Câu hỏi dành cho những người đã mã hóa bằng cả hai ngôn ngữ:

Trong khi mã hóa trong Lược đồ, bạn đã bỏ lỡ những yếu tố cụ thể nào trong trải nghiệm mã hóa LISP chung của bạn? Hoặc, ngược lại, trong khi mã hóa trong Common LISP, bạn đã bỏ lỡ điều gì từ mã hóa trong Scheme?

Tôi không nhất thiết chỉ là các tính năng ngôn ngữ. Sau đây là tất cả những điều hợp lệ để bỏ lỡ, theo như câu hỏi liên quan:

  • Thư viện cụ thể.
  • Các tính năng cụ thể của các môi trường phát triển như SLIME, DrRacket, v.v.
  • Các tính năng của việc triển khai cụ thể, như khả năng của Gambit để viết các khối mã C trực tiếp vào nguồn Đề án của bạn.
  • Và tất nhiên, tính năng ngôn ngữ.

Ví dụ về loại câu trả lời tôi hy vọng:

  • "Tôi đã cố gắng thực hiện X trong Common LISP, và nếu tôi có các phần tiếp theo hạng nhất của Scheme, tôi hoàn toàn sẽ làm Y, nhưng thay vào đó tôi phải làm Z, điều đó còn đau hơn."
  • "Viết kịch bản quá trình xây dựng trong dự án Scheme của tôi ngày càng đau đớn khi cây nguồn của tôi phát triển và tôi đã liên kết với ngày càng nhiều thư viện C. Đối với dự án tiếp theo của tôi, tôi đã quay trở lại Common LISP."
  • "Tôi có một cơ sở mã C++ lớn hiện có và đối với tôi, việc có thể nhúng các cuộc gọi C++ trực tiếp vào mã Đề án Gambit của tôi hoàn toàn xứng đáng với bất kỳ thiếu sót nào mà Scheme có thể có so với LISP thông thường, thậm chí bao gồm thiếu hỗ trợ SWIG."

Vì vậy, tôi hy vọng vào những câu chuyện chiến tranh, thay vì những tình cảm chung chung như "Lược đồ là một ngôn ngữ đơn giản hơn", v.v.

159
SuperElectric

Bằng đại học của tôi là về Khoa học nhận thức và Trí tuệ nhân tạo. Từ đó tôi đã có một giới thiệu một khóa học về LISP. Tôi nghĩ rằng ngôn ngữ này rất thú vị (như "thanh lịch") nhưng thực sự không nghĩ nhiều về nó cho đến khi tôi bắt gặp Quy tắc thứ mười của Greensasta sau đó:

Bất kỳ chương trình C hoặc Fortran nào đủ phức tạp đều chứa một chương trình đặc biệt, được chỉ định không chính thức, có lỗi, triển khai chậm một nửa LISP chung.

Quan điểm của Greensasta là (một phần) rằng nhiều chương trình phức tạp có trình thông dịch tích hợp. Thay vì xây dựng một trình thông dịch thành một ngôn ngữ, ông cho rằng việc sử dụng một ngôn ngữ như LISP đã có sẵn một trình thông dịch (hoặc trình biên dịch) có ý nghĩa hơn.

Lúc đó tôi đang làm việc trên một ứng dụng khá lớn thực hiện các tính toán do người dùng xác định bằng trình thông dịch tùy chỉnh cho ngôn ngữ tùy chỉnh. Tôi quyết định thử viết lại cốt lõi của nó trong LISP như một thử nghiệm quy mô lớn.

Mất khoảng sáu tuần. Mã ban đầu là ~ 100.000 dòng Delphi (một biến thể Pascal). Trong LISP đã giảm xuống ~ 10.000 dòng. Tuy nhiên, điều đáng ngạc nhiên hơn nữa là thực tế là động cơ LISP nhanh hơn 3-6 lần. Và hãy nhớ rằng đây là công việc của một tân sinh viên LISP! Đó là toàn bộ trải nghiệm đối với tôi; lần đầu tiên tôi thấy khả năng kết hợp hiệu suất và biểu cảm trong một ngôn ngữ duy nhất.

Một thời gian sau khi tôi bắt đầu làm việc với một dự án dựa trên web, tôi đã thử giọng một số ngôn ngữ. Tôi đã bao gồm LISP và Scheme trong hỗn hợp. Cuối cùng, tôi đã chọn triển khai Scheme -- Chez Scheme . Tôi đã rất hài lòng với kết quả.

Dự án dựa trên web là một hiệu suất cao "công cụ lựa chọn" . Chúng tôi sử dụng Lược đồ theo một số cách khác nhau, từ xử lý dữ liệu đến truy vấn dữ liệu để tạo trang. Ở nhiều điểm, chúng tôi thực sự bắt đầu với một ngôn ngữ khác nhưng cuối cùng lại chuyển sang Scheme vì những lý do tôi sẽ mô tả ngắn gọn dưới đây.

Bây giờ tôi có thể trả lời câu hỏi của bạn (ít nhất là một phần).

Trong buổi thử giọng, chúng tôi đã xem xét một loạt các triển khai LISP và Scheme. Về phía LISP, chúng tôi đã xem xét (tôi tin) Allegro CL, CMUCL, SBCL và LispWorks. Về phía Scheme, chúng tôi đã xem xét (tôi tin) Bigloo, Chicken, Chez, Gambit. (Việc lựa chọn ngôn ngữ đã có từ lâu; đó là lý do tại sao tôi hơi mơ hồ. Tôi có thể đào một số ghi chú nếu nó quan trọng.)

Ngay lập tức chúng tôi đang tìm kiếm một) chủ đề gốc và b) Hỗ trợ Linux, Mac và Windows. Hai điều kiện đó kết hợp đã đánh gục tất cả mọi người nhưng (tôi nghĩ) Allegro và Chez - vì vậy để tiếp tục đánh giá, chúng tôi phải nới lỏng yêu cầu đa luồng.

Chúng tôi tập hợp một bộ các chương trình nhỏ và sử dụng chúng để đánh giá và thử nghiệm. Điều đó tiết lộ một số vấn đề. Ví dụ: một số triển khai có các khiếm khuyết ngăn một số thử nghiệm chạy đến hoàn thành; một số triển khai không thể biên dịch mã vào thời gian chạy; một số triển khai không thể dễ dàng tích hợp mã được biên dịch theo thời gian chạy với mã được biên dịch trước; một số triển khai có người thu gom rác rõ ràng tốt hơn (hoặc rõ ràng tệ hơn) so với những người khác; Vân vân.

Đối với nhu cầu của chúng tôi, chỉ có ba triển khai thương mại - Allegro, Chez và Lispworks - đã vượt qua các thử nghiệm chính của chúng tôi. Trong ba người duy nhất, Chez đã vượt qua tất cả các bài kiểm tra với màu sắc bay. Vào thời điểm đó, tôi nghĩ Lispworks không có chủ đề gốc trên bất kỳ nền tảng nào (tôi nghĩ họ làm bây giờ) và tôi nghĩ Allegro chỉ có chủ đề gốc trên một số nền tảng. Hơn nữa, Allegro có phí cấp phép thời gian "gọi cho chúng tôi" mà tôi không thích lắm. Tôi tin rằng Lispworks không có phí thời gian chạy và Chez có sự sắp xếp đơn giản (và rất hợp lý) (và nó chỉ khởi động nếu bạn sử dụng trình biên dịch vào thời gian chạy).

Đã tạo ra một số đoạn mã đáng kể trong cả LISP và Scheme, đây là một số điểm so sánh và tương phản:

  • Các môi trường LISP trưởng thành hơn nhiều. Bạn nhận được rất nhiều bang cho buck. (Đã nói rằng, nhiều mã hơn cũng tương đương với nhiều lỗi hơn.)

  • Các môi trường LISP khó học hơn nhiều. Bạn cần nhiều thời gian hơn để thành thạo; LISP thông thường là một ngôn ngữ lớn - và đó là trước khi bạn đến các thư viện mà các triển khai thương mại thêm vào đầu ngôn ngữ đó. (Đã nói rằng, trường hợp cú pháp của Scheme tinh tế và phức tạp hơn nhiều so với bất kỳ điều gì trong LISP.)

  • Các môi trường LISP có thể khó tạo ra nhị phân hơn một chút. Bạn cần "lắc" hình ảnh của mình để loại bỏ các bit không cần thiết và nếu bạn không thực hiện chương trình của mình một cách chính xác trong quá trình đó, bạn có thể gặp phải lỗi thời gian chạy sau này . Ngược lại, với Chez, chúng tôi biên dịch một tệp cấp cao nhất bao gồm tất cả các tệp khác mà nó cần và chúng tôi đã hoàn tất.

Tôi đã nói trước đó rằng chúng tôi đã kết thúc việc sử dụng Scheme ở một số nơi mà ban đầu chúng tôi không có ý định. Tại sao? Tôi có thể nghĩ ra ba lý do ngoài đỉnh đầu.

Đầu tiên, chúng tôi đã học cách tin tưởng vào Chez (và nhà phát triển của nó, Cadence). Chúng tôi đã hỏi rất nhiều từ công cụ và nó luôn được chuyển giao. Ví dụ, trong lịch sử, Chez có một số lượng nhỏ các khiếm khuyết nhỏ và trình quản lý bộ nhớ của nó rất, rất tốt.

Thứ hai, chúng tôi đã học cách yêu thích màn trình diễn mà chúng tôi có được từ Chez. Chúng tôi đã sử dụng thứ gì đó giống như ngôn ngữ kịch bản - và chúng tôi đã nhận được tốc độ mã gốc từ nó. Đối với một số điều không quan trọng - nhưng nó không bao giờ bị tổn thương, và đôi khi nó giúp ích rất nhiều.

Thứ ba, chúng tôi đã học cách yêu thích Đề án trừu tượng có thể cung cấp. Nhân tiện, tôi không chỉ có nghĩa là macro; Ý tôi là những thứ như đóng cửa, lambdas, gọi đuôi, v.v ... Một khi bạn bắt đầu nghĩ theo những thuật ngữ đó, các ngôn ngữ khác có vẻ khá hạn chế khi so sánh.

Đề án có hoàn hảo không? Không; đó là một sự đánh đổi. Đầu tiên, nó cho phép các nhà phát triển riêng lẻ hoạt động hiệu quả hơn - nhưng các nhà phát triển khó khăn hơn trong việc tìm kiếm mã của nhau vì các biển chỉ dẫn mà hầu hết các ngôn ngữ có (ví dụ: đối với các vòng lặp) bị thiếu trong Đề án (ví dụ: có hàng triệu cách để làm một vòng lặp for). Thứ hai, có một nhóm các nhà phát triển nhỏ hơn nhiều để nói chuyện, thuê, mượn, v.v.

Tóm lại, tôi nghĩ tôi muốn nói: LISP và Scheme cung cấp một số khả năng không có sẵn ở bất kỳ nơi nào khác. Khả năng đó là một sự đánh đổi, vì vậy tốt hơn hết là nên có ý nghĩa trong trường hợp cụ thể của bạn. Trong trường hợp của chúng tôi, các yếu tố quyết định giữa việc đi với LISP hay Scheme có liên quan nhiều hơn đến các tính năng rất cơ bản (hỗ trợ nền tảng, luồng nền tảng, biên dịch thời gian chạy, cấp phép thời gian chạy) so với các tính năng ngôn ngữ hoặc thư viện. Một lần nữa, trong trường hợp của chúng tôi cũng là một sự đánh đổi: với Chez chúng tôi có các tính năng cốt lõi mà chúng tôi muốn nhưng chúng tôi đã mất các thư viện rộng lớn mà môi trường LISP thương mại có.

Ngoài ra, chỉ để nhắc lại: chúng tôi đã xem xét các Lisps và Đề án khác nhau từ lâu; tất cả họ đã phát triển và cải thiện kể từ đó.

102
Michael Lenaghan

Tôi thường không thích dán một liên kết như một câu trả lời, nhưng tôi đã viết một bài viết trên blog về chính điều này. Nó không đầy đủ, nhưng nó có được một số điểm chính thông qua.

http://symbo1ics.com/blog/?p=729

Chỉnh sửa: Đây là các điểm nguyên tắc:

  1. [~ # ~] sự tồn tại [~ # ~] : Cả hai lisps xuất hiện sau một loạt các lisps khác. Sơ đồ lấy con đường tối thiểu, tiên đề. CL đi theo con đường baroque.
  2. [~ # ~] case [~ # ~] : Điển hình là lược đồ phân biệt chữ hoa chữ thường. CL không phải (mặc dù nó có thể). Điều này đôi khi bị bỏ lỡ, nhưng tính thực tế của nó được tranh luận (bởi tôi).
  3. [~ # ~] tên [~ # ~] : Tên của các ký hiệu trong CL rất nhiều lần lẻ và khó hiểu. TERPRI, PROGN, v.v ... Lược đồ thường có tên rất hợp lý. Đây là một cái gì đó bị bỏ lỡ trong CL.
  4. [~ # ~] các hàm [~ # ~] : CL có một không gian tên hàm riêng biệt. Đây là không bị bỏ lỡ trong Lược đồ. Có một không gian tên duy nhất thường cho phép lập trình chức năng rất sạch sẽ, điều này thường khó hoặc khó xử trong CL. Nhưng nó có chi phí --- đôi khi bạn phải làm xáo trộn các tên như "list" thành "lst" trong Lược đồ.
  5. [~ # ~] macro [~ # ~] : Tôi nhớ các macro bẩn cấp thấp nhất trong Lược đồ. Vâng, syntax-rules Tất cả đều ổn và bảnh bao cho đến khi bạn thực sự muốn hack một số thứ. Mặt khác, các macro vệ sinh đôi khi bị bỏ sót trong CL. Không có cách tiêu chuẩn để làm chúng có nghĩa là phát minh lại bánh xe.
  6. [~ # ~] tính di động [~ # ~] : Thường thì trường hợp CL dễ mang theo hơn, mặc dù cả hai ngôn ngữ đều được chuẩn hóa. CL lớn hơn và do đó có nhiều tính năng tiêu chuẩn hơn để sử dụng mà không cần thư viện bên ngoài. Nó cũng có nghĩa là nhiều thứ phụ thuộc vào việc thực hiện có thể được thực hiện một cách hợp lý. Ngoài ra, Scheme phải chịu đựng hàng nghìn tỷ triển khai, hầu hết trong số đó không tương thích. Điều này làm cho CL rất mong muốn.
  7. [~ # ~] thư viện [~ # ~] : Rất liên quan đến điểm cuối cùng của tôi. Đề án có SRFI nhưng không được thừa nhận toàn cầu. Không có cách di động để làm việc với các thư viện. CL mặt khác có cách. Và Quicklisp là một món quà từ thượng đế (Xach) --- một loại kho thư viện để sử dụng.
  8. [~ # ~] triển khai [~ # ~] : Lược đồ chịu quá nhiều triển khai. Không có thực hiện kinh điển thực sự. Mặt khác, CL có một số triển khai sử dụng cụ thể hoặc hiệu suất rất cao (hiệu suất cao: SBCL, thương mại: Allegro, nhúng: ECL, xách tay: CLISP, Java: ABCL, ...).

Mặc dù tôi chỉ nói chuyện ở người đầu tiên một chút ở trên, nhưng rõ ràng những gì tôi bỏ lỡ và những gì tôi không.

[Tôi xin lỗi nếu những điều này quá chung chung. Có vẻ như bạn có thể muốn nhiều chi tiết cụ thể hơn. Có một số chi tiết cụ thể trong bài viết.]

37
Quadrescence

Gần đây tôi đã bắt đầu một dự án gia đình bằng cách sử dụng thư viện có phiên bản C và Java. Tôi muốn sử dụng LISP cho dự án và tôi đã dành khoảng một tháng để sử dụng LISP, Scheme chung hoặc Clojure. Tôi có một số kinh nghiệm với cả ba, nhưng chỉ là các dự án đồ chơi. Tôi sẽ kể cho bạn nghe một chút về trải nghiệm của tôi với từng người trong số họ trước khi nói cho bạn biết tôi đã chọn cái nào.

Vợt PLT có Nice IDE không chỉ cho phép bạn đánh giá các biểu thức từ trình chỉnh sửa mà còn cho phép bạn nhập dấu ngoặc thay vì parens, chuyển chúng trở lại parens khi thích hợp. Vợt cũng có một bộ lớn của các thư viện với bản cài đặt và thậm chí còn có sẵn để tải xuống. Trình gỡ lỗi trực quan cũng hữu ích.

Việc triển khai LISP chung của tôi (SBCL) không có IDE, nhưng theo thông lệ với các triển khai CL nguồn mở để sử dụng Emacs và SLIME. Sự kết hợp này có thể rất hiệu quả. Cùng với khả năng đánh giá biểu thức khi bạn nhập chúng vào tệp nguồn, còn có một REPL có tất cả các lệnh chỉnh sửa của emacs, do đó, sao chép mã có thể thực hiện hiệu quả cả hai cách. các đối tượng được hiển thị trong bộ đệm REPL có thể được sao chép và dán. Alt+(Alt+) là hiệu quả để xử lý các dấu ngoặc đơn và thụt lề phù hợp.

Tất cả các tính năng Emacs ở trên cũng có sẵn cho Clojure. Trải nghiệm chỉnh sửa của tôi với Clojure tương tự như LISP. Java interop hoạt động tốt và tôi muốn thực hiện một dự án Clojure khi nó đáo hạn.

Tôi đã có thể truy cập thư viện bằng cả ba (LISP chung, Vợt và Clojure), nhưng cuối cùng tôi đã chọn LISP chung cho dự án. Yếu tố quyết định là FFI dễ sử dụng hơn trong LISP chung. CFFI có một hướng dẫn rất tốt với mã ví dụ và giải thích chi tiết về từng phương pháp. Tôi đã có thể bọc 20 chức năng C vào một buổi chiều và từ đó không phải chạm vào mã.

Yếu tố khác là tôi quen thuộc với LISP thông thường hơn với Đề án Clojure hoặc R6RS. Tôi đã đọc hầu hết các cuốn sách LISP và Graham phổ biến thực tế, và tôi cảm thấy thoải mái với Hyperspec. Đây chưa phải là mã "lispy", nhưng tôi chắc chắn điều đó sẽ thay đổi khi tôi có thêm kinh nghiệm.

25
Larry Coleman

Tôi lập trình trong cả CL và Vợt.

Bây giờ tôi đang phát triển một trang web trong Common LISP và tôi đã viết một bộ các chương trình nội bộ cho chủ nhân trước đây của tôi trong Vợt.

Đối với mã nội bộ, tôi đã chọn Vợt (khi đó được gọi là PLT Scheme) vì chủ lao động là một cửa hàng Windows và tôi không thể bắt họ trả tiền cho LispWorks. Việc triển khai CL nguồn mở duy nhất tốt cho Windows là (và vẫn là) CCL, yêu cầu hỗ trợ SSE trong bộ xử lý. Nhà tuyển dụng, giá rẻ, đang sử dụng Ngay cả khi chủ nhân đã có phần cứng tốt, thư viện GUI duy nhất có hậu quả trong Common LISP là McCLIM, chỉ hoạt động trên Unix. Vợt có một thư viện GUI tốt hoạt động trên cả Unix và Windows, rất quan trọng đối với tôi thành công của dự án.

Tôi đã dành hơn một năm để đưa ra trình soạn thảo DrRacket nguyên thủy. EMACS không thể biến phiên bản GUI của Vợt, sau đó được gọi là MrEd, thành LISP kém hơn trên Windows. Tôi đã phải làm mà không thể đánh giá biểu thức tại con trỏ bằng một lần nhấn phím. Thay vào đó, tôi phải chọn thủ công biểu thức S, sao chép nó, nhấp vào cửa sổ REPL (vì không có tổ hợp phím để chuyển sang biểu thức S), sau đó dán biểu thức S. phải làm mà không có trình soạn thảo có thể chỉ cho tôi các đối số dự kiến ​​của hàm hoặc macro tôi đang sử dụng. DrRacket không thể thay thế cho SLIME.

Nhà tuyển dụng đã sử dụng cơ sở dữ liệu độc quyền với API XML phức tạp, yêu cầu vô số thông tin dường như không cần thiết để có thể trả lời phiên bản truy vấn CHỌN của nó. Tôi đã quyết định sử dụng cả HTMLPrag để phát ra XML cho API này và phân tích các câu trả lời. Nó đã làm việc tuyệt vời.

Tôi đã phải học hệ thống macro "trường hợp cú pháp" quá phức tạp của vợt để viết một macro cho phép tôi tương tác với API XML quá phức tạp bằng cách nhập các biểu mẫu trông giống như SQL. Phần này sẽ dễ dàng hơn nhiều nếu tôi có DEFMACRO theo ý của tôi. Tuy nhiên, kết quả cuối cùng vẫn liền mạch mặc dù phải mất nhiều nỗ lực hơn để đạt được.

Hơn nữa, tôi đã phải làm mà không có macro LOOP của LISP chung. Vợt bắt đầu cung cấp một giải pháp thay thế chỉ sau khi tôi đã viết hầu hết mã, và giải pháp thay thế vẫn còn tệ so với LOOP (mặc dù nhóm phát triển của vợt khẳng định nó tốt hơn-- đơn giản là họ đã sai). Cuối cùng tôi đã viết rất nhiều mẫu LET có tên đã sử dụng "xe hơi" và "cdr" để lặp lại các danh sách.

Nói về xe hơi và cdr, không có gì khó chịu hơn việc diễn giải (xe '()) của Scheme là một lỗi. Tôi đã tận dụng độ nhạy trường hợp của vợt và triển khai CAR và CDR, có ngữ nghĩa chung của LISP. Tuy nhiên, việc tách '() và #f làm cho việc trả về' () làm giá trị mặc định trở nên ít hữu ích hơn nhiều.

Cuối cùng tôi cũng đã thực hiện lại UNWIND-PROTECT, và đã phát minh ra hệ thống khởi động lại của riêng tôi để lấp đầy khoảng trống mà Vợt để lại. Cộng đồng vợt cần biết rằng khởi động lại rất hữu ích và dễ thực hiện.

Hình thức cho phép giá trị của vợt quá dài, vì vậy tôi đã triển khai NHIỀU-GIÁ TRỊ-BIND. Điều này là hoàn toàn cần thiết, bởi vì Vợt yêu cầ bạn phải nhận tất cả các giá trị được tạo, cho dù bạn có sử dụng chúng hay không.

Sau đó, tôi đã cố gắng viết một ứng dụng API XML của eBay trong Common LISP, chỉ để thấy rằng nó không có bất cứ thứ gì như HTMLPrag. HTMLPrag rất hữu ích. Tôi đã kết thúc việc thực hiện dự án đó trong vợt. Tôi đã thử nghiệm với các cơ sở Lập trình Văn học của Rquet, chỉ để phát hiện ra rằng tôi là lập trình viên duy nhất trên Trái đất tìm thấy mã chữ viết đúng được viết khó hơn so với mã thông thường hoặc viết mã "nhận xét quá mức" không đúng cách.

Dự án mới của tôi đang được thực hiện trong Common LISP, đó là lựa chọn đúng đắn vì cộng đồng Vợt chỉ không tin vào sự song song, điều cần thiết cho dự án này. Điều duy nhất tôi nghĩ rằng tôi có thể đã bỏ lỡ từ Vợt là sự tiếp tục. Tuy nhiên, tôi đã có thể làm những gì tôi cần bằng cách sử dụng khởi động lại, và, khi nhìn lại, có lẽ có thể đã thực hiện nó với một đóng cửa đơn giản.

21
Racketeer

Đề án được thiết kế với một phần tổng hợp riêng trong tâm trí. Do đó, sức mạnh của các macro của nó thường bị hạn chế nghiêm trọng, ngay cả với các phần mở rộng cho phép defmacro kiểu LISP thông thường thay vì hệ thống macro kém, hạn chế vệ sinh. Không phải lúc nào cũng có thể định nghĩa một macro xác định một macro khác, dự định sử dụng ngay lập tức trong một dòng mã tiếp theo. Và khả năng như vậy là rất cần thiết để thực hiện các trình biên dịch eDSL hiệu quả.

Không cần phải đề cập rằng việc triển khai Đề án chỉ với các macro vệ sinh R5RS hầu như không hữu ích đối với tôi, vì phong cách siêu lập trình của tôi không thể được dịch đầy đủ sang vệ sinh.

May mắn thay, có các triển khai Đề án (ví dụ: Vợt) không có giới hạn đó.

5
SK-logic