จะเขียน Clojure Code ได้อย่างไร


13

ฉันใหม่กับ Clojure ฉันเข้าใจรหัสที่ฉันเขียนได้ แต่มันยากเกินไปที่จะเข้าใจในภายหลัง
มันยากที่จะจับคู่วงเล็บ

อะไรคือข้อตกลงทั่วไปที่ต้องติดตามเกี่ยวกับการตั้งชื่อและการเยื้องในสถานการณ์ต่าง ๆ

ตัวอย่างเช่นฉันเขียนตัวอย่างการกำหนดโครงสร้างตัวอย่างเพื่อทำความเข้าใจ แต่มันดูไม่สามารถอ่านได้อย่างสมบูรณ์ในครั้งที่สอง

(defn f [{x :x y :y z :z [a b c] :coll}] (print x " " y  " " z " " a " " b " " c)) 

ในกรณีของการปรับโครงสร้างควรทำโดยตรงที่ระดับพารามิเตอร์หรือเริ่มฟอร์มให้แล้วดำเนินการต่อที่นั่น?


3
มีคำตอบที่ดีเกี่ยวกับการอ่านได้ที่ Stack Overflow คุณสามารถตรวจสอบได้ stackoverflow.com/a/1894891/1969106
yfklon

2
โดยทั่วไปการเขียนรหัส Lisp ที่อ่านได้ยาก พวกเขาคิดค้น backronym "Lost In Superfluous วงเล็บ" ด้วยเหตุผล
Mason Wheeler

คำตอบ:


23

แบบแผนการตั้งชื่อ

  • อยู่ตัวพิมพ์เล็กสำหรับฟังก์ชั่น
  • ใช้-สำหรับการใส่ไฮเฟ็น

    (defn add-one [i] (inc i))

  • เพรดิเคต (เช่นฟังก์ชันที่ส่งคืนจริงหรือเท็จ) ลงท้ายด้วย? ตัวอย่าง:odd? even? nil? empty?

  • !จบขั้นตอนการเปลี่ยนแปลงของรัฐใน คุณจำได้set!ใช่มั้ย หรือswap!

  • เลือกความยาวชื่อตัวแปรแบบสั้นทั้งนี้ขึ้นอยู่กับการเข้าถึง นั่นหมายความว่าหากคุณมีตัวแปรเสริมขนาดเล็กมากคุณสามารถใช้ชื่อตัวอักษรเดียวได้บ่อยครั้ง (map (fn [[k v]] (inc v)) {:test 4 :blub 5})เลือกชื่อตัวแปรที่ยาวขึ้นตามต้องการโดยเฉพาะอย่างยิ่งถ้าพวกมันถูกใช้สำหรับโค้ดบรรทัดจำนวนมากและคุณไม่สามารถเดาวัตถุประสงค์ได้ทันที (ความคิดเห็นของฉัน).

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

    • ใช้ชื่อที่มีความหมายขั้นตอนกำลังทำอะไรบางอย่างเพื่อให้คุณสามารถอธิบายได้ดีที่สุดโดยใช้คำกริยา Clojure ในตัวฟังก์ชั่นควรใส่คุณในการติดตามขวา: drop, take, assocฯลฯ จากนั้นก็มีบทความที่ดีที่อธิบายถึงวิธีการที่จะเลือกชื่อที่มีความหมาย: http://ecmendenhall.github.io/blog/blog/2013/09/ 02 / ทำความสะอาด Clojure ความหมายชื่อ /

ฟังก์ชั่นแลมบ์ดา

  • คุณสามารถตั้งชื่อฟังก์ชั่นแลมบ์ดาได้ สิ่งนี้สะดวกสำหรับการดีบั๊กและการทำโปรไฟล์ (ประสบการณ์ของฉันคือ ClojureScript)

    (fn square-em [[k v]] {k (* v v)})

  • ใช้ฟังก์ชั่นแลมบ์ดาแบบอินไลน์ได้#()อย่างสะดวก

ช่องว่าง

  • ไม่ควรมีบรรทัด parens เท่านั้น คือปิดวงเล็บทันที โปรดจำไว้ว่า parens มีไว้สำหรับเอดิเตอร์และคอมไพเลอร์การเยื้องสำหรับคุณ

  • รายการพารามิเตอร์ฟังก์ชั่นจะขึ้นบรรทัดใหม่

   (defn cons
     [ab]
     (รายการ ab))

มันสมเหตุสมผลถ้าคุณคิดถึงสตริง doc อยู่ระหว่างชื่อฟังก์ชันและพารามิเตอร์ สตริงเอกสารต่อไปนี้อาจไม่ใช่สิ่งที่ฉลาดที่สุด)

   (defn cons
     "การจับคู่สิ่งต่าง ๆ "
     [ab]
     (รายการ ab))
  • ข้อมูลที่จับคู่สามารถคั่นด้วยบรรทัดใหม่ตราบใดที่คุณยังคงจับคู่
  (defn f 
    [{x: x 
      y: y 
      z: z  
      [abc]: coll}] 
    (พิมพ์ x "" y "" z "" a "" b "" c)) 

(คุณสามารถป้อนได้,ตามที่คุณต้องการ แต่นี่จะทำให้รู้สึกไม่ดี)

  • สำหรับการเยื้องใช้ตัวแก้ไขที่ดีพอ ปีที่ผ่านมานี่เป็น emacs สำหรับการแก้ไขเสียงกระเพื่อม, เป็นกลุ่มที่ดีในวันนี้ IDE clojure ทั่วไปควรจัดเตรียมฟังก์ชันนี้ด้วย อย่าใช้โปรแกรมแก้ไขข้อความแบบสุ่ม

    ในกลุ่มในโหมดคำสั่งคุณสามารถใช้=คำสั่งเพื่อเยื้องอย่างเหมาะสม

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

(+ (ถ้าให้ [อายุ (: coll ส่วนบุคคลอายุ])
     (ถ้า (>> อายุ 18 ปี)
       อายุ
       0))
   (นับ (ช่วง (- 3 b))
                 ลด + 
                         (ช่วง b 10))))

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

ฟังก์ชั่นการสั่งซื้อที่สูงขึ้นเมื่อเทียบกับforและdoseqแบบฟอร์ม

มาจากพื้นหลัง Scheme ฉันค่อนข้างภูมิใจที่ได้เข้าใจmapและฟังก์ชั่นแลมบ์ดา ฯลฯ ค่อนข้างบ่อยฉันจะเขียนบางอย่างเช่นนี้

(map (fn [[k x]] (+ x (k data))) {:a 10 :b 20 :c 30})

อ่านยากมาก forรูปแบบเป็นวิธีที่ดีกว่า:

(for [[k x] {:a 10 :b 20 :c30}]
  (+ x (k data)))

`แผนที่มีประโยชน์หลายอย่างและดีมากถ้าคุณใช้ฟังก์ชั่นที่ตั้งชื่อไว้ กล่าวคือ

(map inc [12 30 10]

(map count [[10 20 23] [1 2 3 4 5] (range 5)])

ใช้มาโครเธรด

ใช้มาโครเธรด->และ->>เช่นเดียวกับที่dotoเกี่ยวข้อง

ประเด็นก็คือว่าเธรดมาโครทำให้ซอร์สโค้ดปรากฏเป็นเส้นตรงมากกว่าองค์ประกอบของฟังก์ชัน โค้ดต่อไปนี้ค่อนข้างอ่านไม่ออกโดยไม่มีแมโครเธรด:

   (f (g (h 3) 10) [10 3 2 3])

เปรียบเทียบกับ

   (-> 
     (h 3)
     (g 10)
     (f [10 3 2 3]))

โดยการใช้เธรดแมโครโดยทั่วไปจะสามารถหลีกเลี่ยงการแนะนำตัวแปรชั่วคราวที่ใช้เพียงครั้งเดียวเท่านั้น

สิ่งอื่น ๆ

  • ใช้เอกสาร
  • ฟังก์ชั่นให้สั้น
  • อ่านรหัส clojure อื่น ๆ

ฟังก์ชั่นที่มีการปรับโครงสร้างนั้นดูสวยงามพร้อมการเยื้อง!
Amogh Talpallikar

+1 เพื่อให้ฟังก์ชั่นสั้น ฟังก์ชั่นเล็ก ๆ น้อย ๆ มากมายเป็นงานเอกสารด้วยตัวเองอีกมากมาย
Daniel Gratzer

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

@NathanWallace ในแบบที่ฉันเห็นด้วยกับคุณ แต่ในบางแง่มุมฉันไม่ ชื่อยาวบางครั้งมีแนวโน้มที่จะทำให้ฟังก์ชั่นเกินความจริง ดังนั้นคุณอาจพบว่าการดำเนินการตัวกรองทั่วไปบางอย่างนั้นโดยทั่วไปแล้วในขณะที่เมื่อมีการโต้แย้งapplesแทนคุณxsคิดว่ามันเป็นการเฉพาะสำหรับแอปเปิ้ล จากนั้นฉันก็จะพิจารณาชื่ออาร์กิวเมนต์ของฟังก์ชั่นว่าจะสามารถเข้าถึงได้ไกลกว่าปล่อยให้ a สำหรับตัวแปรลูป ดังนั้นถ้าจำเป็นคุณสามารถมีได้อีกต่อไป เป็นความคิดสุดท้าย: ฉันจะปล่อยให้คุณด้วย "ชื่อรหัสไม่ใช่ค่า" concatenative.org/wiki/view/Concatenative%20language/ …
wirrbel

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