在Clojure循环中重新定义let'd变量 [英] Redefining a let'd variable in Clojure loop

查看:138
本文介绍了在Clojure循环中重新定义let'd变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好。我一直在修补Clojure,并且不断遇到同样的问题。让我们看一下这段代码:

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)))))

现在我希望它能打印出以128开头的序列:

Now I expect this to print out a sequence starting with 128 as so:

128
64
32
16
8
4
2

相反,这是一个无限循环,一遍又一遍地打印128个。显然我预期的副作用无效。

Instead, it's an infinite loop, printing 128 over and over. Clearly my intended side effect isn't working.

那么我应该如何在这样的循环中重新定义x的值?我意识到这可能不像Lisp那样(也许我可以使用递归其自身的匿名函数),但是如果我不知道如何设置这样的变量,我会发疯的。

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.

我的另一个猜测是使用set !,但这会给出无效的分配目标,因为我没有绑定形式。

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.

推荐答案

def 定义一个顶级变量,即使您在某些代码的函数或内部循环中使用它也是如此。您在 let 中得到的不是vars。根据 let 的文档:

def defines a toplevel var, even if you use it in a function or inner loop of some code. What you get in let are not vars. Per the documentation for let:


使用let创建的本地变量不是变量。一旦创建,它们的值就永远不会改变!

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

(Emphasis not mine.) You don't need mutable state for your example here; you could use loop and recur.

(loop [x 128]
  (when (> x 1)
    (println x)
    (recur (/ x 2))))

如果想花哨,可以避免显式的循环

If you wanted to be fancy you could avoid the explicit loop entirely.

(let [xs (take-while #(> % 1) (iterate #(/ % 2) 128))]
  (doseq [x xs] (println x)))

如果您真的想要使用可变状态,则 atom 可能会起作用。

If you really wanted to use mutable state, an atom might work.

(let [x (atom 128)]
  (while (> @x 1)
    (println @x)
    (swap! x #(/ %1 2))))

(您不需要 do ,而会为您将其主体包裹在一个显式主体中。)如果您真的,真的想要使用 vars 做到这一点,您必须做些可怕的事情

(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.

(with-local-vars [x 128]
  (while (> (var-get x) 1)
    (println (var-get x))
    (var-set x (/ (var-get x) 2))))

但这非常丑陋,根本不是惯用的Clojure。为了有效地使用Clojure,您应该尝试停止在可变状态方面进行思考。尝试以非功能风格编写Clojure代码肯定会让您发疯。一段时间后,您可能会发现一个惊喜,您实际上很少需要可变变量。

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.

这篇关于在Clojure循环中重新定义let'd变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆