重新定义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的(我可以使用递归的它的自我,也许是一个匿名函数),但如果我不知道如何设置变量这样,我就要疯了。
我的其他的猜测是使用设置!但,让“无效的分配目标”,因为我在绑定表单我不是。
请,见识一下这是如何工作的。
解决方案
def
限定顶层变种,即使你在函数或一些码内循环使用。你在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代码的非功能性的风格。过了一会儿,你会发现它是你实际上是一个意外的惊喜如何很少需要可变变量。
其他提示
瓦尔(这是你当你“高清”的东西)是注定不会被重新分配(但可以):
user=> (def k 1)
#'user/k
user=> k
1
有什么从做阻止你:
user=> (def k 2)
#'user/k
user=> k
2
如果你想有一个线程本地设定的“地点”,您可以使用“绑定”和“set!”
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的办法一个大洞。
无可变当地人,人们被迫使用易复发,功能 循环结构。虽然这可能首先看起来很奇怪,它只是作为 简洁与突变的循环,产生的图案可以是 Clojure中别处重复使用,即复发,减轻,改变,通勤等 全部(逻辑)非常相似。即使我能察觉到并 防止逃逸变异关闭,我决定保持下去 一致性。即使在最小的范围内,非不同诱变环路 更容易理解和调试比变异的。在任何情况下,瓦尔 可供使用在适当的时候。
在大多数实施with-local-vars
宏随后帖担忧;)
您可以更惯用使用iterate
和take-while
代替,
user> (->> 128
(iterate #(/ % 2))
(take-while (partial < 1)))
(128 64 32 16 8 4 2)
user>