我是Clojure的新手,我只想尝试构建一些示例应用程序以熟悉语法。我注意到以下顺序依赖行为。
我创建了一个名为timex的项目来计算两个日期之间的周数。我正在使用clj-time函数进行日期差异计算。
如果我的core.clj如下所示:
(ns timex.core
(:gen-class))
(defn -main
"Calculate weeks between dates."
[& args]
dp
)
(require '[clj-time.core :as t])
(def d2 (t/date-time 1989 01 07))
(def dw (t/in-weeks (t/interval d2 (t/now))))
(def dp (str "The number of weeks between Jan 7, 1989 and now is " dw "!"))
**
如果我运行lein repl,我会收到以下错误: #CompilerException java.lang.RuntimeException:无法在此上下文中解析符号:dp,编译:(timex / core.clj:4:1)
但是,如果我重新排序文件中的行并将def' s和require语句放在main之前
(ns timex.core
(:gen-class))
(require '[clj-time.core :as t])
(def d2 (t/date-time 1989 01 07))
(def dw (t/in-weeks (t/interval d2 (t/now))))
(def dp (str "The number of weeks between Jan 7, 1989 and now is " dw "!"))
(defn -main
"Calculate weeks between dates."
[& args]
dp
)
**
然后当我运行lein repl并调用(-main)函数时,我得到:
timex.core=> (-main)
"The number of weeks between Jan 7, 1989 and now is 1341!"
这种明显的顺序依赖正常还是我做错了什么?如果是后者,那么我将不胜感激任何我应该审查的建议或文件。感谢。
答案 0 :(得分:3)
Clojure中的编译单元是s-expression
,而不是像许多其他语言一样的整个文件。当您加载文件时,文件文件在一次传递中从上到下进行评估。 vars(通过调用def
创建的内容)必须在使用它们的位置上方创建,或者如果需要相互递归,则可以使用declare
,尽管这种情况很少见。
值得花一些时间习惯编译传统意义上的文件和加载lisp意义上的文件之间的区别,因为它是宏系统和语言的许多其他方面的基础。当您需要来自命名空间的文件,或从repl调用load-file
时,将调用clojure编译器并重复读取,宏扩展,然后从顶部开始对文件中的每个进行评估。第一行告诉它定义事物的名称空间,这就是ns
表达式首先出现的原因。然后,进一步的表单定义该命名空间中如果你再次加载文件,它只会再次从顶部读取文件。它不会清除命名空间或任何其他魔法,所以如果你评估文件,然后更改顺序并再次评估它可以继续工作,因为所需的函数已经在命名空间中定义(存储在memeory中)通过运行。习惯这个时,经常运行lein check
有助于确保事情顺利进行。
PS:绝大多数情况下,对require
的调用都是在文件顶部的ns
表单中。