Clojure ループで let'd 変数を再定義する
質問
わかりました。私は Clojure をいじっていますが、同じ問題に何度も遭遇します。このコードの小さな断片を見てみましょう。
(let [x 128]
(while (> x 1)
(do
(println x)
(def x (/ x 2)))))
これで、128 で始まるシーケンスが次のように出力されることが期待されます。
128
64
32
16
8
4
2
代わりに、128 を何度も出力する無限ループになります。明らかに、意図した副作用は機能していません。
では、このようなループ内で x の値を再定義するにはどうすればよいでしょうか?これは Lisp らしくないかもしれないことはわかっていますが (おそらく、それ自体を再帰する匿名関数を使用することもできます)、このように変数を設定する方法を理解していないと、気が狂ってしまうでしょう。
もう 1 つの推測は set! を使用することですが、バインディング フォームではないため、「無効な割り当てターゲット」が表示されます。
これがどのように機能するのか教えてください。
解決
def
関数またはコードの内部ループで使用する場合でも、トップレベルの var を定義します。何が入るのか let
var ではありません。あたり のドキュメント let
:
let で作成されたローカルは変数ではありません。一度作成された価値観は決して変わりません。
(強調は私ではありません。) ここでの例では可変状態は必要ありません。あなたは使うことができます loop
そして recur
.
(loop [x 128]
(when (> x 1)
(println x)
(recur (/ x 2))))
派手になりたい場合は、露骨な表現を避けることができます loop
全体的に。
(let [xs (take-while #(> % 1) (iterate #(/ % 2) 128))]
(doseq [x xs] (println x)))
もし、あんたが 本当に 可変状態を使用したいと考えていましたが、 原子 うまくいくかもしれない。
(let [x (atom 128)]
(while (> @x 1)
(println @x)
(swap! x #(/ %1 2))))
(必要ありません do
; while
その本体を明示的なものでラップします。) 本当に これを一緒にやりたかった 変数 このようなひどいことをしなければならないでしょう。
(with-local-vars [x 128]
(while (> (var-get x) 1)
(println (var-get x))
(var-set x (/ (var-get x) 2))))
しかし、これは非常に醜いものであり、慣用的な Clojure とはまったく異なります。Clojure を効果的に使用するには、変更可能な状態について考えるのをやめてください。Clojure コードを非機能的なスタイルで記述しようとすると、間違いなく気が狂ってしまうでしょう。しばらくすると、可変変数が実際に必要になることはほとんどないことに驚くかもしれません。
他のヒント
バール再割り当てすることを意味しない(することができますが)(それはあなたが「DEF」何かするときあなたが得るものです):
user=> (def k 1)
#'user/k
user=> k
1
やってからあなたを止めるものは何もありません。
user=> (def k 2)
#'user/k
user=> k
2
あなたはスレッドローカル設定可能な「場」をしたい場合は、「結合」を使用し、「設定!」することができます:
user=> (def j) ; this var is still unbound (no value)
#'user/j
user=> j
java.lang.IllegalStateException: Var user/j is unbound. (NO_SOURCE_FILE:0)
user=> (binding [j 0] j)
0
それではあなたはこのようなループを書くことができます:
user=> (binding [j 0]
(while (< j 10)
(println j)
(set! j (inc j))))
0
1
2
3
4
5
6
7
8
9
nil
しかし、私はこれはかなりunidiomaticだと思います。
、あなたはリッチヒッキーがそれらを取り除くための彼の理由を説明し、このメーリングリストの議論に興味があるかもしれません言語から。 なぜ変更可能ではない地元の人たちですか
関連部品ます:
地元の人々はすなわち可変変数であった場合は、、その後、クロージャはオーバー閉じることができ いくつかの余分ななし(閉鎖が逃げることができることを考えると可変状態、および、 同じの禁止)、結果はスレッド安全ではないだろう。そして、人々 確かにそうでしょう、例えば閉鎖ベースの擬似オブジェクト。結果 Clojureのアプローチの中に巨大な穴になります。
可変地元の人々がなければ、人々は、機能、RECURを使用することを余儀なくされています 構造をループします。これは最初は奇妙に思えるかもしれませんが、それは同じようです 突然変異を有するループとして簡潔、得られたパターンとすることができます Clojureの内の他の場所で再利用、即ちなど通勤、変更、軽減、再発 非常によく似た(論理的に)全てです。私は検出することができたにもかかわらず、および 逃げるの変異クロージャを防ぐため、私はそれを維持するためにこの方法を決めました 一貫性のため。最小の文脈において、非突然変異ループは ものを変異よりも理解しやすく、デバッグ。いずれにせよ、バール 使用の際に適切に利用できます。
with-local-vars
マクロを実装する後続のポストの問題の大部分;)
あなたはより多くの慣用的に代わりiterate
とtake-while
を使用することができ、
user> (->> 128
(iterate #(/ % 2))
(take-while (partial < 1)))
(128 64 32 16 8 4 2)
user>