1. ホーム
  2. clojure

mentally Lisp/Clojureのコードの読み方

2023-08-02 20:59:49

質問

たくさんの素敵な回答をありがとうございました。1つだけ正解としてマークすることはできません

注:すでに wiki になっています

私は関数型プログラミングの初心者です。関数型プログラミングの簡単な関数、例えば数の階乗の計算などは読むことができますが、大きな関数を読むのは難しいと感じています。 その理由の一つは、関数定義内のコードの小さなブロックを把握することができないからだと思います。 ( ) にマッチさせるのが難しくなってきているからです。

誰かが私にいくつかのコードの読み方を教えてくれて、いくつかのコードを素早く解読するためのヒントを教えてくれたら最高です。

注:このコードは10分凝視すれば理解できますが、これと同じコードがJavaで書かれていたら10分もかからないと思います。だから、Lispスタイルのコードに安心するためには、もっと速くやらなければならないと思うのです

注:私はこれが主観的な質問であることを知っています。そして、私はここで証明できる正しい答えを求めているわけではありません。このコードをどのように読み取るかについてのコメントだけでも歓迎しますし、非常に役に立ちます。

(defn concat
  ([] (lazy-seq nil))
  ([x] (lazy-seq x))
  ([x y]
    (lazy-seq
      (let [s (seq x)]
        (if s
          (if (chunked-seq? s)
            (chunk-cons (chunk-first s) (concat (chunk-rest s) y))
            (cons (first s) (concat (rest s) y)))
          y))))
  ([x y & zs]
     (let [cat (fn cat [xys zs]
                 (lazy-seq
                   (let [xys (seq xys)]
                     (if xys
                       (if (chunked-seq? xys)
                         (chunk-cons (chunk-first xys)
                                     (cat (chunk-rest xys) zs))
                         (cons (first xys) (cat (rest xys) zs)))
                       (when zs
                         (cat (first zs) (next zs)))))))]
       (cat (concat x y) zs))))

どのように解決するのですか?

Lispのコードは、特に、規則的な構文のため、他の関数型言語よりもさらに読みにくいです。Wojciechは意味理解を向上させるための良い答えを与えてくれます。構文に関するヘルプはこちらです。

まず、コードを読むときに、括弧を気にしないでください。インデントを気にしてください。一般的なルールは、同じインデントレベルにあるものは関連しているということです。ですから

      (if (chunked-seq? s)
        (chunk-cons (chunk-first s) (concat (chunk-rest s) y))
        (cons (first s) (concat (rest s) y)))

次に、1行にすべてを収められない場合は、次の行を少しインデントしてください。これは ほとんど は常にスペース2つです。

(defn concat
  ([] (lazy-seq nil))  ; these two fit
  ([x] (lazy-seq x))   ; so no wrapping
  ([x y]               ; but here
    (lazy-seq          ; (lazy-seq indents two spaces
      (let [s (seq x)] ; as does (let [s (seq x)]

第三に、関数への複数の引数が一行に収まらない場合、最初の開始括弧の下に第二、第三などの引数を並べます。多くのマクロは、重要な部分が最初に表示されるようにするためのバリエーションを持つ同様のルールを持っています。

; fits on one line
(chunk-cons (chunk-first s) (concat (chunk-rest s) y))

; has to wrap: line up (cat ...) underneath first ( of (chunk-first xys)
                     (chunk-cons (chunk-first xys)
                                 (cat (chunk-rest xys) zs))

; if you write a C-for macro, put the first three arguments on one line
; then the rest indented two spaces
(c-for (i 0) (< i 100) (add1 i)
  (side-effects!)
  (side-effects!)
  (get-your (side-effects!) here))


これらのルールは、コード内のブロックを見つけるのに役立ちます。

(chunk-cons (chunk-first s)

括弧を数えないでください 次の行を確認してください。

(chunk-cons (chunk-first s)
            (concat (chunk-rest s) y))

次の行がその下にインデントされているので、最初の行が完全な式でないことがわかります。

もし defn concat を上から見た場合、同じレベルに3つのものがあるので、3つのブロックがあることがわかります。しかし、3行目以下はその下にインデントされているので、残りはその3番目のブロックに属しています。

Schemeのスタイルガイドはこちらです。 . Clojureは知りませんが、他のLispはどれもあまり変わらないので、ほとんどのルールは同じはずです。