我对 Clojure 比较陌生,并且在七周内阅读了七种语言中的 Clojure 章节,我不明白为什么书中的这段代码对我不起作用。我正在使用 Leiningen 和 Clojure 版本 1.5.1。据我仔细检查后所知,我输入的代码与书中所读的完全一样。
这是代码:
(ns seven-languages.compass)
(defprotocol Compass
(direction [c])
(left [c])
(right [c]))
(def directions [:north :east :south :west])
(defn turn [base amount]
(rem (+ base amount) (count directions)))
(defrecord SimpleCompass [bearing]
Compass
(direction [_] (directions bearing))
(left [_] (SimpleCompass. (turn bearing 3)))
(right [_] (SimpleCompass. (turn bearing 1)))
Object
(toString [this] (str "[" (direction this) "]")))
我在 ~/clojure/seven-languages 目录中运行“lein repl”(通过在 ~/clojure 中运行“lein new Seven-languages”创建)。相对于这个目录,我的 .clj 文件位于 src/seven_languages 中。到目前为止,我已经能够通过键入从 repl 成功导入和使用它们(use 'seven-languages.filenamehere)
。
因此,将上面列出的代码保存为 src/seven_languages/compass.clj 后,我从 REPL 运行它:
user=> (use 'seven-languages.compass)
nil
但是当我尝试定义 SimpleCompass 的“实例”时,就像在书中一样输入它,就会发生这种情况:
user=> (def c (SimpleCompass. 0))
CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: SimpleCompass, compiling:(NO_SOURCE_PATH:1:8)
我也尝试使用 加载文件(load-file "src/seven_languages/compass.clj")
,但得到了相同的结果。由于实际加载似乎按预期工作,我想知道在编写《七周之七语言》之后,Clojure 版本中 defprotocol 或 defrecord 的工作方式是否发生了一些变化。在 Clojure 章节的介绍中,作者写道:“我使用的是 Clojure 1.2 的预发布版本,到你拿到这本书时它应该已经完全准备好了。”
谁能说出为什么这段代码不能正常工作?如果是版本问题,您将如何为 Clojure 1.5.1 更新此代码?
编辑:啊哈!找到这个后我想通了:
Clojure - deftype denied - unable to resolve classname on tests
这是一个命名空间问题。我猜这是自编写 7LI7W 时 1.2 版以来的变化。无论出于何种原因,虽然导入文件中的函数是“自动处理”的,因此您可以直接使用它们,但类型不会自动处理。您必须包含类型的完整路径,并确保使用带下划线而不是连字符的实际路径。我通过SimpleCompass
用完整路径替换我的代码来工作,seven_languages.compass.SimpleCompass
:
user=> (def c (seven_languages.compass.SimpleCompass. 0))
#'user/c
user=> c
#seven_languages.compass.SimpleCompass{:bearing 0}
user=> (left c)
#seven_languages.compass.SimpleCompass{:bearing 3}
user=> (right c)
#seven_languages.compass.SimpleCompass{:bearing 1}