如何使用 - >管道Clojure迭代器-seq (线程)宏?

时间:2012-04-23 05:38:01

标签: java clojure iterator sequence

我正在尝试将词性功能的输出传递到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 func返回一个iterator-seq,我不知道如何在这个上下文中迭代它,因为我是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(在尝试使用线程宏之前,我使用多个{{循环遍历每个序列) 1}}和doseq s)。

从评论中可以看出,线程宏可能不是最惯用的方式,但我仍然希望看到如何使它工作,这样我才能填补理解上的空白。

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

(println w)

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

BTW:上面的代码包含了MIT Java WordNet库(http://projects.csail.mit.edu/jwi/)。

2 个答案:

答案 0 :(得分:6)

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

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

在Clojure中,您不会发现自己需要经常创建iterator-seq对象。因为clojure可以非常方便地处理“Iterable”java对象的迭代(参见:http://clojuredocs.org/clojure_core/clojure.core/iterator-seq)。但是,迭代器本身不可迭代 要完全理解这一点,您需要了解Iterables和Iterators之间的区别,这主要是因为Java语言中的语义保持一致和直接:Why is Java's Iterator not an Iterable?

那么什么是'seq'?

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

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和Strings。由于库的其余部分都是基于这些函数构建的,因此非常支持在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中,我们可以找到一个值 使用过滤功能:

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--Recursive%20Looping。或者您可以使用标准递归调用。

答案 1 :(得分: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周围的parens。另请注意,您使用的互操作语法非常古老。