在Clojure中,懒惰的seq总是分块吗?
我的印象是懒惰的seq总是被分块在Clojure中,懒惰的seq总是分块吗?,clojure,lazy-evaluation,chunking,lazy-sequences,Clojure,Lazy Evaluation,Chunking,Lazy Sequences,我的印象是懒惰的seq总是被分块 => (take 1 (map #(do (print \.) %) (range))) (................................0) 正如预期的那样,打印32个点,因为range返回的惰性seq被分为32个元素块。但是,当我用自己的函数获取rss提要代替范围尝试此操作时,惰性seq不再分块: => (take 1 (map #(do (print \.) %) (get-rss-feeds r))) (."http://
=> (take 1 (map #(do (print \.) %) (range)))
(................................0)
正如预期的那样,打印32个点,因为range
返回的惰性seq被分为32个元素块。但是,当我用自己的函数获取rss提要
代替范围
尝试此操作时,惰性seq不再分块:
=> (take 1 (map #(do (print \.) %) (get-rss-feeds r)))
(."http://wholehealthsource.blogspot.com/feeds/posts/default")
只打印了一个点,所以我猜getrssfeeds
返回的惰性seq没有分块。事实上:
=> (chunked-seq? (seq (range)))
true
=> (chunked-seq? (seq (get-rss-feeds r)))
false
以下是获取rss提要的源代码:
(defn get-rss-feeds
"returns a lazy seq of urls of all feeds; takes an html-resource from the enlive library"
[hr]
(map #(:href (:attrs %))
(filter #(rss-feed? (:type (:attrs %))) (html/select hr [:link])))
因此,看起来块度取决于懒惰seq的生成方式。我查看了函数范围的源代码,有迹象表明它是以一种“粗块”的方式实现的。所以我有点搞不清楚这是怎么回事。有人能澄清一下吗
这就是我需要知道的原因
我必须执行以下代码:(获取rss条目(获取rss提要h-res)url)
调用getrssfeeds
返回我需要检查的提要的一个惰性url序列
调用get rss entry
查找特定条目(其:link字段与get rss entry的第二个参数匹配)。它检查getrssfeeds
返回的惰性序列。评估每个项目需要通过网络发出http请求以获取新的rss提要。为了尽量减少http请求的数量,必须逐个检查序列,并在匹配时立即停止
代码如下:
(defn get-rss-entry
[feeds url]
(ffirst (drop-while empty? (map #(entry-with-url % url) feeds))))
带有url的条目
返回延迟的匹配序列,如果没有匹配,则返回空序列
我对此进行了测试,它似乎工作正常(一次评估一个提要url)。但我担心的是,在某个地方,不知何故,它会开始以一种“笨重”的方式运行,并开始一次评估32个提要。我知道有办法,但在这种情况下似乎根本不需要
我是否非惯用地使用lazy seq?循环/重复是一个更好的选择吗?依赖于组块的模糊性似乎是不明智的,正如你上面提到的。在您确实需要不分块的情况下,显式地“取消分块”也是明智的,因为如果在某个其他点您的代码以分块化的方式更改,那么事情就不会中断。另一方面,如果您需要按顺序执行操作,代理是一个很好的工具,您可以将下载功能发送给代理,然后无论您如何评估该功能,它们都将一次运行一个,并且只运行一次。在某些情况下,您可能希望pmap
您的序列,然后即使取消分块也无法工作,尽管使用atom仍能正常工作 惰性序列并不总是分块的,这取决于它们是如何产生的
例如,此函数生成的惰性seq不分块:
(defn integers-from [n]
(lazy-seq (cons n (do (print \.) (integers-from (inc n))))))
(take 3 (integers-from 3))
=> (..3 .4 5)
但许多其他clojure内置函数确实会出于性能原因(例如)生成分块的seq,您的担心是对的。如果feeds
参数是一个返回分块seq的集合,那么您的getrss条目
确实会调用带有url的条目
。例如,如果feeds
是一个向量,map
将一次对整个块进行操作
Fogus的《Clojure之乐》直接解决了这个问题,第12章定义了功能seq1
:
(defn seq1 [s]
(lazy-seq
(when-let [[x] (seq s)]
(cons x (seq1 (rest s))))))
您可以在您知道您想要尽可能多的惰性的地方使用此选项,就在您使用url调用条目之前:
(defn get-rss-entry
[feeds url]
(ffirst (drop-while empty? (map #(entry-with-url % url) (seq1 feeds)))))
(defn获取rss条目
[订阅源url]
(ffirst(空时删除)(映射#(url为%url的条目)(seq1提要')))
如果您使用clojure.core
中的各种块函数和/或您的序列实现IChunk
和IChunkedSeq
接口,则序列似乎仅被“分块”。目前(在1.4.0中),这些都没有文档记录。您使用的是clojure的哪个版本?能否请您用示例代码的草图对此进行扩展?你是说代理而不是原子吗?你是说代理而不是原子吗?因为提供了交换功能!将重试/atom/agent/g对此表示抱歉。我的手指背叛了我的大脑,按错了键。。。修好了,非常感谢。顺便说一句,我刚看完这本书,把我的Clojure游戏带到了一个新的高度。迫不及待地等待更新版本。值得指出的是,对seq1
的调用必须在源代码处完成。例如,如果您在一个分块序列上接收到来自map
的延迟序列,那么您就不走运了-map
无论您做什么都会向前看。添加map
和filter
都可能产生分块序列,这一点非常重要。将副作用和懒惰混为一谈会产生微妙的错误。我来帮忙。