C # Dev - ฉันได้ลองใช้ Lisps แล้ว แต่ฉันไม่เข้าใจ [ปิด]


37

หลังจากสองสามเดือนของการเรียนรู้และเล่นกับ Lisp ทั้ง CL และบิตของ Clojure ฉันยังไม่เห็นเหตุผลที่น่าสนใจที่จะเขียนอะไรลงไปแทนที่จะเป็น C #

ฉันจะชอบเหตุผลที่น่าสนใจบางส่วนหรือสำหรับคนที่จะชี้ให้เห็นว่าฉันหายไปบางสิ่งบางอย่างที่ยิ่งใหญ่จริงๆ

จุดแข็งของ Lisp (ต่อการวิจัยของฉัน):

  • กะทัดรัดสัญกรณ์ที่แสดงออก - มากกว่า C # ใช่ใช่ ... แต่ฉันดูเหมือนจะสามารถแสดงความคิดเห็นเหล่านั้นใน C # ด้วย
  • การสนับสนุนโดยนัยสำหรับการตั้งโปรแกรมการทำงาน - C # พร้อมด้วยวิธีการขยาย LINQ:
    • mapcar = .Select (แลมบ์ดา)
    • mapcan = .Select (แลมบ์ดา) .regregate ((a, b) => a.Union (b))
    • car / first = .First ()
    • cdr / rest = .Skip (1) .... เป็นต้น
  • การสนับสนุนฟังก์ชั่นแลมบ์ดาและลำดับสูงกว่า - C # มีสิ่งนี้และไวยากรณ์ง่ายขึ้น:
    • "(แลมบ์ดา (x) (ร่างกาย))" กับ "x => (ร่างกาย)"
    • "# (" กับ "%", "% 1", "% 2" ดีใน Clojure
  • วิธีการจัดส่งแยกออกจากวัตถุ - C # มีวิธีนี้ผ่านวิธีการขยาย
  • การส่งหลายวิธี - C # ไม่มีสิ่งนี้ แต่ฉันสามารถนำไปใช้เป็นการเรียกใช้ฟังก์ชันในเวลาไม่กี่ชั่วโมง
  • โค้ดคือข้อมูล (และมาโคร) - บางทีฉันอาจไม่ได้แมโคร "อากาศ" แต่ฉันไม่ได้เห็นตัวอย่างเดียวที่ความคิดของแมโครไม่สามารถนำมาใช้เป็นฟังก์ชันได้ ไม่เปลี่ยน "ภาษา" แต่ฉันไม่แน่ใจว่าเป็นจุดแข็ง
  • DSLs - สามารถทำได้ผ่านการจัดวางฟังก์ชั่นเท่านั้น ... แต่ใช้งานได้
  • การเขียนโปรแกรม "exploratory" แบบ Untyped - สำหรับ structs / classes, autoproperties ของ C # และ "object" นั้นทำงานได้ค่อนข้างดีและคุณสามารถเพิ่มการพิมพ์ที่แข็งแกร่งขึ้นได้อย่างง่ายดาย
  • ทำงานบนฮาร์ดแวร์ที่ไม่ใช่ของ Windows ใช่เลยเหรอ นอกวิทยาลัยฉันรู้จักเพียงคนเดียวที่ไม่ได้ใช้ Windows ที่บ้านหรืออย่างน้อย VM ของ Windows บน * nix / Mac (จากนั้นอีกครั้งบางทีนี่อาจสำคัญกว่าที่ฉันคิดและฉันเพิ่งถูกล้างสมอง ... )
  • REPL สำหรับการออกแบบจากล่างขึ้นบน - ตกลงฉันยอมรับว่ามันดีจริงๆและฉันคิดถึงมันใน C #

สิ่งที่ฉันขาดหายไปใน Lisp (เนื่องจากการผสมผสานของ C #, .NET, Visual Studio, Resharper):

  • namespaces แม้จะมีวิธีการแบบคงที่ฉันต้องการผูกพวกเขากับ "ชั้นเรียน" เพื่อจัดหมวดหมู่บริบทของพวกเขา (Clojure ดูเหมือนว่าจะมีสิ่งนี้ CL ดูเหมือนจะไม่ได้)
  • การรวบรวมและการออกแบบที่ยอดเยี่ยม
    • ระบบประเภทช่วยให้ฉันสามารถระบุ "ความถูกต้อง" ของโครงสร้างข้อมูลที่ฉันผ่าน
    • สิ่งที่สะกดผิดจะถูกขีดเส้นใต้เรียลไทม์; ฉันไม่ต้องรอจนกว่าจะรู้
    • การปรับปรุงรหัส (เช่นการใช้วิธีการ FP แทนวิธีที่จำเป็น) จะถูกบันทึกอัตโนมัติ
  • เครื่องมือพัฒนา GUI: WinForms และ WPF (ฉันรู้ว่า Clojure สามารถเข้าถึงไลบรารี Java GUI ได้ แต่พวกเขาต่างกับฉันทั้งหมด)
  • เครื่องมือแก้จุดบกพร่อง GUI: จุดพัก, step-in, step-over, ตัวตรวจสอบค่า (ข้อความ, xml, กำหนดเอง), นาฬิกา, debug-by-thread, เบรกพอยต์แบบมีเงื่อนไข, หน้าต่าง call-stack ที่มีความสามารถในการข้ามไปยังรหัสที่ระดับใดก็ได้ ในกอง
    • (เพื่อความเป็นธรรมการ จำกัด ของฉันกับ Emacs + Slime ดูเหมือนจะให้บางสิ่งนี้ แต่ฉันเป็นส่วนหนึ่งของวิธีการที่ขับเคลื่อนด้วย GUI GUI)

ฉันชอบโฆษณาเกินจริงโดยรอบ Lisp และฉันให้โอกาส

แต่มีอะไรที่ฉันสามารถทำได้ใน Lisp ที่ฉันทำไม่ได้ใน C #? อาจเป็น verbose ใน C # อีกเล็กน้อย แต่ฉันก็มีการเติมข้อความอัตโนมัติด้วย

ฉันพลาดอะไรไป เหตุใดฉันจึงควรใช้ Clojure / CL


4
เนื่องจากคุณกำลังเขียนโปรแกรมในสไตล์การใช้งานอยู่แล้วและดูเหมือนว่าคุณต้องพึ่งพาโครงสร้างพื้นฐานของ Windows ค่อนข้างมากฉันจึงไม่เห็นเหตุผลที่น่าสนใจสำหรับคุณที่จะเปลี่ยนจาก C # ทุกคนที่ฉันรู้จักใช้ Mac หรือ Linux มากขึ้นอยู่กับฝูงชนที่คุณใช้ (ฉันใช้ Windows ทุกรุ่นตั้งแต่ 3.1 แต่ฉันยังคงชอบทำงานกับซอฟต์แวร์ข้ามแพลตฟอร์มและระบบ * nix โดยทั่วไป ... แต่จากนั้นฉันก็ไม่ได้ทำงานกับ Native GUI, เซิร์ฟเวอร์และเว็บ UI ทุกอย่าง)
Sean Corfield

2
คุณอาจจะมีลักษณะที่สุกรก่อน Perl มันเป็นคำพูดที่สนุกที่จะฟังและนำเสนอตัวอย่างง่ายๆของสิ่งที่แมโครดีสำหรับ
Matthias Benkard

4
"ฉันไม่ได้เห็นตัวอย่างเดียวที่ความคิดของแมโครไม่สามารถนำมาใช้เป็นฟังก์ชัน" ได้ - ฉันต้องการดูว่าคุณเขียนANDหรือORทำหน้าที่อย่างไร มันสามารถทำได้ (ให้LAMBDAซึ่งเป็นแมโคร) แต่ฉันไม่เห็นวิธีที่ชัดเจนในการทำที่จะไม่ดูดทั้งหมด

1
"Namespaces. ... Clojure ดูเหมือนจะมีสิ่งนี้ CL ดูเหมือนจะไม่" - คุณจะเจาะจงมากขึ้นเกี่ยวกับสิ่งที่คุณต้องการหรือไม่? ฉันไม่ได้ใช้ Clojure จริงๆ แต่เนมสเปซของมันดูสวยมากเหมือนกับแพ็คเกจของ CL

4
โจนาธาน: การใช้ CL :(หรือ::สำหรับสัญลักษณ์ยกเลิกการส่งออก) สำหรับการตั้งชื่อสัญลักษณ์ในแพคเกจเช่นตัวอย่างของคุณที่นี่จะใช้และhttp:session chat:session

คำตอบ:


23

มาโครให้คุณเขียนคอมไพเลอร์โดยไม่ต้องกังวลกับภาษาของคุณ DSL ในภาษาอย่าง C # โดยทั่วไปจะเป็นล่ามรันไทม์ ขึ้นอยู่กับโดเมนของคุณที่อาจมีปัญหาเนื่องจากเหตุผลด้านประสิทธิภาพ ความเร็วมีความสำคัญไม่เช่นนั้นคุณจะได้รับการเข้ารหัสในภาษาที่ตีความไม่ใช่ C #

นอกจากนี้มาโครยังให้คุณพิจารณาปัจจัยมนุษย์ - ซินแท็กซ์ที่ใช้งานง่ายที่สุดสำหรับ DSL หนึ่ง ๆ คืออะไร ด้วย C # คุณไม่สามารถทำอะไรกับไวยากรณ์ได้มากนัก

ดังนั้นชัดช่วยให้คุณมีส่วนร่วมในการออกแบบภาษา w / o เสียสละเส้นทางไปสู่ประสิทธิภาพ และในขณะที่ปัญหาเหล่านี้อาจไม่สำคัญสำหรับคุณแต่สิ่งเหล่านี้มีความสำคัญอย่างยิ่งเนื่องจากเป็นรากฐานของภาษาการเขียนโปรแกรมเชิงสร้างสรรค์ที่มีประโยชน์ทั้งหมด ใน C # องค์ประกอบพื้นฐานนี้จะปรากฏต่อคุณในรูปแบบที่ จำกัด มากที่สุด

ความสำคัญของมาโครจะไม่สูญหายไปกับภาษาอื่น - เทมเพลตแฮสเค็ลล์, camlp4 คำนึงถึง แต่อีกครั้งมันเป็นปัญหาของการใช้งาน - มาโครที่ขาดหายไปในปัจจุบันน่าจะเป็นระบบที่ใช้งานง่ายที่สุดและมีประสิทธิภาพในการแปลงเวลาคอมไพล์

ดังนั้นโดยสรุปสิ่งที่ผู้คนทั่วไปทำด้วย w / ภาษาเช่น C # คือการสร้าง DSILs (ภาษาตีความเฉพาะโดเมน) Lisp เปิดโอกาสให้คุณสร้างสิ่งที่ต่างไปจากเดิมอย่างสิ้นเชิง DSP (Domain Specific Paradigms) แร็กเก็ต (เดิมชื่อ PLT-Scheme) สร้างแรงบันดาลใจเป็นพิเศษในเรื่องนี้ - พวกเขามีภาษาขี้เกียจ (Lazy Racket), ประเภทที่พิมพ์ (แร็กเก็ตแบบ Typed), Prolog และ Datalog ทั้งหมดฝังอยู่ในแมโครผ่านทางแมโคร กระบวนทัศน์เหล่านี้ทั้งหมดนำเสนอวิธีแก้ปัญหาที่ทรงพลังสำหรับปัญหาที่มีขนาดใหญ่ - ปัญหาที่ไม่สามารถแก้ไขได้ดีที่สุดโดยการโปรแกรมที่จำเป็น


3
สิ่งที่ฉันคิดว่าน่าสนใจเกี่ยวกับเรื่องนี้ก็คือฉันไม่เคยเจอโอกาส (หรือแค่มองข้ามมันไปโดยไม่รู้ตัว) เพื่อใช้ DSL / DSIL คุณสามารถอธิบายรายละเอียดเกี่ยวกับพลังของ DSL ได้หรือไม่? (สำหรับทั้งผู้พัฒนาและผู้ใช้) ฟังดูเหมือนว่าอาจเป็นเรื่องใหญ่ที่ฉันพลาดไป

15
@ Jonathan Mitchem คุณได้ใช้ DSL ภายนอกจำนวนมากแล้ว - ไฟล์กำหนดค่า, สร้างสคริปต์, การกำหนดค่า ORM, เครื่องสร้างรหัสภาพ (เช่นเครื่องมือแก้ไข winforms), XAML, เครื่องมือสร้างคำแยกวิเคราะห์ ฯลฯ DSL แบบฝังจะดีกว่าเช่นคุณ ไม่จำเป็นต้องมีเครื่องมือสร้างแยกต่างหากสำหรับพวกเขาและพวกเขากำลังให้บริการในภาษาเดียว และคุณต้องคุ้นเคยกับ DSL อย่างน้อยสองตัว - นิพจน์ทั่วไปและ SQL
SK-logic

ว้าวโอเค ฉันไม่เคยคิดว่าเป็น DSL ตอนนี้ฉันหลงทางไปอย่างมากจากสิ่งที่ฉันสามารถทำได้ด้วย c # ฉันจะติดกับ c # สำหรับ " ฉันชอบการใช้เครื่องมือเดียวที่มีพฤติกรรมสอดคล้องกันเป็นการส่วนตัว แต่ฉันเข้าใจถึงคุณค่าของ DSL ในตัวอย่างเหล่านี้

2
@ Jonathan Mitchem ปัญหาของเครื่องมือที่ให้มาคือไม่เหมาะสำหรับวัตถุประสงค์ที่กำหนด คุณต้องเลือกเครื่องมือต่าง ๆ สำหรับงานต่าง ๆ และพลังของเมตาดาต้าภาษาใด ๆ รวมถึงเสียงกระเพื่อมคือคุณไม่จำเป็นต้องเปลี่ยนไปใช้เครื่องมืออื่นอย่างเต็มที่เนื่องจากคุณสามารถพัฒนาเครื่องมือเฉพาะโดเมนของคุณเองให้เป็นภาษาหลักของคุณได้ ฉันขอแนะนำให้คุณดูที่ Nemerle มันจะง่ายต่อการเข้าใจสำหรับนักพัฒนา C # และมาโครของมันเกือบจะทรงพลังเหมือนกับ Lisp
SK-logic

3
"ฉันชอบการใช้เครื่องมือเดียว ... " การสร้าง DSL อย่างถูกต้องใน LISP ไม่เคยซ่อนเรี่ยวแรงทั้งหมดของ Lisp ดังนั้นคุณจึงยังมีเครื่องมือหนึ่งอยู่ พวกเขาเพียงเพิ่มคำศัพท์ในแบบที่ทำให้การเข้ารหัสสำหรับโดเมนใดโดเมนหนึ่งเป็น "ธรรมชาติ" มากกว่าเช่นมี abstractions ที่ดีกว่าและองค์กรโดยรวม

15

เกือบทุกอย่างที่เดิมมีเฉพาะใน Lisps ได้ย้ายไปยังภาษาอื่น ๆ จำนวนมาก ข้อยกเว้นที่ใหญ่ที่สุดคือปรัชญา "รหัสคือข้อมูล" และมาโคร

โค้ดคือข้อมูล (และมาโคร) - บางทีฉันอาจไม่ได้มาโคร "อากาศ" แต่ฉันไม่ได้เห็นตัวอย่างเดียวที่ความคิดของแมโครไม่สามารถนำมาใช้เป็นฟังก์ชันได้

ใช่มาโครยากที่จะคร่ำครวญ Metaprogramming โดยทั่วไปนั้นยากที่จะติดตาม หากคุณเห็นแมโครใด ๆที่สามารถนำไปใช้เป็นฟังก์ชั่นได้โปรแกรมเมอร์ก็ทำผิดพลาด ควรใช้มาโครเมื่อฟังก์ชันไม่ทำงาน

ตัวอย่าง "hello world" ของแมโครคือการเขียน "if" ในแง่ของเงื่อนไข หากคุณสนใจเรียนรู้พลังของมาโครลองกำหนด "my-if" ใน clojure โดยใช้ฟังก์ชั่นและดูว่ามันแตก ฉันไม่ได้ตั้งใจจะลึกลับฉันไม่เคยเห็นใครเริ่มเข้าใจแมโครโดยใช้คำอธิบายเพียงอย่างเดียว แต่สไลด์โชว์นี้เป็นบทนำที่ดีมาก: http://www.slideshare.net/pcalcado/lisp-macros-in -20 นาทีเนื้อเรื่อง-Clojure นำเสนอ

ฉันมีโครงการขนาดเล็กที่ฉันบันทึกเส้นโค้ดเป็นร้อย / พันเส้นโดยใช้มาโคร (เทียบกับที่ใช้ใน Java) Clojure ส่วนใหญ่นำไปใช้ใน Clojure ซึ่งเป็นไปได้เพียงเพราะระบบมาโคร


3
โดยไม่ต้องสัมผัสรหัสใด ๆ ฉันก็เลยลองใช้สถานการณ์ "ถ้า" ที่ไม่มีมาโครในหัวของฉันทั้งใน Clojure และ C # มันมีประสิทธิภาพ (หรือแท้จริง) ไม่สามารถทำได้ ฉันยอมรับว่าเรียบร้อย แต่ฉันขาดการเชื่อมต่อว่ามันมีประโยชน์กับฉันอย่างไร ฉันจะทำอย่างไรกับมาโคร (นอกเหนือจากเขียนว่า "ถ้า") ที่ฉันไม่สามารถทำได้ด้วยฟังก์ชั่นที่ชาญฉลาดหรือการออกแบบ OO "สิ่งนี้จะช่วยฉันได้อย่างไร" ไม่ใช่เกณฑ์ที่ถูกต้องสำหรับการประเมินภาษา แต่สำหรับการประเมินว่าฉันจะใช้หรือไม่ (ฉันต้องการให้เสียงกระเพื่อมนั้นคุ้มค่าที่จะเปลี่ยนไป แต่มันก็ยังไม่ถึงจุดที่จะกลายเป็นทางเลือกที่ทำงานได้)

1
มันจะใช้เวลาสักหน่อยในการติดตามตัวอย่าง สำหรับฉัน "การบันทึกรหัสสำเร็จรูป" มักจะเรียกคำว่า "การเปลี่ยนโครงสร้าง" ซึ่งเป็นส่วนหนึ่งของสาเหตุที่คำอธิบายไม่ได้ทำให้ฉันเชื่อมั่น ฉันมีวิธีแก้ปัญหาที่ใช้งานได้ในทุกครั้งที่ฉันใช้มัน ฉันคิดว่าคำถามนั้นเป็นวิธีที่คุณระบุกรณีที่คุณสามารถใช้แมโครได้อย่างไร

1
ฉันยอมรับว่ามาโครมีความคุ้มค่า แต่ก็ไม่เป็นความจริงที่ my-if ต้องการพวกเขาสามารถเขียนเป็นฟังก์ชันลำดับที่สูงขึ้น (ดูตัวอย่าง CL ด้านล่าง) คุณต้องการแมโครจริงๆถ้าคุณต้องการเดินโค้ดหรือวางไว้ในบริบทศัพท์ใหม่ (defun my-if (test then &optional else) (cond (test (funcall then)) ('otherwise (funcall else))))

1
โดยทั่วไปกับฟังก์ชั่นที่คุณมีทางเลือกที่จะยอมรับรหัสที่ยกมาซึ่งคุณสามารถเดิน แต่มันหายไปบริบทคำศัพท์เพราะคุณกำลังเดินและดำเนินการในบริบทศัพท์ของฟังก์ชั่นของคุณ หรือคุณสามารถยอมรับการปิดซึ่งเก็บบริบทศัพท์ของพวกเขา แต่คุณสามารถโทรเท่านั้นไม่เดินพวกเขาหรือเพิ่มบางสิ่งบางอย่างเพื่อบริบทศัพท์ของพวกเขา หากต้องการทำทั้งสองอย่างคุณจำเป็นต้องมีแมโครซึ่งสามารถเดินและล้อมรอบโค้ดก่อนที่การขยายจะถูกวางลงในบริบทศัพท์ของการเรียกใช้แมโคร

7
@Jonathan Mitchem, ไวยากรณ์ของ LINQ เป็นตัวอย่างที่ดีของสิ่งที่สามารถทำได้กับมาโคร แต่ต้องถูกนำไปใช้ในแกนภาษาแทนเนื่องจากภาษานั้นไม่รองรับ metaprogramming ที่เหมาะสม
SK-logic

14

ฉันไม่ใช่ผู้เชี่ยวชาญใน Common LISP แต่ฉันรู้ทั้ง C # และ Clojure อย่างสมเหตุสมผลดังนั้นหวังว่านี่เป็นมุมมองที่มีประโยชน์

  • Simple syntax => power - Lisps เป็นเรื่องเกี่ยวกับไวยากรณ์ที่ง่ายที่สุดที่สามารถทำงานได้ S-expressionsคือทุกสิ่งที่คุณต้องการ เมื่อคุณได้ grokked "(ฟังก์ชั่นการโทร arg1 arg2 arg3)" แล้วคุณจะได้รับมัน ส่วนที่เหลือเป็นเพียงการเลือกฟังก์ชั่นการโทรและการโต้แย้งที่ถูกต้อง ดูเหมือนจะง่ายนิดเดียว แต่ความจริงก็คือความเรียบง่ายนี้เป็นสิ่งที่ทำให้ Lisps มีความแข็งแกร่งและความยืดหยุ่นอย่างมากและโดยเฉพาะอย่างยิ่งทำให้ความสามารถในการเขียนโปรแกรมเมตาที่ Lisp มีชื่อเสียง เช่นถ้าฉันต้องการให้แมโครเรียกใช้ฟังก์ชันที่แตกต่างกันหลายตัวบนอาร์กิวเมนต์เดียวกันฉันจะทำสิ่งต่อไปนี้ (ไม่ใช่นี่ไม่ใช่แค่แอปพลิเคชันฟังก์ชันรันไทม์ - มันเป็นแมโครที่สร้างรหัสที่คอมไพล์ราวกับว่าคุณเขียนการเรียกฟังก์ชันแต่ละรายการทั้งหมด ด้วยมือ):
(defmacro print-many [functions args]  
  `(do   
     ~@(map   
        (fn [function] `(println (~function ~@args)))   
        functions)))

(print-many [+ - * /] [10 7 2])  
19  
1  
140  
5/7
  • อันเป็นผลมาจากไวยากรณ์ที่ง่ายอย่างไม่น่าเชื่อปรัชญา"รหัส is data"ของ Lisp นั้นทรงพลังมากกว่าสิ่งใด ๆ ที่คุณสามารถทำได้ด้วยฟังก์ชั่นการจัดวางองค์ประกอบ / แม่แบบ / ข้อมูลทั่วไปเป็นต้นมันเป็นมากกว่า DSL เท่านั้นมันคือการสร้างรหัสและการจัดการ เป็นคุณสมบัติพื้นฐานของภาษา หากคุณพยายามใช้ความสามารถเหล่านี้ในภาษาที่ไม่ใช่homoiconicเช่น C # หรือ Java ในที่สุดคุณก็จะสามารถสร้าง Lisp ได้ในที่สุด ดังนั้นกฎข้อที่สิบ Greenspuns ที่มีชื่อเสียง: "โปรแกรม C หรือ Fortran ที่ซับซ้อนพอสมควรมี ad hoc ที่ระบุอย่างไม่เป็นทางการระบุว่าใช้บั๊กครึ่งหนึ่งของ Common Lisp ที่ช้า" หากคุณเคยพบว่าตัวเองคิดค้นภาษาเทมเพลต / การควบคุมการไหลที่สมบูรณ์แบบในแอปพลิเคชันของคุณคุณอาจรู้ว่าฉันหมายถึงอะไร .....

  • การทำงานพร้อมกันเป็นจุดแข็งของ Clojure (แม้จะสัมพันธ์กับ Lisps อื่น ๆ ) แต่ถ้าคุณเชื่อว่าอนาคตของการเขียนโปรแกรมจะหมายถึงการต้องเขียนแอปพลิเคชั่นที่ขยายขนาดไปยังเครื่องมัลติคอร์สูง ๆ แหวกแนว Clojure STM (Software Transactional Memory) ทำงานเพราะ Clojure รวบรวมโครงสร้างที่ไม่เปลี่ยนแปลงและไม่เกิดการล็อคพร้อมกันบนพื้นฐานของการแยกตัวตนและสถานะใหม่ ๆ (ดูวิดีโอที่ยอดเยี่ยมเกี่ยวกับตัวตนและสถานะของ Rich Hickey ) มันจะเจ็บปวดมากที่จะต่อกิ่งความสามารถในการทำงานพร้อมกันนี้เข้ากับสภาพแวดล้อมภาษา / รันไทม์ซึ่งสัดส่วนที่สำคัญของ APIs ทำงานบนวัตถุที่ไม่แน่นอน

  • ฟังก์ชั่นการเขียนโปรแกรม - Lisps เน้นการเขียนโปรแกรมด้วยฟังก์ชั่นการสั่งซื้อที่สูงขึ้น คุณคิดถูกว่าสามารถเลียนแบบในวัตถุ OO ด้วยการปิด / ฟังก์ชั่นวัตถุ ฯลฯ แต่ความแตกต่างคือทั้งภาษาและไลบรารีมาตรฐานได้รับการออกแบบมาเพื่อช่วยให้คุณเขียนโค้ดด้วยวิธีนี้ ตัวอย่างเช่นลำดับ Clojure ขี้เกียจดังนั้นสามารถยาวอนันต์ไม่เหมือน ArrayLists หรือ HashMaps ใน C # / Java ขั้นตอนวิธีการนี้จะทำให้บางมากง่ายต่อการแสดงเช่นการดำเนินการดังต่อไปนี้เป็นรายการที่ไม่มีที่สิ้นสุดของตัวเลข fibonacci:

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

  • การพิมพ์แบบไดนามิก - Lisps มีแนวโน้มที่จะอยู่ที่ปลาย "ไดนามิก" ของสเปกตรัมภาษาการทำงานของโปรแกรม สิ่งนี้มีทั้งข้อดีและข้อเสีย แต่ฉันขอยืนยันว่าภาษาแบบไดนามิกมีแนวโน้มที่จะสนับสนุนการเขียนโปรแกรมเนื่องจากมีความยืดหยุ่นมากกว่า การพิมพ์แบบไดนามิกนี้มีค่าใช้จ่ายประสิทธิภาพรันไทม์เล็กน้อย แต่ถ้ารบกวนคุณคุณสามารถเพิ่มคำแนะนำประเภทให้กับรหัสของคุณเพื่อรับการพิมพ์คงที่ โดยทั่วไป - คุณจะได้รับความสะดวกสบายตามค่าเริ่มต้นและประสิทธิภาพหากคุณยินดีที่จะทำงานเพิ่มขึ้นเล็กน้อยเพื่อรับมัน

  • การพัฒนาเชิงโต้ตอบ - จริง ๆ แล้วฉันคิดว่าคุณจะได้รับ REPL บางประเภทสำหรับ C # 4.0 แต่ใน LISP มันเป็นมาตรฐานการปฏิบัติมากกว่าความแปลกใหม่ คุณไม่จำเป็นต้องทำการคอมไพล์ / สร้าง / ทดสอบเลย - คุณเขียนโค้ดของคุณโดยตรงในสภาพแวดล้อมการทำงานและหลังจากนั้นก็คัดลอกลงในไฟล์ต้นฉบับของคุณ มันสอนวิธีต่าง ๆ ในการเขียนโค้ดซึ่งคุณจะเห็นผลลัพธ์ได้ทันทีและปรับแก้ปัญหาของคุณซ้ำ ๆ

ความคิดเห็นสั้น ๆ เกี่ยวกับสิ่งที่คุณเห็นว่า "หายไป":

  • อย่างที่คุณพูด Clojure มีเนมสเปซ อันที่จริงมันเป็นเนมสเปซแบบไดนามิก: คุณสามารถอัปเดตตอนรันไทม์ - สิ่งนี้ให้ประโยชน์ที่น่าประหลาดใจสำหรับการพัฒนาเชิงโต้ตอบ ฉันมีฟังก์ชั่นการกำหนดนิยามใหม่ที่สนุกภายใต้จมูกของเธรดที่กำลังทำงานอยู่หรือแฮ็คโครงสร้างข้อมูลสด ....
  • สนับสนุนการคอมไพล์ / ออกแบบเวลา - ไม่เกี่ยวข้องถ้าคุณเขียนโค้ดที่ REPL - คุณเพียงแค่ทำและดูผลลัพธ์โดยตรง ต้องบอกว่า Clojure เริ่มที่จะได้รับการสนับสนุนบาง IDE ที่ดีขึ้นเช่นปลั๊กอินสำหรับ Eclipse
  • การพัฒนา GUI - ใช่คุณจะต้องเรียนรู้ API ใหม่ แต่มันจะเป็นเช่นนี้เสมอเมื่อเปลี่ยนภาษา .... ข่าวดีก็คือ Java APIs นั้นไม่ยากและตอนนี้มี Clojure ที่น่าสนใจ - อุปกรณ์ห่อหุ้ม / ตัวอย่างเฉพาะที่ดูสวยใช้งานง่าย ดูคำถามนี้เกี่ยวกับการพัฒนา Clojure GUIสำหรับตัวอย่างที่ดี
  • การดีบัก GUI: ใช้งานได้กับ GUI ดีบักเกอร์ใน Eclipse สมมติว่าคุณใช้ CounterClockwise หรือ Enclojure ถ้าคุณใช้ Netbeans ต้องบอกว่าฉันไม่ได้ใช้ความสามารถนี้ - ฉันพบการดีบักแบบโต้ตอบที่ REPL มีประสิทธิภาพมากขึ้น

ฉันค้นหาข้อดีของ Clojure / Lisp ที่น่าสนใจและฉันไม่ได้วางแผนที่จะกลับไปที่ C # / Java (นอกเหนือจากสิ่งที่ฉันต้องการยืมห้องสมุดที่มีประโยชน์ทั้งหมด!)

อย่างไรก็ตามมันใช้เวลาสองสามเดือนกว่าที่ฉันจะได้รู้จริง ๆ ทั้งหมด หากคุณสนใจที่จะเรียนรู้ Lisp / Clojure อย่างแท้จริงเป็นประสบการณ์การเรียนรู้ที่รู้แจ้งไม่มีอะไรทดแทนการดำน้ำและบังคับให้คุณทำอะไรสักอย่าง .....


1
ขอบคุณสำหรับการแจกแจงความคิดที่ยอดเยี่ยม ฉันใช้ซีเควนซ์ขี้เกียจจริง ๆ ผ่านทาง IEnumerable <> construct ใน C # (และไม่ได้สัมผัส ArrayList มาตั้งแต่ปี 2005 หรือมากกว่านั้น) แต่ฉันเข้าใจความคิดนี้ การเห็นพ้องด้วยกันของ Clojure นั้นน่าประทับใจอย่างยิ่ง ฉันกำลังใช้กริดคอมพิวติ้งใน C # เมื่อไม่กี่ปีก่อน - การทำงานพร้อมกันในเครื่องมัลติคอร์จำนวนมาก - และฉันยังเชื่อว่านั่นคืออนาคต อาจเป็นความคิดที่ยิ่งใหญ่ที่สุดที่ฉันได้รับจากทุกคนคือ "คุณต้องขุดและสัมผัสมันจริงๆมันไม่สามารถอธิบายได้จริง ๆ " ดังนั้นฉันจะขุดและยังคงประสบ

1
เจ๋งดีใจที่ได้ช่วย - นั่นคือวิญญาณที่ถูกต้องอย่างแน่นอน !!
mikera

10

C # มีคุณสมบัติมากมาย แต่การผสมผสานนั้นให้ความรู้สึกที่แตกต่าง ว่าบางภาษามีคุณสมบัติยังไม่ได้หมายความว่ามันใช้งานง่ายกระบวนทัศน์และบูรณาการได้ดีกับส่วนที่เหลือของภาษา

Common Lisp มีส่วนผสมนี้:

  • s-expressions เป็นไวยากรณ์ของข้อมูล
  • รหัสเป็นข้อมูลนำไปสู่แมโครและเทคนิคอื่น ๆ ของการคำนวณเชิงสัญลักษณ์
  • ภาษาแบบไดนามิกช่วยให้การปรับเปลี่ยนรันไทม์ (การรวมล่าช้า, MOP, ... )
  • พิมพ์อย่างยิ่งพิมพ์แบบไดนามิก
  • ใช้แบบโต้ตอบกับคอมไพเลอร์หรือล่ามที่เพิ่มขึ้น
  • Service Checker เป็นเครื่องมือประมวลผลหลัก
  • จัดการหน่วยความจำด้วย Garbage Collection
  • ภาษาไฮบริดที่มีการใช้งานอย่างหนักในการเขียนโปรแกรมที่จำเป็นฟังก์ชั่นและเชิงวัตถุ สามารถเพิ่มกระบวนทัศน์อื่น ๆ (กฎ, ตรรกะ, ข้อ จำกัด , ... )

ตอนนี้ภาษาอื่น ๆ เช่น C # ก็มีเช่นกัน แต่มักจะไม่ได้เน้นที่เดียวกัน

C # มี

  • ไวยากรณ์ C-like
  • เชิงวัตถุส่วนใหญ่มีความจำเป็นที่มีคุณสมบัติ FP บางอย่าง
  • พิมพ์แบบคงที่
  • ชุดส่วนใหญ่ใช้กับชุดรวบรวม
  • จัดการหน่วยความจำด้วย Garbage Collection

มันไม่ได้ช่วยให้ C # มีคุณสมบัติการจัดการโค้ดตัวอย่างเช่นหากพวกเขาไม่ได้ใช้กันอย่างแพร่หลายใน Lisp ใน Lisp คุณจะไม่พบซอฟต์แวร์มากมายที่ไม่ได้ใช้งานมาโครอย่างหนักในรูปแบบที่เรียบง่ายและซับซ้อนทุกชนิด การใช้การจัดการโค้ดเป็นคุณสมบัติที่สำคัญมากใน Lisp การเลียนแบบการใช้งานบางอย่างเป็นไปได้ในหลายภาษา แต่มันไม่ได้ทำให้มันเป็นคุณสมบัติกลางเหมือนในเสียงกระเพื่อม ดูตัวอย่างเช่น 'On Lisp' หรือ 'Common Lisp'

เหมือนกันสำหรับการพัฒนาเชิงโต้ตอบ หากคุณพัฒนาระบบ CAD ขนาดใหญ่การพัฒนาส่วนใหญ่จะเป็นแบบโต้ตอบจากโปรแกรมแก้ไขและ REPL - โดยตรงไปยังโปรแกรม CAD ที่ทำงานอยู่ คุณสามารถเลียนแบบส่วนใหญ่ใน C # หรือภาษาอื่น ๆ - บ่อยครั้งโดยการใช้ภาษาส่วนขยาย สำหรับเสียงกระเพื่อมมันจะโง่ที่จะเริ่มต้นโปรแกรม CAD ภายใต้การพัฒนาเพื่อเพิ่มการเปลี่ยนแปลง จากนั้นระบบ CAD จะมี Lisp ที่ปรับปรุงแล้วซึ่งจะเพิ่มคุณสมบัติภาษา (ในรูปแบบของแมโครและคำอธิบายเชิงสัญลักษณ์) สำหรับการอธิบายวัตถุ CAD และความสัมพันธ์ (ส่วนที่มีอยู่ ... ) เราสามารถทำสิ่งที่คล้ายกันใน C # แต่ใน Lisp นี่คือรูปแบบการพัฒนาเริ่มต้น

หากคุณพัฒนาซอฟต์แวร์ที่ซับซ้อนโดยใช้กระบวนการพัฒนาเชิงโต้ตอบหรือซอฟต์แวร์ของคุณมีส่วนที่เป็นสัญลักษณ์อย่างมาก (การเขียนโปรแกรมเมต้ากระบวนทัศน์อื่นพีชคณิตคอมพิวเตอร์องค์ประกอบดนตรีการวางแผน ... ) จากนั้นเสียงกระเพื่อมอาจเหมาะสำหรับคุณ

หากคุณทำงานในสภาพแวดล้อม Windows (ฉันไม่มี) ดังนั้น C # จึงมีข้อได้เปรียบเนื่องจากเป็นภาษาหลักในการพัฒนาของ Microsoft Microsoft ไม่สนับสนุน Lisp ในรูปแบบใด ๆ

ที่คุณเขียน:

  • namespaces แม้จะมีวิธีการแบบคงที่ฉันต้องการผูกพวกเขากับ "ชั้นเรียน" เพื่อจัดหมวดหมู่บริบทของพวกเขา (Clojure ดูเหมือนว่าจะมีสิ่งนี้ CL ดูเหมือนจะไม่ได้)

Common Lisp ไม่ได้แนบเนมสเปซเข้ากับคลาส เนมสเปซนั้นให้บริการโดยแพ็คเกจสัญลักษณ์และไม่ขึ้นอยู่กับคลาส คุณต้องปรับวิธีการของคุณในการเขียนโปรแกรมเชิงวัตถุหากคุณใช้ CLOS

  • การคอมไพล์และการออกแบบเวลาที่ยอดเยี่ยมระบบประเภทช่วยให้ฉันสามารถระบุ "ความถูกต้อง" ของโครงสร้างข้อมูลที่ฉันส่งผ่านสิ่งที่สะกดผิดจะถูกขีดเส้นใต้เรียลไทม์; ฉันไม่ต้องรอจนกว่ารันไทม์จะรู้ว่าการปรับปรุงรหัส (เช่นการใช้วิธีการ FP แทนสิ่งจำเป็น) จะถูกบันทึกอัตโนมัติ

ใช้คอมไพเลอร์เสียงกระเพื่อม มันจะแจ้งให้คุณทราบเกี่ยวกับตัวแปรที่ไม่รู้จักอาจมีคำแนะนำสไตล์ (ขึ้นอยู่กับคอมไพเลอร์ที่ใช้) ให้คำแนะนำเกี่ยวกับประสิทธิภาพ ... คอมไพเลอร์คือการกดแป้นไป

  • เครื่องมือพัฒนา GUI: WinForms และ WPF (ฉันรู้ว่า Clojure สามารถเข้าถึงไลบรารี Java GUI ได้ แต่พวกเขาต่างกับฉันทั้งหมด)

Lisps เชิงพาณิชย์เช่น LispWorks และ Allegro CL เสนอสิ่งที่คล้ายกันใน Windows

  • เครื่องมือแก้จุดบกพร่อง GUI: จุดพัก, step-in, step-over, ตัวตรวจสอบค่า (ข้อความ, xml, กำหนดเอง), นาฬิกา, debug-by-thread, เบรกพอยต์แบบมีเงื่อนไข, หน้าต่าง call-stack ที่มีความสามารถในการข้ามไปยังรหัสที่ระดับใดก็ได้ ในสแต็ก (เพื่อความเป็นธรรม จำกัด ของฉันกับ Emacs + Slime ดูเหมือนจะให้บางสิ่งนี้ แต่ฉันเป็นส่วนหนึ่งของวิธีการที่ขับเคลื่อนด้วย GUI GUI VS)

Lisps เชิงพาณิชย์เช่น LispWorks และ Allegro CL เสนอทุกสิ่งภายใต้ Windows


1
คำวิจารณ์ของฉันไม่ได้เป็นของ Lisps ฉันพบว่าพวกเขามีความสามารถมาก ฉันกำลังมองหาสิ่งที่เสียงกระเพื่อมสามารถทำ / ให้ฉันที่ฉันยังไม่ได้ทำ ฉันเข้าใจอย่างถ่องแท้ว่า C # devs ส่วนใหญ่ไม่ได้ใช้คุณสมบัติภาษา "ใหม่" มากมาย ฉันคิดว่าประเด็นที่ใหญ่กว่าคือฉันทำและโดยความซื่อสัตย์นอก GUI ฉันเกือบจะเขียนในสไตล์ FP 100% FP เป็นก้าวกระโดดเชิงแนวคิด แต่ก็คุ้มค่า อย่างไรก็ตามการเปลี่ยนเป็นเสียงกระเพื่อมจากที่ฉันดูเหมือนจะให้ประโยชน์น้อย ฉันหวังว่าจะพบว่ามีเหตุผลที่ฉันควรให้โอกาสกับมันต่อไป

@Jonathan Mitchem: C # ดูไม่เหมือนภาษา FP แม้ว่าภาษาจะมีคุณสมบัติบางอย่างที่สนับสนุนและบางไลบรารีก็อาจใช้งานได้ หากคุณต้องการที่จะทำการเขียนโปรแกรม FP ในระบบนิเวศของ Microsoft แล้ว F # อาจเหมาะสำหรับคุณ

@ Rainer ไม่ดูเหมือนภาษา FP แต่มันสามารถทำได้และค่อนข้างแน่นหนา และเมื่อฉันต้องการ / ต้องการรหัสที่จำเป็นหรือ OO ฉันก็สามารถทำได้เช่นกัน (ปิดหัวข้อเล็กน้อย: ฉันไม่คิดว่า F # เป็นภาษาจริงจังที่ควรค่าแก่การขุดฉันควรเอาหัวของฉันไปรอบ ๆ พระใน Haskell แต่เมื่อฉันเป็น C ++ dev และ C # ออกมาฉันไม่คิดว่ามัน ภาษา "ของจริง" ไม่ว่าจะเป็น [และย้อนกลับไปไม่ใช่ imho])

@Rainer นี่เป็นตัวอย่างของ FP ใน C # jmitchem.livejournal.com/#post-jmitchem-69978และjmitchem.livejournal.com/?skip=10#post-jmitchem-66618

@ Jonathan Mitchem: ทำไมฉันถึงต้องมองหา? ฉันเขียนว่า C # รองรับฟีเจอร์ FP บางอย่าง มันเพิ่งถูกเพิ่มเข้าไปในภาษาเชิงวัตถุที่จำเป็นและไม่ได้ทำให้มันเป็นสำนวนเหมือนใน Lisp, Scheme และโดยเฉพาะอย่างยิ่งเมื่อเทียบกับ SML, F #, Haskell หรือภาษาอื่น ๆ ที่ใช้งานได้เป็นหลัก ประเด็นของฉันคือ: FP บางตัวเป็นไปได้ใน C # แต่ไม่ใช่คุณสมบัติหลัก

5

Rainier Joswig พูดได้ดีที่สุด

สำหรับฉันพลังของเสียงกระเพื่อมไม่สามารถเข้าใจได้โดยเพียงแค่ดูรายการคุณลักษณะเสียงกระเพื่อม สำหรับเสียงกระเพื่อมและเสียงกระเพื่อมสามัญโดยเฉพาะอย่างยิ่งทั้งหมดมากกว่าผลรวมของชิ้นส่วน มันไม่ใช่แค่ REPL บวก s-expressions บวก macros บวกการจัดส่งแบบหลายวิธีรวมถึงการปิดและแพ็คเกจรวมถึง CLOS บวกการรีสตาร์ทบวก X, Y และ Z มันเป็นสิ่งที่รวมกันทั้งหมดอย่างชาญฉลาด มันเป็นประสบการณ์แบบวันต่อวันซึ่งแตกต่างอย่างมากจากประสบการณ์แบบวันต่อวันของภาษาการเขียนโปรแกรมอื่น ๆ

เสียงกระเพื่อมดูแตกต่างมันถูกใช้แตกต่างกันหนึ่งวิธีการเขียนโปรแกรมในแบบที่แตกต่างกันเมื่อใช้งาน มันสง่าสมบูรณ์สมบูรณ์ใหญ่และไร้รอยต่อ มันไม่ได้เป็นโดยไม่มีสิวและเพคาคาดิโลของตัวเอง มีการเปรียบเทียบอย่างชัดเจนกับภาษาอื่น ๆ ที่อาจเกิดขึ้นในที่ที่อาจไม่ออกมาข้างหน้า แต่ในทางใดทางหนึ่งเสียงกระเพื่อมแค่ภาษา X บวก REPL และมาโครหรือภาษา Y บวกการส่งและการรีสตาร์ทแบบหลายวิธี


3
โค้ดคือข้อมูล (และมาโคร) - บางทีฉันอาจไม่ได้แมโคร "อากาศ" แต่ฉันไม่ได้เห็นตัวอย่างเดียวที่ความคิดของแมโครไม่สามารถนำมาใช้เป็นฟังก์ชันได้ ไม่เปลี่ยน "ภาษา" แต่ฉันไม่แน่ใจว่าเป็นจุดแข็ง

โดยทั่วไปแล้วแมโครควรใช้สำหรับบางสิ่งที่ไม่สามารถแสดงเป็นการเรียกใช้ฟังก์ชันได้ ฉันไม่ทราบว่ามีเงื่อนไขเหมือนสวิตช์ใน C # (คล้ายกับสิ่งที่มีอยู่ใน C เป็นสวิตช์ (ฟอร์ม) {กรณี ... }) แต่ให้เราสมมติว่ามันทำและมีข้อ จำกัด เดียวกัน ( จำเป็นต้องใช้ประเภทอินทิกรัล)

ตอนนี้ให้เราคิดต่อไปว่าคุณจะชอบสิ่งที่ดูเหมือนว่า แต่ยื้อกับสตริง ในเสียงกระเพื่อม (ดีโดยเฉพาะใน Common LISP และอาจเป็นในคนอื่น ๆ ) นั่นคือ "เพียงแค่" แมโครออกไปและถ้าคุณ จำกัด ผู้ใช้ให้มีสตริงคงที่ (อาจจะไม่ลำบาก) เพื่อให้ตรงกับคุณสามารถคำนวณ ลำดับขั้นต่ำของการทดสอบเพื่อกำหนดกรณีที่ตรงกัน (หรือใช้ตารางแฮชการจัดเก็บการปิดหรืออาจ)

ในขณะที่อาจเป็นไปได้ที่จะแสดงว่าเป็นฟังก์ชั่น (สตริงเปรียบเทียบรายการของคดีและการปิดการดำเนินการ) มันอาจจะดูไม่เหมือน "รหัสปกติ" และนั่นคือที่มาโคร lisp มีการชนะแน่นอน คุณเคยใช้อาจจะค่อนข้างน้อยแมโครหรือรูปแบบพิเศษ taht มีแมโครเหมือนเช่นdefun, defmacro, setf, cond, loopและอื่น ๆ อีกมากมาย


2

นี่เป็นบทความอธิบายที่ดีที่สุดที่ฉันค้นพบซึ่งนำผู้อ่านจากพื้นผิวของ Lisp advocacy เพื่อแสดงให้เห็นถึงความหมายที่ลึกซึ้งยิ่งขึ้นของแนวคิดที่ใช้:

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

ฉันจำได้ว่าอ่านความคิดเช่นนี้ที่อื่น: ภาษาอื่น ๆ ได้ใช้คุณสมบัติที่หลากหลายของ Lisp; การรวบรวมขยะการพิมพ์แบบไดนามิกฟังก์ชั่นที่ไม่ระบุชื่อการปิดเพื่อสร้าง C ++ / C / Algol ที่ดีขึ้น อย่างไรก็ตามเพื่อให้ได้พลังเต็มรูปแบบของเสียงกระเพื่อมคุณจำเป็นต้องใช้คุณสมบัติที่จำเป็นทั้งหมดซึ่งเป็นจุดที่คุณมีภาษาถิ่นอีกเสียงหนึ่งของ Lisp ตระกูลภาษาที่แตกต่างซึ่งอยู่ในรูปแบบเดียวหรืออีกรูปแบบหนึ่งมานานกว่า 50 ปี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.