我一直在使用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。什么是惯用的方式来运行相同的任务一定次数?
答案 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>