1

我对 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}
4

1 回答 1

3

除了始终完全限定类名之外,您还可以import使用它并在之后使用短名称:

(import seven_languages.compass.SimpleCompass)

;; (SimpleCompass. 0) etc. will work now

此外,值得指出的是,它defrecord会为您创建工厂函数,一个是位置函数,一个是地图函数:

(defrecord Foo [x])

(->Foo 1)
;= #user.Foo{:x 1}
(map->Foo {:x 1})
;= #user.Foo{:x 1}

这些只是常规功能,因此会被您的use调用拉入。

相关地,deftype从 Clojure 1.5.1 开始,仅创建位置工厂。

于 2013-08-03T21:27:59.387 回答