C # Dev - Tôi đã thử Lisps, nhưng tôi không nhận được [đã đóng]


37

Sau vài tháng tìm hiểu và chơi với Lisp, cả CL và một chút Clojure, tôi vẫn không thấy một lý do thuyết phục nào để viết bất cứ điều gì trong đó thay vì C #.

Tôi thực sự muốn một số lý do thuyết phục, hoặc để ai đó chỉ ra rằng tôi đang thiếu một cái gì đó thực sự lớn .

Những điểm mạnh của Lisp (theo nghiên cứu của tôi):

  • Nhỏ gọn, ký hiệu biểu cảm - Hơn cả C #, vâng ... nhưng tôi dường như cũng có thể diễn đạt những ý tưởng đó trong C #.
  • Hỗ trợ ngầm cho lập trình chức năng - C # với các phương thức mở rộng LINQ:
    • mapcar = .Chọn (lambda)
    • mapcan = .Select (lambda) .Aggregate ((a, b) => a.Union (b))
    • xe / đầu tiên = .Đầu tiên ()
    • cdr / rest = .Skip (1) .... vv
  • Lambda và hỗ trợ chức năng bậc cao hơn - C # có điều này và cú pháp được cho là đơn giản hơn:
    • "(lambda (x) (cơ thể))" so với "x => (cơ thể)"
    • "# (" Với "%", "% 1", "% 2" rất hay trong Clojure
  • Công văn phương thức được tách ra khỏi các đối tượng - C # có điều này thông qua các phương thức mở rộng
  • Công văn đa phương thức - C # không có bản gốc này, nhưng tôi có thể thực hiện nó như một cuộc gọi chức năng trong vài giờ
  • Mã là Dữ liệu (và Macro) - Có thể tôi chưa "nhận" macro, nhưng tôi chưa thấy một ví dụ duy nhất trong đó ý tưởng về macro không thể được thực hiện như một hàm; nó không thay đổi "ngôn ngữ", nhưng tôi không chắc đó là một thế mạnh
  • DSL - Chỉ có thể thực hiện thông qua thành phần chức năng ... nhưng nó hoạt động
  • Lập trình "khám phá" chưa được khám phá - cho các cấu trúc / lớp, tính năng tự động và "đối tượng" của C # hoạt động khá tốt và bạn có thể dễ dàng leo thang thành kiểu gõ mạnh hơn khi bạn đi cùng
  • Chạy trên phần cứng không phải Windows - Vâng, vậy? Ngoài đại học, tôi chỉ biết một người không chạy Windows ở nhà hoặc ít nhất là VM của Windows trên * nix / Mac. (Sau đó, một lần nữa, có lẽ điều này quan trọng hơn tôi nghĩ và tôi vừa bị tẩy não ...)
  • REPL cho thiết kế từ dưới lên - Ok, tôi thừa nhận điều này thực sự rất hay và tôi nhớ nó trong C #.

Những điều tôi đang thiếu ở Lisp (do sự kết hợp của C #, .NET, Visual Studio, Resharper):

  • Không gian tên. Ngay cả với các phương thức tĩnh, tôi thích buộc chúng vào một "lớp" để phân loại bối cảnh của chúng (Clojure dường như có điều này, CL dường như không.)
  • Biên dịch tuyệt vời và hỗ trợ thời gian thiết kế
    • hệ thống loại cho phép tôi xác định "tính chính xác" của các cơ sở dữ liệu mà tôi đi qua
    • bất cứ điều gì sai chính tả được gạch chân thời gian thực; Tôi không phải đợi cho đến khi chạy
    • cải tiến mã (chẳng hạn như sử dụng cách tiếp cận FP thay vì bắt buộc) được tự động đề xuất
  • Các công cụ phát triển GUI: WinForms và WPF (Tôi biết Clojure có quyền truy cập vào các thư viện GUI của Java, nhưng chúng hoàn toàn xa lạ với tôi.)
  • Các công cụ gỡ lỗi GUI: breakpoint, step-in, step-over, value tests (text, xml, custom), watch, debug-by-thread, breakpoint có điều kiện, cửa sổ ngăn xếp cuộc gọi với khả năng nhảy tới mã ở mọi cấp độ trong ngăn xếp
    • (Công bằng mà nói, phần còn lại của tôi với Emacs + Slime dường như cung cấp một số điều này, nhưng tôi là một phần của cách tiếp cận dựa trên VS GUI)

Tôi thực sự thích sự cường điệu xung quanh Lisp và tôi đã cho nó một cơ hội.

Nhưng có điều gì tôi có thể làm ở Lisp mà tôi không thể làm tốt trong C # không? Nó có thể dài dòng hơn một chút trong C #, nhưng tôi cũng đã tự động hoàn tất.

Tôi đang thiếu gì? Tại sao tôi nên sử dụng Clojure / CL?


4
Vì bạn đã lập trình theo kiểu chức năng và có vẻ như bạn phụ thuộc khá nhiều vào cơ sở hạ tầng Windows, tôi không thể thấy bất kỳ lý do thuyết phục nào để bạn thay đổi từ C #. Hầu hết mọi người tôi biết đều sử dụng Mac hoặc Linux, vì vậy rõ ràng phụ thuộc rất nhiều vào đám đông bạn chạy cùng (Tôi đã sử dụng mọi phiên bản Windows kể từ 3.1 nhưng tôi vẫn thích làm việc với phần mềm đa nền tảng và các hệ thống * nix nói chung ... nhưng sau đó tôi không thực hiện bất kỳ công việc GUI gốc nào, tất cả các máy chủ và nội dung giao diện người dùng web)
Sean Corfield

2
Bạn có thể xem The Swine Before Perl . Đó là một cuộc nói chuyện thú vị để lắng nghe và trình bày một ví dụ đơn giản về những gì macro tốt cho.
Matthias Benkard

4
"Tôi chưa thấy một ví dụ duy nhất trong đó ý tưởng về macro không thể được triển khai như một hàm" - Tôi muốn xem cách bạn viết ANDhoặc ORnhư một hàm. Nó có thể được thực hiện (được đưa ra LAMBDA, cũng là một macro) nhưng tôi không thấy một cách rõ ràng để làm điều đó sẽ không hoàn toàn hút.

1
"Không gian tên. ... Clojure dường như có cái này, CL dường như không" - bạn có thể nói cụ thể hơn về những gì bạn đang tìm kiếm không? Tôi chưa thực sự sử dụng Clojure nhưng không gian tên của nó trông khá giống các gói của CL.

4
Jonathan: CL sử dụng :(hoặc ::cho các ký hiệu chưa xuất) để đặt tên các ký hiệu trong các gói, ví dụ: ví dụ của bạn ở đây sẽ sử dụng http:sessionchat:session.

Câu trả lời:


23

Macro cho phép bạn viết trình biên dịch với sự thoải mái trong ngôn ngữ của bạn. DSL trong các ngôn ngữ như C # thường tương đương với trình thông dịch thời gian chạy. Tùy thuộc vào tên miền của bạn, điều đó có thể có vấn đề vì lý do hiệu suất. Tốc độ không thành vấn đề , nếu không, bạn sẽ được mã hóa bằng ngôn ngữ được giải thích chứ không phải C #.

Ngoài ra, macro cũng cho phép bạn xem xét các yếu tố con người - cú pháp thân thiện với người dùng nhất đối với DSL cụ thể là gì? Với C #, bạn không thể làm gì nhiều về cú pháp.

Vì vậy, Lisp cho phép bạn tham gia thiết kế ngôn ngữ với hy sinh con đường hướng tới hiệu quả . Và mặc dù những vấn đề này có thể không quan trọng với bạn , nhưng chúng cực kỳ quan trọng vì chúng là nền tảng của tất cả các ngôn ngữ lập trình định hướng sản xuất hữu ích. Trong C #, yếu tố nền tảng này được tiếp xúc với bạn ở dạng rất hạn chế.

Tầm quan trọng của macro không bị mất trên các ngôn ngữ khác - Bản mẫu Haskell, camlp4 xuất hiện trong tâm trí. Nhưng một lần nữa, đó là một vấn đề về khả năng sử dụng - các macro Lisp cho đến nay có khả năng là triển khai mạnh mẽ nhất nhưng thân thiện với người dùng nhất trong việc chuyển đổi thời gian biên dịch.

Vì vậy, tóm lại, những gì mọi người thường làm với các ngôn ngữ như C # là xây dựng DSIL (Ngôn ngữ được giải thích cụ thể theo tên miền). Lisp cung cấp cho bạn cơ hội để xây dựng một cái gì đó triệt để hơn nhiều, DSP (Mô hình cụ thể miền). Vợt (trước đây là PLT-Scheme) đặc biệt truyền cảm hứng về vấn đề này - họ có một ngôn ngữ lười biếng (Vợt lười biếng), một kiểu đánh máy (Vợt đánh máy), Prolog và Datalog đều được nhúng một cách tự nhiên qua các macro. Tất cả các mô hình này cung cấp các giải pháp mạnh mẽ cho các lớp vấn đề lớn - các vấn đề không được giải quyết tốt nhất bằng lập trình mệnh lệnh hoặc thậm chí các mô hình FP.


3
Điều tôi cảm thấy thú vị về lập luận đó là tôi chưa bao giờ gặp phải một cơ hội (hoặc hoàn toàn bỏ qua nó trong sự thiếu hiểu biết của mình) để thực hiện DSL / DSIL. Bạn có thể giải thích về sức mạnh của DSL không? (Đối với cả nhà phát triển và người dùng.) Điều này nghe có vẻ như là điều lớn lao mà tôi thực sự đang thiếu.

15
@Jonathan Mitchem, bạn đã sử dụng rất nhiều DSL bên ngoài - tệp cấu hình, tập lệnh xây dựng, cấu hình ORM, trình tạo mã trực quan (ví dụ: trình chỉnh sửa winforms), XAML, trình tạo trình phân tích cú pháp, v.v. Không cần một công cụ xây dựng riêng cho họ và chúng được phục vụ trong một ngôn ngữ duy nhất. Và bạn phải làm quen với ít nhất hai trong số các DSL được nhúng - biểu thức chính quy và SQL.
SK-logic

Oh, tốt. Tôi chỉ không bao giờ nghĩ đó là DSL. Bây giờ, tôi đã cố tình đi lạc khỏi rất nhiều thứ đó, trên cơ sở "bất cứ điều gì tôi có thể làm với c #, tôi sẽ gắn bó với c # cho". Tôi thích gắn bó với một công cụ với một hành vi nhất quán, cá nhân. Nhưng tôi hiểu giá trị của DSL trong các ví dụ này.

2
@Jonathan Mitchem, vấn đề với bất kỳ công cụ cụ thể nào là không nhất thiết phải phù hợp cho một mục đích nhất định. Bạn phải chọn các công cụ khác nhau cho các nhiệm vụ khác nhau. Và sức mạnh của bất kỳ ngôn ngữ meta nào, bao gồm Lisp, là bạn không phải chuyển đổi hoàn toàn sang một công cụ khác, vì bạn có thể phát triển các công cụ dành riêng cho tên miền của riêng bạn trên ngôn ngữ chính của bạn. Tôi khuyên bạn nên xem Nemerle, sẽ dễ hiểu hơn cho nhà phát triển C # và các macro của nó cũng mạnh mẽ như của Lisp.
SK-logic

3
"Tôi thích gắn bó với một công cụ ..." DSL được chế tạo đúng cách trong Lisp không bao giờ che giấu toàn bộ sức mạnh của Lisp, vì vậy bạn vẫn có một công cụ. Họ chỉ thêm vào từ vựng theo cách làm cho việc mã hóa cho một tên miền cụ thể trở nên "tự nhiên" hơn, tức là với sự trừu tượng tốt hơn và tổ chức tổng thể.

15

Hầu như tất cả mọi thứ ban đầu chỉ có ở Lisps đã di chuyển sang rất nhiều ngôn ngữ hiện đại khác. Ngoại lệ lớn nhất là triết lý "mã là dữ liệu" và macro.

Mã là dữ liệu (và macro) - Có thể tôi chưa "nhận" macro, nhưng tôi chưa thấy một ví dụ duy nhất trong đó ý tưởng về macro không thể được thực hiện như một hàm

Vâng, macro rất khó để mò mẫm. Metaprogramming nói chung là khó để mò mẫm. Nếu bạn thấy bất kỳ macro nào có thể được thực hiện như một hàm, thì lập trình viên đã làm sai. Macro chỉ nên được sử dụng khi một chức năng không hoạt động.

Ví dụ "hello world" của macro là viết "nếu" dưới dạng cond. Nếu bạn thực sự quan tâm để tìm hiểu sức mạnh của macro, hãy thử xác định "my-if" trong clojure, sử dụng các chức năng và xem cách nó phá vỡ. Tôi không có nghĩa là bí ẩn, tôi chưa bao giờ thấy ai bắt đầu hiểu macro bằng cách giải thích một mình, nhưng trình chiếu này là một phần giới thiệu khá hay: http://www.sl slideshoware.net/pcalcado/lisp-macros-in -20 phút-tính năng-trình bày

Tôi đã có các dự án nhỏ nơi tôi đã lưu hàng trăm / nghìn dòng mã theo cách sử dụng macro (so với những gì nó đã thực hiện trong Java). Phần lớn Clojure được triển khai trong Clojure, điều này chỉ khả thi vì hệ thống vĩ mô.


3
Không chạm vào bất kỳ mã nào, tôi đã chạy qua kịch bản triển khai "nếu" không có macro trong đầu, cả trong Clojure và C #. Nó có hiệu quả (hoặc theo nghĩa đen) không thể được thực hiện. Tôi thừa nhận đó là gọn gàng. Nhưng tôi đang thiếu kết nối về việc nó hữu ích với tôi như thế nào. Tôi có thể làm gì với các macro (ngoài việc viết "nếu") mà tôi không thể làm với chức năng thông minh hoặc thiết kế OO? "Làm thế nào điều này có thể giúp tôi" không phải là một tiêu chí hợp lệ để đánh giá một ngôn ngữ, nhưng nó là để đánh giá liệu tôi có sử dụng nó không. (Tôi thực sự muốn một Lisp đáng để chuyển sang, nhưng nó đã không đạt được mục đích trở thành một sự thay thế khả thi.)

1
Nó sẽ khiến tôi mất một chút thời gian để mò mẫm ví dụ đó. Đối với tôi, "lưu mã soạn sẵn" thường gọi thuật ngữ "tái cấu trúc", đó là một phần lý do tại sao các giải thích không thuyết phục được tôi. Tôi có một giải pháp hoạt động trong mọi trường hợp tôi đã áp dụng nó. Tôi nghĩ sau đó câu hỏi là làm thế nào để bạn xác định các trường hợp bạn có thể sử dụng macro?

1
Tôi đồng ý rằng các macro là đáng giá, nhưng không phải là sự thật - nếu tôi yêu cầu chúng, nó có thể được viết dưới dạng hàm bậc cao hơn (xem ví dụ CL bên dưới). Bạn chỉ thực sự cần macro nếu bạn muốn đi bộ mã hoặc đặt nó trong một bối cảnh từ vựng mới. (defun my-if (test then &optional else) (cond (test (funcall then)) ('otherwise (funcall else))))

1
Về cơ bản với một chức năng, bạn có một sự lựa chọn để chấp nhận mã được trích dẫn, bạn có thể đi bộ nhưng nó thiếu ngữ cảnh từ vựng của nó bởi vì bạn đang đi bộ và thực thi nó trong ngữ cảnh từ vựng của chức năng. Hoặc bạn có thể chấp nhận các bao đóng, giữ bối cảnh từ vựng của họ nhưng bạn chỉ có thể gọi, không đi bộ hoặc thêm một cái gì đó vào bối cảnh từ vựng của họ. Để làm cả hai bạn cần một macro, có thể đi bộ và bọc mã trước khi mở rộng của nó sẽ được đặt vào ngữ cảnh từ vựng của lệnh gọi macro.

7
@Jonathan Mitchem, cú pháp LINQ là một ví dụ hay về những gì có thể được thực hiện với macro, nhưng phải được triển khai trong lõi ngôn ngữ, vì ngôn ngữ không hỗ trợ bất kỳ chương trình siêu dữ liệu tốt nào.
SK-logic

14

Tôi không phải là chuyên gia về Common Lisp, nhưng tôi biết cả C # và Clojure một cách hợp lý nên hy vọng đây là một viễn cảnh hữu ích.

  • Cú pháp đơn giản => sức mạnh - Lisps là về cú pháp đơn giản nhất có thể hoạt động. Biểu thức S là tất cả những gì bạn cần. Khi bạn đã mò mẫm "(gọi hàm arg1 arg2 arg3)" thì về cơ bản bạn đã có nó. Việc còn lại chỉ là chọn đúng các lệnh gọi và đối số. Có vẻ gần như đơn giản, nhưng thực tế là sự đơn giản này là thứ mang lại cho Lisps rất nhiều sức mạnh và tính linh hoạt, và đặc biệt cho phép các khả năng lập trình meta mà Lisp nổi tiếng. ví dụ nếu tôi muốn có một vĩ mô để gọi chức năng khác nhau trên cùng một đối số tôi chỉ làm điều gì đó như sau (không này là không chỉ thời gian chạy ứng dụng chức năng - đó là một vĩ mô mà tạo ra biên soạn mã như nếu bạn đã viết ra tất cả các chức năng cuộc gọi cá nhân bằng tay):
(defmacro print-many [functions args]  
  `(do   
     ~@(map   
        (fn [function] `(println (~function ~@args)))   
        functions)))

(print-many [+ - * /] [10 7 2])  
19  
1  
140  
5/7
  • Do cú pháp cực kỳ đơn giản, "mã là dữ liệu" của Lisp mạnh hơn nhiều so với bất cứ điều gì bạn có thể làm với thành phần / mẫu / tổng quát chức năng, v.v. Nó không chỉ là DSL, mà là tạo mã và thao tác mã, trong thời gian biên dịch , như là một tính năng cơ bản của ngôn ngữ. Nếu bạn cố gắng để có được các loại khả năng này bằng ngôn ngữ không đồng âm như C # hoặc Java, cuối cùng bạn sẽ phát minh lại Lisp, thật tệ. Do đó, Quy tắc thứ mười của Greenspun nổi tiếng: "Bất kỳ chương trình C hoặc Fortran nào đủ phức tạp đều chứa quảng cáo, được chỉ định không chính thức, có lỗi, triển khai chậm một nửa Lisp thông thường". Nếu bạn đã từng thấy mình phát minh ra một ngôn ngữ điều khiển dòng / tạo khuôn hoàn chỉnh trong ứng dụng của mình thì có lẽ bạn biết ý tôi là gì .....

  • Đồng thời là một thế mạnh duy nhất của Clojure (thậm chí so với các Lisps khác), nhưng nếu bạn tin rằng tương lai của lập trình sẽ có nghĩa là phải viết các ứng dụng mở rộng cho các máy đa lõi, thì bạn thực sự nên xem xét điều này - thật tuyệt đột phá. Clojure STM (Bộ nhớ giao dịch phần mềm) hoạt động vì Clojure chấp nhận các cấu trúc đồng thời không thay đổi và không khóa dựa trên sự tách biệt mới về nhận dạng và trạng thái (xem video xuất sắc của Rich Hickey về nhận dạng và trạng thái ). Sẽ rất đau đớn khi ghép loại khả năng tương tranh này vào môi trường ngôn ngữ / thời gian chạy trong đó một tỷ lệ đáng kể các API hoạt động trên các đối tượng có thể thay đổi.

  • Lập trình chức năng - Lisps nhấn mạnh lập trình với các hàm bậc cao hơn. Bạn đúng rằng nó có thể được mô phỏng trong các đối tượng OO với các đối tượng đóng / hàm, v.v. nhưng điểm khác biệt là toàn bộ ngôn ngữ và thư viện chuẩn được thiết kế để giúp bạn viết mã theo cách này. Ví dụ, các chuỗi Clojure là lười biếng , do đó có thể dài vô tận không giống như ArrayLists hoặc HashMaps của bạn trong C # / Java. Điều này làm cho một số thuật toán dễ dàng hơn nhiều để diễn đạt, ví dụ như sau đây thực hiện một danh sách vô số các số Wikipedia:

    (def fibs (lazy-cat '(0 1) (map + fibs (drop 1 fibs))))

  • Gõ động - Lisps có xu hướng ở cuối "động" của phổ ngôn ngữ lập trình chức năng (trái ngược với ngôn ngữ lang như Haskell với quan niệm rất mạnh mẽ và đẹp về các kiểu tĩnh). Điều này có cả ưu điểm và nhược điểm, nhưng tôi cho rằng các ngôn ngữ động có xu hướng thiên về năng suất lập trình vì tính linh hoạt cao hơn. Kiểu gõ động này có chi phí hiệu năng thời gian chạy nhỏ, nhưng nếu điều đó làm phiền bạn, bạn luôn có thể thêm gợi ý gõ vào mã của mình để có được kiểu gõ tĩnh. Về cơ bản - bạn có được sự thuận tiện theo mặc định và hiệu suất nếu bạn sẵn sàng làm thêm một chút để có được nó.

  • Phát triển tương tác - thực sự tôi nghĩ bạn có thể nhận được một số REPL cho C # 4.0, nhưng trong Lisp, đó là cách thực hành tiêu chuẩn chứ không phải là một điều mới lạ. Bạn hoàn toàn không phải thực hiện một chu trình biên dịch / xây dựng / kiểm tra - bạn viết mã trực tiếp trong môi trường đang chạy và chỉ sau đó sao chép nó vào các tệp nguồn của bạn. Nó dạy cho bạn một cách rất khác để viết mã, nơi bạn thấy kết quả ngay lập tức và tinh chỉnh giải pháp của bạn lặp đi lặp lại.

Một vài nhận xét nhanh về những điều bạn thấy là "mất tích":

  • Như bạn nói, Clojure có không gian tên. Trên thực tế, chúng là các không gian tên động: bạn có thể cập nhật chúng trong thời gian chạy - điều này cung cấp một số lợi ích đáng ngạc nhiên cho sự phát triển tương tác. Tôi đã có rất nhiều chức năng xác định lại thú vị dưới mũi của một luồng đang chạy hoặc hack một cấu trúc dữ liệu trực tiếp ....
  • Biên dịch / hỗ trợ thời gian thiết kế - không thực sự phù hợp nếu bạn đang mã hóa tại REPL - bạn chỉ cần thực hiện và xem kết quả trực tiếp. Phải nói rằng, Clojure đang bắt đầu nhận được một số hỗ trợ IDE được cải thiện, ví dụ như plugin CounterClockwise cho Eclipse .
  • Phát triển GUI - Có, bạn sẽ phải học một API mới, nhưng đó luôn là trường hợp khi chuyển đổi ngôn ngữ .... tin tốt là các API Java không khó lắm và giờ đây có một số Clojure thú vị- các hàm / ví dụ cụ thể trông khá dễ sử dụng. Xem câu hỏi này về phát triển GUI Clojure để biết một số ví dụ hay
  • Gỡ lỗi GUI: điều này hoạt động với trình gỡ lỗi GUI trong Eclipse giả sử bạn sử dụng CounterClockwise hoặc Enclojure nếu bạn sử dụng Netbeans. Phải nói rằng, tôi không sử dụng khả năng này - tôi thấy việc gỡ lỗi tương tác tại REPL hiệu quả hơn.

Cá nhân tôi thấy những ưu điểm của Clojure / Lisp khá hấp dẫn và tôi không có ý định quay lại C # / Java (ngoài những gì tôi cần để mượn tất cả các thư viện hữu ích!).

Tuy nhiên, phải mất một vài tháng để tôi thực sự hiểu được tất cả. Nếu bạn thực sự quan tâm đến việc học Lisp / Clojure như một trải nghiệm học tập khai sáng, không có gì thay thế cho việc lặn và buộc bản thân phải thực hiện một số điều .....


1
Cảm ơn sự liệt kê tuyệt vời của ý tưởng. Tôi thực sự sử dụng các chuỗi lười biếng rất nhiều thông qua cấu trúc IEnumerable <> trong C # (và đã không chạm vào ArrayList từ năm 2005 trở đi) nhưng tôi hiểu ý tưởng đó. Nguyên thủy đồng thời của Clojure là vô cùng ấn tượng. Tôi đã làm điện toán lưới trong C # vài năm trước - đồng thời trên nhiều máy đa lõi - và tôi vẫn tin rằng đó là tương lai. Có lẽ ý tưởng lớn nhất mà tôi nhận được từ mọi người là "bạn thực sự phải tìm hiểu và trải nghiệm nó, nó thực sự không thể giải thích được". Vì vậy, tôi sẽ tiếp tục đào và tiếp tục trải nghiệm.

1
Thật tuyệt, rất vui vì đã giúp đỡ - và đó hoàn toàn là tinh thần đúng đắn !!
mikera

10

C # đã có rất nhiều tính năng, nhưng sự pha trộn cảm thấy khác nhau. Rằng một số ngôn ngữ có một tính năng vẫn không có nghĩa là nó dễ sử dụng, mô tả và tích hợp tốt với phần còn lại của ngôn ngữ.

Lisp thông thường có sự pha trộn này:

  • biểu thức s như một cú pháp dữ liệu
  • mã dưới dạng dữ liệu dẫn đến macro và các kỹ thuật tính toán tượng trưng khác
  • ngôn ngữ động cho phép sửa đổi thời gian chạy (ràng buộc muộn, MOP, ...)
  • gõ mạnh, gõ động
  • sử dụng tương tác với trình biên dịch gia tăng hoặc trình thông dịch
  • Người đánh giá là công cụ thực thi cốt lõi
  • quản lý bộ nhớ với Bộ sưu tập rác
  • Ngôn ngữ lai với việc sử dụng nhiều chương trình bắt buộc, chức năng và hướng đối tượng. Các mô hình khác có thể được thêm vào (quy tắc, logic, ràng buộc, ...).

Bây giờ, các ngôn ngữ khác, như C #, cũng có điều đó. Nhưng thường không có cùng một điểm nhấn.

C # có

  • Cú pháp giống như C
  • chủ yếu là hướng đối tượng bắt buộc, với một số tính năng FP
  • gõ tĩnh
  • chủ yếu sử dụng hàng loạt với một trình biên dịch hàng loạt
  • quản lý bộ nhớ với Bộ sưu tập rác

Nó không giúp C # có các tính năng thao tác mã ví dụ, nếu chúng không được sử dụng rộng rãi như trong Lisp. Trong Lisp, bạn sẽ không tìm thấy nhiều phần mềm không sử dụng nhiều macro theo mọi cách đơn giản và phức tạp. Sử dụng thao tác mã thực sự là một tính năng lớn trong Lisp. Việc mô phỏng một số cách sử dụng là có thể trong nhiều ngôn ngữ, nhưng nó không làm cho nó trở thành một tính năng trung tâm như trong Lisp. Xem những cuốn sách như 'On Lisp' hoặc 'Lisp Common Common' để biết ví dụ.

Tương tự cho sự phát triển tương tác. Nếu bạn phát triển một hệ thống CAD lớn, hầu hết sự phát triển sẽ tương tác từ trình chỉnh sửa và REPL - trực tiếp vào ứng dụng CAD đang chạy. Bạn có thể mô phỏng phần lớn ngôn ngữ đó bằng C # hoặc các ngôn ngữ khác - thường bằng cách triển khai ngôn ngữ mở rộng. Đối với Lisp, thật ngu ngốc khi khởi động lại ứng dụng CAD đang được phát triển để thêm các thay đổi. Hệ thống CAD sau đó cũng sẽ chứa một Lisp nâng cao có thêm các tính năng ngôn ngữ (dưới dạng macro và mô tả biểu tượng) để mô tả các đối tượng CAD và các mối quan hệ của chúng (một phần, có chứa, ...). Người ta có thể làm những điều tương tự trong C #, nhưng trong Lisp đây là phong cách phát triển mặc định.

Nếu bạn phát triển phần mềm phức tạp bằng quy trình phát triển tương tác hoặc phần mềm của bạn có một phần tượng trưng đáng kể (lập trình meta, mô hình khác, đại số máy tính, sáng tác nhạc, lập kế hoạch, ...), thì Lisp có thể dành cho bạn.

Nếu bạn làm việc trong môi trường Windows (tôi không), thì C # có lợi thế, vì đây là ngôn ngữ phát triển chính của Microsoft. Microsoft không hỗ trợ bất kỳ hình thức Lisp nào.

Bạn viết:

  • Không gian tên. Ngay cả với các phương thức tĩnh, tôi thích buộc chúng vào một "lớp" để phân loại bối cảnh của chúng (Clojure dường như có điều này, CL dường như không.)

Lisp thông thường không gắn không gian tên vào các lớp. Không gian tên được cung cấp bởi các gói biểu tượng và độc lập với các lớp. Bạn cần định hướng lại cách tiếp cận của bạn đối với lập trình hướng đối tượng nếu bạn sử dụng CLOS.

  • Biên dịch tuyệt vời và hỗ trợ thời gian thiết kế, hệ thống loại cho phép tôi xác định "tính chính xác" của các cơ sở dữ liệu tôi chuyển qua bất cứ thứ gì sai chính tả được gạch chân trong thời gian thực; Tôi không phải đợi cho đến khi thời gian chạy để biết các cải tiến mã (chẳng hạn như sử dụng cách tiếp cận FP thay vì bắt buộc) được tự động đề xuất

Sử dụng trình biên dịch Lisp. Nó thông báo cho bạn về các biến không xác định, có thể có các đề xuất về kiểu (tùy thuộc vào trình biên dịch được sử dụng), đưa ra gợi ý về hiệu quả, ... Trình biên dịch là một tổ hợp phím.

  • Các công cụ phát triển GUI: WinForms và WPF (Tôi biết Clojure có quyền truy cập vào các thư viện GUI của Java, nhưng chúng hoàn toàn xa lạ với tôi.)

Các Lisps thương mại như LispWorks và Allegro CL cung cấp những thứ tương tự trong Windows.

  • Các công cụ gỡ lỗi GUI: breakpoint, step-in, step-over, value tests (text, xml, custom), watch, debug-by-thread, breakpoint có điều kiện, cửa sổ ngăn xếp cuộc gọi với khả năng nhảy tới mã ở mọi cấp độ trong ngăn xếp (Công bằng mà nói, phần của tôi với Emacs + Slime dường như cung cấp một số điều này, nhưng tôi là một phần của cách tiếp cận dựa trên VS GUI)

Các Lisps thương mại như LispWorks và Allegro CL cung cấp tất cả những thứ đó trong Windows.


1
Những lời chỉ trích của tôi không thực sự về Lisps. Tôi thấy họ khá có khả năng. Tôi đang tìm kiếm những gì Lisp có thể làm / cho tôi mà tôi chưa làm. Tôi hoàn toàn hiểu rằng hầu hết các nhà phát triển C # không sử dụng nhiều tính năng ngôn ngữ "mới". Tôi nghĩ rằng điểm lớn hơn là tôi làm, và thành thật mà nói, ngoài GUI, tôi gần như viết theo kiểu 100% FP. FP là một bước nhảy vọt về mặt khái niệm, nhưng đáng giá. Tuy nhiên, chuyển sang Lisp từ nơi tôi dường như cung cấp rất ít lợi ích. Tôi hy vọng tìm thấy rằng có một lý do tôi nên tiếp tục cho nó một cơ hội.

@Jonathan Mitchem: C # không thực sự giống ngôn ngữ FP, ngay cả khi ngôn ngữ đó có một số tính năng hỗ trợ và một vài thư viện có thể sử dụng ngôn ngữ đó. Nếu bạn muốn lập trình FP trong hệ sinh thái Microsoft, thì F # có thể dành cho bạn.

@Rainer Không, nó không giống ngôn ngữ FP, nhưng nó có thể làm được, và khá nhỏ gọn ở đó. Và khi tôi muốn / cần mã bắt buộc, hoặc OO, tôi cũng có thể làm điều đó. (hơi lạc đề: Tôi không thực sự coi F # là một ngôn ngữ nghiêm túc đáng để nghiên cứu, tôi muốn tìm hiểu về các đơn vị ở Haskell. Nhưng khi tôi là một nhà phát triển C ++ và C # xuất hiện, tôi đã không xem xét nó một ngôn ngữ "thực sự" hoặc sau đó không phải là,


@Jonathan Mitchem: tại sao tôi phải tìm? Tôi đã viết rằng C # hỗ trợ một số tính năng FP. Nó chỉ được thêm vào một ngôn ngữ hướng đối tượng bắt buộc và không làm cho nó thành ngôn ngữ, như trong, nói, Lisp, Scheme và đặc biệt không được so sánh với SML, F #, Haskell hoặc các ngôn ngữ chức năng chủ yếu khác. Quan điểm của tôi là: một số FP có thể có trong C #, nhưng nó không phải là một tính năng cốt lõi.

5

Rainier Joswig nói nó là tốt nhất.

Đối với tôi, sức mạnh của Lisp không thể hiểu được bằng cách chỉ nhìn vào một danh sách các tính năng của Lisp. Đối với Lisp và Lisp thông thường nói riêng, toàn bộ lớn hơn tổng của các bộ phận. Đó không chỉ là REPL cộng với biểu thức s cộng với macro cộng với công văn đa phương thức cộng với đóng gói cộng với gói cộng với CLOS cộng với khởi động lại cộng với X, Y và Z. Đó là toàn bộ shebang được tích hợp một cách khéo léo. Đó là trải nghiệm hàng ngày rất khác so với trải nghiệm hàng ngày của các ngôn ngữ lập trình khác.

Lisp trông khác nhau, nó được sử dụng khác nhau, một cách tiếp cận lập trình theo một cách khác khi sử dụng nó. Đó là thanh lịch, đầy đủ, lớn và liền mạch. Nó không phải là không có nhược điểm riêng và peccadilloes. Chắc chắn có sự so sánh với các ngôn ngữ khác có thể được thực hiện ở nơi mà nó có thể không đi ra phía trước. Nhưng không có cách nào là Lisp chỉ ngôn ngữ X cộng với REPL và macro, hoặc ngôn ngữ Y cộng với việc gửi đa phương thức và khởi động lại.


3
Mã là Dữ liệu (và Macro) - Có thể tôi chưa "nhận" macro, nhưng tôi chưa thấy một ví dụ duy nhất trong đó ý tưởng về macro không thể được thực hiện như một hàm; nó không thay đổi "ngôn ngữ", nhưng tôi không chắc đó là một thế mạnh

Thông thường, một macro nên được sử dụng cho một cái gì đó không thể biểu thị như một lời gọi hàm. Tôi không biết nếu có một điều kiện giống như chuyển đổi trong C # (tương tự như những gì tồn tại trong C, như chuyển đổi (biểu mẫu) {case ...}), nhưng chúng ta hãy giả sử nó có và có các hạn chế tương tự ( yêu cầu một loại tích phân).

Bây giờ, chúng ta hãy giả sử thêm rằng bạn thích một cái gì đó trông giống như vậy, nhưng gửi đi trên chuỗi. Trong lisp (tốt, cụ thể là trong Common Lisp và có thể ở những người khác), đó là "chỉ" một macro và nếu bạn hạn chế người dùng có các chuỗi liên tục (có thể không khó khăn) để khớp với, bạn có thể tính toán (thời gian biên dịch) một chuỗi các thử nghiệm tối thiểu để xác định trường hợp nào khớp (hoặc sử dụng bảng băm, lưu trữ các bao đóng, có thể).

Mặc dù có thể biểu thị rằng đó là một hàm (chuỗi so sánh, danh sách các trường hợp và bao đóng để thực thi), nhưng nó có thể không giống như "mã thông thường" và đó là nơi các macro lisp có một chiến thắng rõ ràng. Bạn đã có thể sử dụng khá một vài macro hoặc các hình thức đặc biệt taht là vĩ mô như thế nào, như defun, defmacro, setf, cond, loopvà nhiều hơn nữa nhiều.


2

Đây là bài viết giải thích tốt nhất mà tôi đã tìm thấy đưa người đọc từ bề mặt của sự vận động Lisp để cho thấy một số ý nghĩa sâu sắc hơn của các khái niệm đang được sử dụng:

http://www.defmacro.org/ramblings/lisp.html

Tôi nhớ lại việc đọc ở nơi khác một ý tưởng như thế này: Các ngôn ngữ khác đã áp dụng các tính năng khác nhau của Lisp; bộ sưu tập rác, gõ động, hàm ẩn danh, đóng để tạo ra C ++ / C / Algol tốt hơn. Tuy nhiên, để có được toàn bộ sức mạnh của Lisp, bạn cần tất cả các tính năng cần thiết của nó, tại thời điểm đó, bạn có một phương ngữ khác của Lisp, một họ ngôn ngữ riêng biệt đã tồn tại ở dạng này hay dạng khác trong hơn 50 năm.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.