在clojure中使用惰性字符串集合进行分区

在clojure中使用惰性字符串集合进行分区,clojure,lazy-evaluation,Clojure,Lazy Evaluation,从字符串集合开始,如: (def str-coll ["abcd" "efgh" "jklm"]) 目标是从字符串集合的头部提取特定数量的字符,生成字符串的分区分组。这是期望的行为: (use '[clojure.contrib.str-utils2 :only (join)]) (partition-all 3 (join "" str-coll)) ((\a \b \c) (\d \e \f) (\g \h \j) (\k \l \m)) 但是,使用联合部队对整个集合进行评估,这在处理

从字符串集合开始,如:

(def str-coll ["abcd" "efgh" "jklm"])
目标是从字符串集合的头部提取特定数量的字符,生成字符串的分区分组。这是期望的行为:

(use '[clojure.contrib.str-utils2 :only (join)])
(partition-all 3 (join "" str-coll))

((\a \b \c) (\d \e \f) (\g \h \j) (\k \l \m))
但是,使用联合部队对整个集合进行评估,这在处理非常大的字符串集合时会导致内存问题。我的特定用例是从一个惰性集合生成字符串子集,该惰性集合是通过解析一个由分隔记录组成的大文件生成的:

(defn file-coll [in-file]
  (->> (line-seq (reader in-file))
    (partition-by #(.startsWith ^String % ">"))
    (partition 2))))

并且是建立在工作的基础上。我尝试了reduce、partition和join的组合,但无法找到正确的咒语来从第一个字符串的开头提取字符,并根据需要惰性地评估后续字符串。非常感谢您提供的任何想法或建议。

不太确定您的目标,但以下内容与您的第一个示例相同,并且很懒散

为清晰起见,请逐步:

user=> (def str-coll ["abcd" "efgh" "jklm"]) #'user/str-coll user=> (map seq str-coll) ((\a \b \c \d) (\e \f \g \h) (\j \k \l \m)) user=> (flatten *1) (\a \b \c \d \e \f \g \h \j \k \l \m) user=> (partition 3 *1) ((\a \b \c) (\d \e \f) (\g \h \j) (\k \l \m)) 用户=>(def str coll[“abcd”“efgh”“jklm”]) #'用户/str coll 用户=>(映射顺序str coll) ((\a\b\c\d)(\e\f\g\h)(\j\k\l\m)) 用户=>(展平*1) (\a\b\c\d\e\f\g\h\j\k\l\m) 用户=>(分区3*1) ((\a\b\c)(\d\e\f)(\g\h\j)(\k\l\m)) 现在大家一起:

(->> str-coll (map seq) flatten (partition 3)) (->str coll (图seq) 压平 (第三分区)
编辑:我写的所有东西都错了

当将带有var arg的函数应用于seq大于离散arg数的函数时,seq的其余部分将作为var arg传递(请参阅)

对于尔根说:我很愚蠢。你很聪明。我错了。你是对的。你是最棒的。我是最坏的。你很漂亮。我没有吸引力

以下是我的白痴记录


回应于尔根·霍策尔的评论

mapcat
并不完全懒惰,因为
apply
在计算要应用的参数数量时并不懒惰。此外,
apply
不能是惰性的,因为函数必须使用离散数量的参数调用。当前,如果arg的数量超过20,则剩余的arg将转储到一个数组中,因此是非惰性的

因此,请查看
mapcat
的源代码:

(defn mapcat "Returns the result of applying concat to the result of applying map to f and colls. Thus function f should return a collection." {:added "1.0"} [f & colls] (apply concat (apply map f colls))) 这很好,因为
str coll
没有完全实现,但是外部
apply
将计算为:

user=> (map seq str-coll) ((\a \b \c \d) (\e \f \g \h) (\j \k \l \m)) user=> (concat '(\a \b \c \d) '(\e \f \g \h) '(\j \k \l \m)) (\a \b \c \d \e \f \g \h \j \k \l \m) 并演示了varargs并不懒惰:

user=> (defn foo [& more] (type more)) #'user/foo user=> (foo 1 2 3 4) clojure.lang.ArraySeq user=> (apply foo (repeat 4 1)) clojure.lang.Cons user=>(defn foo[&更多](键入更多)) #'用户/foo 用户=>(foo 1 2 3 4) clojure.lang.ArraySeq 用户=>(应用foo(重复4-1)) clojure.lang.Cons 虽然作为对位,但以下作品让我感到困惑:

user=> (take 10 (apply concat (repeat [1 2 3 4]))) (1 2 3 4 1 2 3 4 1 2) user=>(取10(应用concat(重复[1234])) (1 2 3 4 1 2 3 4 1 2)
无需展平,只需使用mapcat:(partition all 3(mapcat seq str coll))ataggart和Jürgen来浓缩字符序列,非常感谢这些解决方案:映射到seq正是我所缺少的。克服了这个障碍,我意识到分区并不像我希望的那样懒惰。虽然每个分区都是以惰性方式提供的,但是每个分区的各个组件都不是;因此,在分隔符处对初始文件进行分区并不能提供所需的惰性字符串,而这些字符串将被送入该文件。@Jürgen:mapcat不是惰性的(它使用apply),因此我没有使用它。@ataggart:Nope,它是!只要检查:(键入(mapcat-seq-str-coll))为什么要应用防止懒惰?@Jürgen:mapcat返回一个懒惰的seq,但它是如何产生的并不完全是懒惰的。更多信息,请参阅我的附加“答案”。这里没有“获取所有这些n个懒序列”。concat使用“惰性参数列表”调用。在我们的示例中,您可以通过将字符串集合设置为无限延迟列表来检查这一点:(def str coll(repeat“abcd”)),然后只获取部分结果:(take 10(partition all 3(mapcat seq str coll)))请参阅我文章底部的编辑,以获得我声明的更多证据。此外,concat不会使用“惰性参数列表”。没有这样的东西。所有函数调用都是通过向函数传递离散数量的参数来实现的。您可以在IFn的调用方法中看到这一点:函数声明(例如,[&coll])仅仅意味着要包装的参数是(非惰性的)尽管你关于应用无穷级数的观点让我感到困惑。 user=> (take 10 (apply concat (repeat [1 2 3 4]))) (1 2 3 4 1 2 3 4 1 2)