0

我一直在使用 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”运行它时,我只看到“检查点”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。将同一任务运行一定次数的惯用方式是什么?

4

3 回答 3

3

循环返回一个惰性序列,该for序列仅在需要时进行评估。

当你运行程序里面的replfor结果是为了在屏幕上显示结果而实现的。

但是,当您运行时,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)))
于 2013-10-28T23:40:30.573 回答
1

Clojurefor不是命令式循环(你应该完全避免考虑 Clojure 中的循环),它是一个列表推导式,它返回惰性序列。它用于创建序列,而不是用于打印任何内容。你可以意识到这一点,并让它发挥作用,但这是不好的方式。

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

从我的角度来看,与 Clojure 中的命令式循环结构最相似的是尾递归,它是由循环/递归制成的。它仍然是 Clojure 中相当低级的构造,当然不应该以命令式循环的方式使用。更好地了解函数式编程原则以及 Clojure 核心函数。试图将 Java/Perl 思维转移到 Clojure 可能会伤害您。

于 2013-10-29T02:02:40.193 回答
0

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

于 2013-10-29T14:00:45.197 回答