【发布时间】:2014-05-31 11:23:09
【问题描述】:
我正在阅读本书的第一版,虽然我很喜欢它,但其中一些示例似乎已经过时了。我会放弃并找到另一本书来学习,但我对作者所说的内容真的很感兴趣,并且想让这些例子适合我自己,所以我正在尝试更新它们。
以下代码是分析依赖于 clojure.contrib 的文本的 map/reduce 方法。我尝试将 .split 函数更改为使用 #"\w+" 重新排序,使用 line-seq 而不是 read-lines,并将 .toLowerCase 更改为字符串/小写。我尝试将我的问题跟踪到源代码并彻底阅读文档以了解 read-lines 函数在您使用整个序列后关闭,并且 line-seq 返回一个惰性字符串序列,实现 java.io.BufferedReader。对我的问题最有帮助的是post about how to read files after clojure 1.3。即使如此,我也无法让它工作。
所以这是我的问题:我需要在以下代码中更改哪些依赖项和/或函数才能使其成为现代、可靠、惯用的 Clojure?
第一个命名空间:
(ns chapter-data.word-count-1
(:use clojure.contrib.io
clojure.contrib.seq-utils))
(defn parse-line [line]
(let [tokens (.split (.toLowerCase line) " ")]
(map #(vector % 1) tokens)))
(defn combine [mapped]
(->> (apply concat mapped)
(group-by first)
(map (fn [[k v]]
{k (map second v)}))
(apply merge-with conj)))
(defn map-reduce [mapper reducer args-seq]
(->> (map mapper args-seq)
(combine)
(reducer)))
(defn sum [[k v]]
{k (apply + v)})
(defn reduce-parsed-lines [collected-values]
(apply merge (map sum collected-values)))
(defn word-frequency [filename]
(map-reduce parse-line reduce-parsed-lines (read-lines filename)))
第二个命名空间:
(ns chapter-data.average-line-length
(:use rabbit-x.data-anal
clojure.contrib.io))
(def IGNORE "_")
(defn parse-line [line]
(let [tokens (.split (.toLowerCase line) " ")]
[[IGNORE (count tokens)]]))
(defn average [numbers]
(/ (apply + numbers)
(count numbers)))
(defn reducer [combined]
(average (val (first combined))))
(defn average-line-length [filename]
(map-reduce parse-line reducer (read-lines filename)))
但是当我在 light table 中编译和运行它时,我得到了一堆错误:
1) 在 word-count-1 命名空间中,当我尝试在编辑后重新加载 ns 函数时得到这个:
java.lang.IllegalStateException: spit already refers to: #'clojure.contrib.io/spit in namespace: chapter-data.word-count-1
2) 在 average-line-length 命名空间中,在相同情况下我会遇到类似的名称冲突错误:
clojure.lang.Compiler$CompilerException: java.lang.IllegalStateException: parse-line already refers to: #'chapter-data.word-count-1/parse-line in namespace: chapter-data.average-line-length, compiling:(/Users/.../average-line-length.clj:7:1)
3) 奇怪的是,当我退出并重新启动 light table 时,将代码直接复制并粘贴到文件中(替换那里的内容)并调用其顶级函数的实例 word-count-1 命名空间运行良好,给了我test.txt 文件中某些单词的出现次数,但平均行长命名空间给了我这个:
"Warning: *default-encoding* not declared dynamic and thus is not dynamically rebindable, but its name suggests otherwise. Please either indicate ^:dynamic *default-encoding* or change the name. (clojure/contrib/io.clj:73)...
4) 此时,当我调用第一个命名空间的 word-frequency 函数时,它返回 nil 而不是单词出现的数量,当我调用第二个命名空间的 average-line-length 函数时,它返回
java.lang.NullPointerException: null
core.clj:1502 clojure.core/val
【问题讨论】:
-
我猜您遇到的错误与 LightTable 有关。也许尝试在这个问题的标签中添加
lighttable,看看是否有人观看该标签将能够提供一些指导?另外,尝试使用 Leiningen 从命令行运行您的代码,看看是否有什么不同。这些不是您在运行 Clojure 代码时通常会遇到的错误类型。 -
为了记录,我使用 LightTable,但没有遇到这些问题,所以如果它是 LightTable 的东西,也许可以通过更改一些 LT 配置设置来修复它,或者通过从刮。不过,那是在黑暗中刺伤:)
-
好点。我会在其他用途中尝试它,看看它是否继续发生,如果没有重新安装灯台。关于过时的习惯用法/依赖关系,有没有更好的方法来做到这一点?
-
我得到了完全相同的错误,但作为终端 leiningen repl 和 emacs 中的警告。每个都提到 contrib.io 或 contrib.seq.utils,如果我更改代码,我会得到名称冲突错误,与上面相同。我很确定我需要换掉依赖项并使用不同的功能。但是哪些?
-
额外信息:我很确定在 Clojure 1.2 中,您可以动态地将值绑定到符号,而无需声明它,并且按照惯例,它们是 earmuffed 的。在后来的版本中,这些约定变成了要求,遵循变异代码应该是强类型的理念。可以理解的是,一个 1.2 版的 contrib 库,它没有被维护以符合以后的要求,不会被更改以支持引擎盖下的动态绑定。此外,contrib 中使用的许多预定义符号被迁移到不同的命名空间,这意味着可能存在重叠。
标签: clojure mapreduce stack-trace dependency-management name-collision