我正在研究这本书的第一版,虽然我很喜欢这本书,但有些例子似乎过时了。我会放弃,找另一本书学习,但我真的很感兴趣的作者是什么,并希望使这些例子为自己工作,所以我试图更新他们的过程中。
下面的代码是一种基于clojure.contrib的分析文本的映射/减少方法。我尝试用#"\w+“将.split函数更改为read,使用line-seq代替读行,并将.toLowerCase改为string/小写。我试图跟踪源代码中的问题,彻底地读取文档,以了解在您使用整个序列之后,read-lines函数将关闭,而line-seq返回一个惰性字符串序列,实现java.io.BufferedReader。对我的问题最有帮助的是发布有关如何在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)))但是,当我在轻型表中编译和运行它时,我会得到大量错误:
1)在word-count-1命名空间中,当我尝试编辑后重新加载ns函数时,我会得到这样的结果:
java.lang.IllegalStateException: spit already refers to: #'clojure.contrib.io/spit in namespace: chapter-data.word-count-12)在平均行长命名空间中,在相同的情况下,我会得到类似的名称冲突错误:
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表时,将代码直接复制并粘贴到文件中(替换其中的内容),并调用顶级函数的实例-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发布于 2014-04-17 14:52:31
据我所知,clojure.contrib.io和clojure.contrib.seq-utils已不再更新,实际上它们可能与clojure.core函数(如spit )相冲突。我建议去掉这些依赖项,看看是否只使用核心函数就可以做到这一点。spit应该可以工作--您所得到的错误是由useing clojure.contrib.io引起的,它包含自己的spit函数,看起来大致相当;也许clojure.core中的当前版本是clojure.contrib.io/spit的“新的和改进的”版本。
parse-line函数的问题似乎是由于在两个不同的名称空间中定义了两个同名的函数而引起的。名称空间之间不相互依赖,但如果在REPL中加载两个名称空间,则仍然会遇到冲突。如果您一次只需要使用一个,那么尝试使用其中一个,然后当您想使用另一个时,请确保首先执行一个(remove-ns name-of-first-ns)来释放vars,这样就不会发生冲突。或者,您可以通过将parse-line更改为(defn- parse-line ...,使其成为每个命名空间中的私有函数。
编辑:如果您仍然需要clojure.contrib.io或clojure.contrib.seq-utils中任何在core或其他地方不可用的函数,则始终可以将源复制到您的命名空间中。参见github上的clojure.contrib.io和clojure.contrib.seq-utils。
https://stackoverflow.com/questions/23111957
复制相似问题