【问题标题】:Why does Clojure's range iterate only 32 times?为什么 Clojure 的范围只迭代 32 次?
【发布时间】:2013-12-15 11:33:57
【问题描述】:

这个 Clojure 代码输出 32,而我期望输出 100。为什么会这样?用计数器制作循环的好方法是什么?

(def t 0)
(for [i (range 100)]
  (def t (+ 1 t))
  )
(println t)

【问题讨论】:

    标签: loops for-loop clojure range


    【解决方案1】:

    for 计算为惰性序列。由于一种叫做分块的东西,你在这里很幸运,for 被分块成 32 个元素的惰性分区。

    您问题中的代码非常不习惯。

    在你的情况下,你可以打电话给(println 100)

    如果你想循环一个序列以获得一些副作用,那么你可以使用doseq

    (doseq [i (range 100)]
      (println i))
    
    ;; or
    
    (dotimes [i 100]
      (println i))
    

    【讨论】:

    • doseq 正是我所需要的,谢谢!很难从命令式编程语言切换到函数式 Clojure。有没有收集好的惯用代码的来源?
    • @JayForeman 前往 4clojure.com,查看前 10 名左右的其他人的解决方案,完成所有简单和中等的解决方案。到那时你就会得到它。
    【解决方案2】:

    一些注意事项:

    • clojure 中的 for 是惰性的,这意味着它返回一个惰性序列。因为 clojure 中的惰性序列是分块的,所以您看到的是对第一个块的评估。
    • 您通常不希望在使用 for 或 map 时产生副作用(因为它们很懒惰)。为了避免这种情况,您应该使用doseq 或至少doall
    • 您也不想在函数内部定义,这不是在 clojure 中做事的正确方法。您应该使用更实用的方式,或者(如果合适的话)clojure 的一种状态构造(例如atomsagents

    这是一种更惯用的方式来获得你想要的东西:

    ; a more functional way
    (def t (reduce + (take 100 (repeat 1)))) 
    (println t) 
    
    ; a more "stateful" approach
    (def t (atom 0))
    (doseq [i (range 100)]
      (swap! t inc))
    
    (println @t)
    

    【讨论】:

    • 我身边的两分钱:(reduce + 0 ...) == (reduce + ...)(swap! t + 1) == (swap! t inc)。 (根本)并没有改善答案,但我仍然想提一下。 :-)
    • @xsc 我觉得你太谦虚了。我认为这两点确实改善了答案,使答案更清晰。
    猜你喜欢
    • 1970-01-01
    • 2020-12-20
    • 2017-10-13
    • 1970-01-01
    • 1970-01-01
    • 2011-10-28
    • 2020-04-03
    • 2014-03-15
    • 2015-12-01
    相关资源
    最近更新 更多