私はClojureと関数型プログラミングを勉強しています。練習するために、4clojureの問題に取り組んでいます。
この機能(最善のアプローチではありません。私は知っています)は機能しています。(逆インターリーブ)ただし、関数はnilを再調整しています。
(defn reverse_interleave
[coll ss]
(let [xx (dec ss)]
(loop [coll (reverse coll) s xx _ss ss ret `()]
(if (nil? (first coll)) (do (println :ret ret) ret))
(when-let [x (first coll)]
(recur
(rest coll)
(if (zero? s) xx (dec s))
(if (or (= 1 _ss) (zero? _ss)) 0 (dec _ss))
(if (zero? _ss)
(map-indexed #(if (= % s) (cons x %2) %2) ret)
(cons (list x) ret))
))
)) ret)
(reverse_interleave (range 9) 3)
問題は...なぜですか?
ここで重要なのは、Clojureはほとんどの命令型言語のようなステートメントを実際には実行しないということです。でdo
、それが可能である副作用のために複数の式を評価して、最後のものの値を返す、などいくつかの構築物let
およびfn
暗黙的に含まれていますdo
。ただし、例外をスローしない限り、最後の式以外の式で評価を停止して「完了しました」と言うことはできません。そのため、ライン全体
(if (nil? (first coll)) (do (println :ret ret) ret))
その副作用によってのみ見ることができます。の値ret
またはとして評価され、nil
破棄されます。
次に、を開きますwhen-let
。これはのバリアントであるためwhen
、条件が満たされている場合は本体の値を返すか、満たされていない場合は本体の値を返しますnil
。本文はrecur
ループの先頭への命令であるため、このループは値でのみ終了できますnil
。
自然な修正は、if
フォームの最後の親を取得し、その後に移動when-let
して、フォーム全体がelse-caseの値になるようにすることです。コードは、それret
を作成するループの外側で名前を使用しているため、そのままではコンパイルに失敗しますが、それがなくなると、意図したとおりに機能するようです。
(defn reverse_interleave
[coll ss]
(let [xx (dec ss)]
(loop [coll (reverse coll) s xx _ss ss ret `()]
(if (nil? (first coll))
(do (println :ret ret) ret)
(when-let [x (first coll)]
(recur
(rest coll)
(if (zero? s) xx (dec s))
(if (or (= 1 _ss) (zero? _ss)) 0 (dec _ss))
(if (zero? _ss)
(map-indexed #(if (= % s) (cons x %2) %2) ret)
(cons (list x) ret))))))))
(reverse_interleave (range 9) 3)
;;returns ((0 3 6) (1 4 7) (2 5 8))
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加