新手:" for"函数中的循环以意想不到的方式运行

时间:2013-10-28 23:12:53

标签: clojure leiningen

我一直在使用Java和Perl开发很长一段时间,但我想学习一些新知识,所以我开始研究clojure。我尝试过的第一件事就是为河内之谜解决方案,但我在漂亮的打印功能上一直有奇怪的行为。基本上,当我使用'lein run'运行它时,我的for循环永远不会进入,但是当我从repl运行它时似乎工作正常。这是一个精简的例子:

(ns test-app.core
  (:gen-class))

(defn for-print
  "Print the same thing 3 times"
  [ p-string ]
  (println (str "Checkpoint: " p-string))
  (for
    [x [1 2 3]]
    (printf "FOR: %s\n" p-string)
))


(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (for-print "Haldo wurld!"))

当我使用'lein run'运行时,我只能看到“Checkpoint”println的输出。如果我删除该行,我根本就没有输出。但是,如果我运行'lein repl'然后输入(-main)它会按预期打印字符串3次:

test-app.core=> (-main)
Checkpoint: Haldo wurld!
(FOR: Haldo wurld!
FOR: Haldo wurld!
FOR: Haldo wurld!
nil nil nil)
test-app.core=> 

这里发生了什么?我有一种感觉,我正在以错误的方式接近这一点,尝试使用我过去的Perl / Java心态来编写clojure。什么是惯用的方式来运行相同的任务一定次数?

3 个答案:

答案 0 :(得分:3)

for循环返回一个只在需要时才计算的延迟序列。

当您在repl内运行程序时,实现结果以便在屏幕上显示结果。

但是当你使用lein run运行时,结果永远不会被使用,因此集合没有实现。

您有几种选择:

1)在doall循环之外使用for进行强制延迟序列实现

例如:

(defn for-print
 "Print the same thing 3 times"
 [ p-string ]
 (println (str "Checkpoint: " p-string))
 (doall (for
    [x [1 2 3]]
    (printf "FOR: %s\n" p-string))))

2)因为您只打印副作用并且没有真正创建集合,所以您可以使用doseq.

例如:

  (defn for-print
  "Print the same thing 3 times"
  [ p-string ]
  (println (str "Checkpoint: " p-string))
  (doseq [x [1 2 3]]
   (printf "FOR: %s\n" p-string)))

答案 1 :(得分:1)

Clojure for不是命令式循环(你应该避免考虑Clojure中的循环),它是一个列表理解,它返回延迟序列。它用于创建序列,而不是用于打印任何内容。你可以实现它,并使它工作,但这是不好的方式。

正如吉列尔莫所说,你正在寻找专为副作用而设计的doseq微距。在你的特定情况下,它可能是最惯用的Clojure。

从我的观点来看,与Clojure中的命令式循环结构最相似的是尾递归,用loop / recur进行。它仍然是Clojure中相当低级的构造,当然不应该以命令式循环方式使用。更好地了解函数式编程原理以及Clojure核心函数。尝试将Java / Perl思想转移到Clojure可能会对您造成伤害。

答案 2 :(得分:0)

其他答案是正确的,并提供详细信息。我想添加一些可能有用的更高级别的说明。在大多数语言中,“for”表示“为了这些和条件,执行此类和此类行为(可能是任意类型,包括副作用)。”这种事情可以在Clojure中完成,我见过有经验的Clojure程序员在有用和方便的时候这样做。但是,使用带有副作用的循环通常会违背语言的优势。所以在Clojure中,“for”这个词在大多数语言中都有不同的含义:生成(懒惰)序列。它意味着“对于这些输入,当/他/等等他们满足这样的条件时,暂时绑定到这些变量,通过以这样的方式处理每组值来生成(懒惰)序列。”< / p>