1

我正在尝试将词性函数的输出通过管道传输到 index-words 函数中,并使用 (->) 线程宏打印结果输出:

(defn parts-of-speech []
  (seq (. POS values)))

(defn index-words [pos]
  (iterator-seq (. dict getIndexWordIterator pos)))

(-> (parts-of-speech) index-words println)

但是 index-words 函数返回一个迭代器序列,我不确定如何在这种情况下迭代它,因为我是 Clojure 的新手。

编辑:根据建议更新代码。

更新:

感谢@kotarak 和@jayunit100 的回答以及@sw1nn 和@marko-topolnik 的评论,我至少有两种变体:

(->> (parts-of-speech) (map index-words) (map println) doall)

(doseq [w (map index-words (parts-of-speech))]
  (println w))

我来自命令式背景,我对这个问题的目标是理解线程宏以尝试编写更惯用的 Clojure(在尝试线程宏之前,我使用多个doseqlets 循环遍历每个序列)。

从评论看来,线程宏可能不是最惯用的方法,但我仍然想看看如何使它工作,以便填补这一理解空白。

此外,(parts-of-speech)返回一个由四个项目组成的序列,如果您执行 a(println (count w))而不是(println w),您可以看到它打印四个序列的计数,而不是一个连续序列:

(doseq [w (map index-words (parts-of-speech))]
  (println (count w)))

;= 117798
;= 11529
;= 21479
;= 4481

您将如何修改上述内容以打印一个连续的单词流而不是打印四个序列的内容?

顺便说一句:上面的代码包装了 MIT Java WordNet 库(http://projects.csail.mit.edu/jwi/)。

4

2 回答 2

6

seqs 和 iterator-seq 之间的关系如下:一个 iterator-seq 从一个迭代器创建一个 seq。

原谅这里的冗长,但要回答“如何迭代 iterator-seq 的输出”的问题,我们必须首先明确定义为什么需要调用 iterator-seq 以开头:

在 Clojure 中,您不会发现自己需要太频繁地创建 iterator-seq 对象。由于 clojure 可以非常轻松地处理“Iterable”java 对象的迭代(参见:http ://clojuredocs.org/clojure_core/clojure.core/iterator-seq )。但是,迭代器本身是不可迭代的。
要完全理解这一点,您需要了解 Iterables 和 Iterators 之间的区别,这主要是由于在 Java 世界中保持语义一致和直接:为什么 Java 的 Iterator 不是 Iterable?.

那么什么是'seq'?

在clojure 中有一个比java 的Iterator 接口更高的抽象,即ISeq。iterator-seq 在底层为我们创建了一个 ISeq。这个 ISeq 对象现在可以被许多 Clo​​jure 函数使用,这些函数对项目的顺序列表进行操作。

user=> (iterator-seq (.iterator (new java.util.ArrayList ["A" "B"])))
("A" "B")
;Thus, we now have an ISeq implementation derived from an iterator.  

因此,您的“iterator-seq”函数正在为您创建一个来自 java 迭代器的 Clojure“序列”。澄清一下——当我们在不可迭代的对象上调用“iterator-seq”时的错误信息是有用的:

user=> (iterator-seq "ASDF")                                         
java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Iterator (NO_SOURCE_FILE:0)

这告诉我们“iterator-seq”函数需要一个 java.util.Iterator 作为输入。

您可能遇到的下一个合乎逻辑的问题是:

为什么我们需要从迭代器创建序列?seq 抽象与 java 中的迭代器抽象有何不同?

Iterable 接口不像 Clojure 的 ISeq 那样抽象。例如,考虑字符串。显然,字符串是顺序的。然而,它们在 Java 中不可迭代。数组也是如此。

从clojure网站:

“seq 适用于 Java 引用数组、Iterables 和字符串。由于该库的大部分其余部分都是基于这些函数构建的,因此对在 Clojure 算法中使用 Java 对象有很好的支持。”

因此,您的 iterator-seq 的目的是将您的迭代器对象“包装”成一个序列抽象,该抽象将能够利用所有 clojures 功能优势。

定义 iterator-seq 的作用

来自http://clojure.org/sequences

“seq 函数产生了适合集合的 ISeq 实现。”

在您的情况下,我们可以这样说:

“iterator-seq 函数为您的 getIndexWordsIterator 生成 ISeq 的实现”。

最后:我怎样才能迭代一个 seq ?

这个问题需要根据上下文仔细回答。

迭代当然是可能的 - 但不是 clojure 的主要关注点,它可能不是你真正想要的。由于 iterator-seq 已经为我们创建了一个 SEQ,现在我们可能可以使用 Clojure 的函数运算符之一使用该 seq(即在列表推导、映射函数等中)。这消除了手动迭代的需要。

例如,我们经常遍历一个列表来查找一个值。在 clojure 中,我们可以使用 filter 函数找到一个值:

user=> (filter #(= \A %) (seq "ABCD"))   
(\A)

而不是过滤,也许我们想通过迭代将一个函数应用于多个对象,将结果存储在一个新的集合中。同样,这不需要通过 Clojure 中的显式迭代来完成:

user=> (map #(.hashCode %) (seq "ABCZ")) 
(65 66 67 90)

最后,如果您真的需要手动遍历您的集合,您可以使用 Loop-recur 构造手动尾递归遍历您的序列,一次一个元素: http: //clojure.org/functional_programming#Functional%20Programming --递归%20循环。或者您可以使用标准递归调用。

于 2012-04-23T07:25:31.580 回答
2

你实际上必须调用你的函数。目前将函数 parts-of-speech传递给index-words.

(defn parts-of-speech
  []
  (.values POS))

(defn index-words
  [pos]
  (iterator-seq (.getIndexWordIterator dict pos)))

(-> (parts-of-speech) index-words println)

注意周围的括号parts-of-speech。另请注意,您使用的互操作语法非常古老。

于 2012-04-23T05:51:44.870 回答