Bagaimana cara menulis Kode Clojure yang dapat dibaca?

13

Saya baru di Clojure. Saya dapat memahami kode yang saya tulis tetapi menjadi terlalu sulit untuk memahaminya nanti.
Menjadi sulit untuk mencocokkan tanda kurung.

Apa konvensi umum yang harus diikuti mengenai konvensi penamaan dan indentasi dalam berbagai situasi?

Sebagai contoh saya menulis contoh de-struktur contoh untuk memahami tetapi kelihatannya benar-benar tidak dapat dibaca untuk kedua kalinya.

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

Dalam kasus de-structuring, apakah lebih baik melakukannya langsung di tingkat parameter atau memulai formulir let lalu lanjutkan ke sana?

Amogh Talpallikar
sumber
3
Ada jawaban yang bagus tentang keterbacaan di Stack Overflow. Anda bisa memeriksanya. stackoverflow.com/a/1894891/1969106
yfklon
2
Menulis kode Lisp mudah dibaca secara umum sulit. Mereka menemukan backronym "Lost In Superfluous Parenthesis" karena suatu alasan.
Mason Wheeler

Jawaban:

23

Konvensi penamaan

  • tinggal huruf kecil untuk fungsi
  • gunakan -untuk tanda hubung (apa yang akan menjadi garis bawah atau kasus unta dalam bahasa lain).

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

  • Predikat (yaitu fungsi yang mengembalikan benar atau salah) diakhiri dengan ? Contoh:odd? even? nil? empty?

  • Prosedur perubahan negara diakhiri dengan !. Anda ingat set!kan? atauswap!

  • Pilih panjang nama variabel pendek tergantung pada jangkauannya. Itu berarti jika Anda memiliki variabel bantu yang sangat kecil, Anda seringkali dapat menggunakan nama satu huruf. (map (fn [[k v]] (inc v)) {:test 4 :blub 5})pilih nama variabel yang lebih panjang sesuai kebutuhan, terutama jika mereka digunakan untuk banyak baris kode dan Anda tidak dapat langsung menebak tujuannya. (pendapat saya).

    Saya merasa bahwa banyak programmer clojure cenderung menggunakan nama generik dan pendek. Tapi ini tentu saja bukan observasi objektif. Intinya adalah bahwa banyak fungsi clojure sebenarnya cukup generik.

Fungsi Lambda

  • Anda sebenarnya dapat memberi nama fungsi lambda. Ini nyaman untuk debugging dan profil (pengalaman saya di sini adalah dengan ClojureScript).

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

  • Gunakan fungsi lambda sebaris senyaman #()mungkin

Ruang putih

  • Seharusnya tidak ada garis paren saja. Yaitu, tutup kurung segera. Ingat parens ada untuk editor dan kompiler, lekukan untuk Anda.

  • Daftar parameter fungsi menuju baris baru

   (defn kontra
     [ab]
     (daftar ab))

Ini masuk akal jika Anda berpikir tentang string dokumen. Mereka berada di antara nama fungsi dan parameter. String doc berikut ini mungkin bukan yang paling bijaksana;)

   (defn kontra
     "Mempasangkan hal-hal"
     [ab]
     (daftar ab))
  • Data yang dipasangkan dapat dipisahkan oleh baris baru selama Anda mempertahankan pasangan tersebut
  (defn f 
    [{x: x 
      Y y 
      z: z  
      [abc]: coll}] 
    (cetak x "" y "" z "" a "" b "" c)) 

(Anda juga dapat masuk ,sesuka Anda tetapi ini terasa tidak suram).

  • Untuk indentasi gunakan editor yang cukup bagus. Bertahun-tahun yang lalu ini adalah emacs untuk mengedit lisp, vim juga bagus hari ini. IDE clojure yang khas juga harus menyediakan fungsionalitas ini. Hanya saja, jangan gunakan editor teks acak.

    Dalam vim dalam mode perintah, Anda dapat menggunakan =perintah untuk membuat indentasi dengan benar.

  • Jika perintah terlalu panjang (bersarang, dll.) Anda dapat memasukkan baris baru setelah argumen pertama. Sekarang kode berikut ini cukup tidak masuk akal tetapi menggambarkan bagaimana Anda dapat mengelompokkan dan membuat indentasi:

(+ (if-let [usia (: coll usia pribadi)]
     (jika (> usia 18)
       usia
       0))
   (hitung (rentang (- 3 b)
                 (kurangi + 
                         (rentang b 10)))))

Lekukan yang baik berarti Anda tidak perlu menghitung tanda kurung. Tanda kurung adalah untuk komputer (untuk menafsirkan kode sumber dan untuk indentasi itu). Lekukan untuk memudahkan Anda memahami.

Fungsi fordan doseqbentuk orde tinggi

Berasal dari latar belakang Skema, saya agak bangga telah memahami mapfungsi dan lambda, dll. Jadi cukup sering, saya akan menulis sesuatu seperti ini

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

Ini sulit dibaca. The forBentuk adalah cara yang lebih baik:

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

`map memiliki banyak kegunaan dan sangat bagus jika Anda menggunakan fungsi-fungsi yang disebutkan. Yaitu

(map inc [12 30 10]

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

Gunakan Threading macro

Gunakan makro threading ->dan ->>juga dotojika berlaku.

Intinya adalah bahwa threading macro membuat kode sumber tampak lebih linier daripada komposisi fungsi. Sepotong kode berikut ini tidak dapat dibaca tanpa makro threading:

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

dibandingkan dengan

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

Dengan menggunakan threading macro, seseorang biasanya dapat menghindari memperkenalkan variabel sementara yang hanya digunakan sekali.

Hal-hal lain

  • Gunakan dokumen
  • menjaga fungsi singkat
  • baca kode clojure lainnya
wirrbel
sumber
Fungsi dengan penataan struktur terlihat indah dengan lekukan!
Amogh Talpallikar
+1 untuk menjaga fungsi tetap singkat. Banyak fungsi-fungsi kecil jauh lebih mendokumentasikan diri
Daniel Gratzer
1
Saya sangat tidak setuju bahwa itu ide yang baik untuk menggunakan nama variabel pendek, bahkan dalam fungsi "jangka pendek". Nama variabel yang baik sangat penting untuk keterbacaan dan tidak ada biaya kecuali stroke kunci. Ini adalah salah satu hal yang paling mengganggu saya tentang komunitas Clojure. Ada banyak orang dengan penolakan yang hampir memusuhi nama variabel deskriptif. Inti Clojure dikotori dengan nama variabel 1 huruf untuk argumen fungsi, dan itu membuatnya lebih sulit untuk belajar bahasa (misalnya menjalankan docatau sourcedalam REPL). Akhir kata-kata kasar, untuk jawaban yang sebaliknya bagus
Nathan Wallace
@NathanWallace Saya setuju dengan Anda, tetapi dalam beberapa aspek saya tidak. Nama panjang terkadang cenderung membuat fungsi terlalu spesifik. Jadi Anda mungkin menemukan bahwa beberapa operasi filter umum sebenarnya umum, sedangkan ketika argumennya applesbukan xs, Anda berpikir itu khusus untuk apel. Kemudian, saya juga akan menganggap nama argumen fungsi lebih jauh daripada membiarkan-katakan a untuk variabel loop. jadi jika perlu, Anda bisa memilikinya lebih lama. Sebagai pemikiran terakhir: Saya akan meninggalkan Anda dengan "Kode nama bukan nilai" concatenative.org/wiki/view/Concatenative%20language/…
wirrbel
Saya mungkin menambahkan paragraf tentang sesuatu seperti antarmuka pribadi vs publik. Terutama tentang perpustakaan. Ini adalah aspek kualitas kode yang tidak cukup dibicarakan dan saya telah belajar banyak tentang ini sejak menulis jawaban itu.
wirrbel