【问题标题】:How to pipe a Clojure iterator-seq using the -> (thread) macro?如何使用 -> (thread) 宏管理 Clojure 迭代器序列?
【发布时间】:2023-03-28 20:59:01
【问题描述】:

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

(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 的cmets,我至少有两种变体:

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

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

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

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

另外,(parts-of-speech) 返回一个由四个项目组成的序列,如果您执行 (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/)。

【问题讨论】:

  • 您的问题还没有解决吗?这看起来像正确的代码。它不打印序列吗?要在下面回答您的评论:在 clojure 中,您不使用迭代器。使用doseq 实现序列迭代。所以如果你愿意,你可以说(doseq [w (index-words (parts-of-speech)] (println w))
  • BTW 遍历原生 Java 数组与遍历 clojure 中的任何其他内容相同——即所有 seqable 内容。

标签: java clojure iterator sequence


【解决方案1】:

seqs和iterator-seq的关系如下:一个iterator-seq从一个iterator创建一个seq。

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

在 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。现在,许多 Clo​​jure 函数都可以使用这个 ISeq 对象,这些函数针对项目的顺序列表进行操作。

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中,我们可以通过 使用过滤功能:

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

【讨论】:

  • 谢谢杰。这就是我所拥有的: (->> (词性) (map index-words) (map println) doall) 这行得通,除了有两个问题我仍然很模糊: 1. 它正在打印一个完整的序列单词而不是单个单词。 2.我仍然不清楚如何修改它以使用->形式而不是->>。
  • 另一个重要的区别是迭代器是可变的(.next() 调用改变了迭代器的状态),而seqs 是不可变的和持久的。
  • @espeed :您可以尝试将 let 与中间打印语句一起使用,而不是 ->>,这样您就可以看到中间输出了吗?我建议使用 let 而不是线程操作符,这可能看起来很复杂。
  • @ivant 关于可变性的好点...在 seq 上调用“rest”不会更改 seq,而是返回一个新的 seq,它是一个子集。这意味着我们可以,例如,多次调用 (rest mySeq) 得到相同的结果。 java中的相反场景。
【解决方案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 周围的括号。另请注意,您使用的互操作语法非常古老。

【讨论】:

  • 谢谢。现在我得到“无法将 [Ledu.mit.jwi.item.POS; 转换为 edu.mit.jwi.item.POS”
  • [L;表示类名数组
  • 谢谢——现在我们回到我原来的问题——在这种情况下迭代数组和迭代器的正确方法是什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-02
  • 1970-01-01
  • 2017-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多