在 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(也许我可以使用一个在其自身上递归的匿名函数),但如果我不知道如何设置这样的变量,我会发疯的。
我的另一个猜测是使用 set!,但这会给出“无效的分配目标”,因为我没有处于绑定形式。
请告诉我这是如何工作的。
OK. I've been tinkering with Clojure and I continually run into the same problem. Let's take this little fragment of code:
(let [x 128]
(while (> x 1)
(do
(println x)
(def x (/ x 2)))))
Now I expect this to print out a sequence starting with 128 as so:
128
64
32
16
8
4
2
Instead, it's an infinite loop, printing 128 over and over. Clearly my intended side effect isn't working.
So how am I supposed to redefine the value of x in a loop like this? I realize this may not be Lisp like (I could use an anonymous function that recurses on it's self, perhaps), but if I don't figure out how to set variable like this, I'm going to go mad.
My other guess would be to use set!, but that gives "Invalid assignment target", since I'm not in a binding form.
Please, enlighten me on how this is supposed to work.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您认为在纯函数中拥有可变局部变量将是一个很好的方便功能,而且不会造成任何损害,因为该函数仍然保持纯函数,您可能会对这个邮件列表讨论感兴趣,其中 Rich Hickey 解释了他从语言中删除它们的原因。 为什么不使用可变局部变量?
相关部分:
后续帖子的大部分内容都涉及实现
with-local-vars
宏;)If you think that having mutable local variables in pure functions would be a nice convenient feature that does no harm, because the function still remains pure, you might be interested in this mailing list discussion where Rich Hickey explains his reasons for removing them from the language. Why not mutable locals?
Relevant part:
The majority of the subsequent posts concerns implementing a
with-local-vars
macro ;)def
定义了一个顶级变量,即使您在函数或某些代码的内部循环中使用它。 你在let
中得到的不是变量。 根据let
的文档:(强调不是我的。)这里的示例不需要可变状态; 您可以使用
loop
和recur
。如果你想变得更奇特,你可以完全避免显式的
循环
。如果您确实想要使用可变状态,atom 可能会起作用。
(您不需要
do
;while
会为您将其主体包装在一个显式的主体中。)如果您真的非常想要这样做对于 vars 你必须做这样可怕的事情。但这非常丑陋,而且根本不是 Clojure 惯用的。 为了有效地使用 Clojure,您应该尝试停止考虑可变状态。 尝试以非函数式风格编写 Clojure 代码肯定会让您发疯。 一段时间后,您可能会惊喜地发现实际上很少需要可变变量。
def
defines a toplevel var, even if you use it in a function or inner loop of some code. What you get inlet
are not vars. Per the documentation forlet
:(Emphasis not mine.) You don't need mutable state for your example here; you could use
loop
andrecur
.If you wanted to be fancy you could avoid the explicit
loop
entirely.If you really wanted to use mutable state, an atom might work.
(You don't need a
do
;while
wraps its body in an explicit one for you.) If you really, really wanted to do this with vars you'd have to do something horrible like this.But this is very ugly and it's not idiomatic Clojure at all. To use Clojure effectively you should try to stop thinking in terms of mutable state. It will definitely drive you crazy trying to write Clojure code in a non-functional style. After a while you may find it to be a pleasant surprise how seldom you actually need mutable variables.
变量(这就是当你“定义”某些东西时得到的)并不意味着被重新分配(但可以):
没有什么可以阻止你这样做:
如果你想要一个线程本地可设置的“位置”,你可以使用“绑定”和“设置!”:
那么你可以写一个像这样的循环:
但我认为这是非常不惯用的。
Vars (that's what you get when you "def" something) aren't meant to be reassigned (but can be):
There's nothing stopping you from doing:
If you want a thread-local settable "place" you can use "binding" and "set!":
So then you can write a loop like this:
But I think this is pretty unidiomatic.
您可以更惯用地使用
iterate
和take-while
来代替,You could more idiomatically use
iterate
andtake-while
instead,