重新定义let';Clojure循环中的d变量

重新定义let';Clojure循环中的d变量,clojure,let,function,Clojure,Let,Function,嗯。我一直在修补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(我可能会

嗯。我一直在修补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(我可能会使用一个在其自身上递归的匿名函数),但如果我不知道如何像这样设置变量,我会发疯的

我的另一个猜测是使用set!,但这会给出“无效的赋值目标”,因为我不是绑定形式


请告诉我应该如何工作。

def
定义了顶级变量,即使您在函数或某些代码的内部循环中使用它。你在
中得到的不是变量。Per:

使用let创建的局部变量不是变量。一旦创造,他们的价值观就永远不会改变

(重点不是我的。)这里的示例不需要可变状态;您可以使用
循环
重复

(loop [x 128]
  (when (> x 1)
    (println x)
    (recur (/ x 2))))
如果你想变得花哨,你可以完全避免显式的
循环

(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
,而
为你把它的身体包装成一个明确的身体。)如果你真的真的真的想这样做,你就必须做这样可怕的事情

(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 2)
#'user/k
user=> k
2
如果您想要一个线程本地可设置的“place”,可以使用“binding”和“set!”:

然后你可以写一个循环如下:

user=> (binding [j 0]
         (while (< j 10)
           (println j)
           (set! j (inc j))))
0
1
2
3
4
5
6
7
8
9
nil
user=>(绑定[j 0]
(而(

但是我认为这是非常不自然的。

如果你认为在纯函数中使用可变局部变量是一个很好的、方便的功能,而且没有任何伤害,因为函数仍然保持纯,那么你可能会对这个邮件列表讨论感兴趣,Rich Hickey解释了他将它们从语言中删除的原因

相关部分:

如果局部变量是可变的,那么闭包可以关闭 可变状态,并且,假设闭包可以转义(不需要额外的 禁止相同),其结果将是线程不安全。和人 当然会这样做,例如,基于闭包的伪对象。结果 这将是Clojure方法中的一个巨大漏洞

如果没有可变的局部变量,人们将被迫使用Recurr函数 循环构造。虽然这一点一开始看起来很奇怪,但实际上也一样 简洁如带突变的循环,由此产生的模式可以 在Clojure的其他地方重复使用,即重现、减少、改变、通勤等 都(逻辑上)非常相似。即使我能探测到 为了防止变异闭包逃逸,我决定保持这种方式 为了一致性。即使在最小的上下文中,非变异循环也很重要 比变异的更容易理解和调试。无论如何,Vars 可在适当的时候使用


随后的大多数帖子都涉及使用本地VAR实现
macro;)

您可以更习惯地使用
迭代
花时间

user> (->> 128
           (iterate #(/ % 2))
           (take-while (partial < 1)))

(128 64 32 16 8 4 2)
user>
user>(->>128
(迭代#(/%2))
(需要一段时间(部分<1)))
(128 64 32 16 8 4 2)
用户>

谢谢。我意识到我的方式并不利索,因为副作用是不容忽视的。我当时正在处理一些事情(一个项目Euler问题),但无法让那个简单的测试用例工作,这证明我不理解一些东西。谢谢你的帮助。我忘记了循环可以保持recur,这非常干净(没有额外的函数做递归)。副作用是Lispy,这取决于你在看哪个Lisp。在CommonLisp中,您可以避开(循环x=128然后(/x2),而(>x1)执行(打印x))。但是副作用不是Clojure,它是一个非常古老的问题,但这是一个非常好的答案,我是新来Clojure的,这使我从几个小时的同一问题的斗争中解脱出来。非常感谢@BrianCarperVery优雅。。!
user=> (binding [j 0]
         (while (< j 10)
           (println j)
           (set! j (inc j))))
0
1
2
3
4
5
6
7
8
9
nil
user> (->> 128
           (iterate #(/ % 2))
           (take-while (partial < 1)))

(128 64 32 16 8 4 2)
user>