Redefining a let'd variable in Clojure loop

后端 未结 4 1331
难免孤独
难免孤独 2021-02-01 02:00

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)
            


        
4条回答
  •  野趣味
    野趣味 (楼主)
    2021-02-01 02:55

    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.

提交回复
热议问题