Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
370 views
in Technique[技术] by (71.8m points)

Redefining a let'd variable in Clojure loop

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.

question from:https://stackoverflow.com/questions/940712/redefining-a-letd-variable-in-clojure-loop

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:

Locals created with let are not variables. Once created their values never change!

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

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

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

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...