Geo*_*o G 15 clojure lazy-evaluation chunking lazy-sequences
我的印象是懒惰的seqs总是被分块.
=> (take 1 (map #(do (print \.) %) (range)))
(................................0)
Run Code Online (Sandbox Code Playgroud)
正如预期的那样打印32个点,因为返回的lazy seq range
被分成32个元素块.但是,当range
我用自己的函数尝试这个时get-rss-feeds
,懒惰的seq不再是分块的:
=> (take 1 (map #(do (print \.) %) (get-rss-feeds r)))
(."http://wholehealthsource.blogspot.com/feeds/posts/default")
Run Code Online (Sandbox Code Playgroud)
只打印了一个点,所以我猜测返回的lazy-seq get-rss-feeds
没有分块.确实:
=> (chunked-seq? (seq (range)))
true
=> (chunked-seq? (seq (get-rss-feeds r)))
false
Run Code Online (Sandbox Code Playgroud)
以下是来源get-rss-feeds
:
(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])))
Run Code Online (Sandbox Code Playgroud)
因此,看起来粗糙取决于懒惰的seq是如何产生的.我偷看了函数的来源,range
并且有一些暗示它以"矮胖"的方式实现.所以我对它是如何工作有点困惑.有人可以澄清一下吗?
这就是我需要知道的原因.
我必须遵循以下代码: (get-rss-entry (get-rss-feeds h-res) url)
调用get-rss-feeds
返回我需要检查的一系列URL的延迟URL.
调用get-rss-entry
查找特定条目(其中:link字段与get-rss-entry的第二个参数匹配).它检查由返回的惰性序列get-rss-feeds
.评估每个项目需要通过网络提供http请求以获取新的RSS源.为了最小化http请求的数量,重要的是逐个检查序列并在匹配时立即停止.
这是代码:
(defn get-rss-entry
[feeds url]
(ffirst (drop-while empty? (map #(entry-with-url % url) feeds))))
Run Code Online (Sandbox Code Playgroud)
entry-with-url
如果没有匹配,则返回惰性匹配序列或空序列.
我测试了它,它似乎正常工作(一次评估一个提要网址).但是我担心某个地方,某种程度上它会以"粗糙"的方式开始表现,并且它将开始一次评估32个提要.我知道有一种方法可以避免这里讨论的粗短行为,但在这种情况下似乎甚至不需要.
我是否使用懒惰的seq非惯用语?循环/重复是一个更好的选择吗?
Cho*_*ser 11
你是对的.如果参数是一个返回chunked seqs的集合,那么你get-rss-entry
的确会调用entry-with-url
超过严格必要的feeds
.例如,如果feeds
是向量,map
则将一次对整个块进行操作.
这个问题直接在Fogus的" 欢乐的Clojure"中解决,其功能seq1
在第12章中定义:
(defn seq1 [s]
(lazy-seq
(when-let [[x] (seq s)]
(cons x (seq1 (rest s))))))
Run Code Online (Sandbox Code Playgroud)
在你打电话之前,你可以在你知道你想要最懒惰的地方使用这个entry-with-url
:
(defn get-rss-entry [feeds url] (ffirst (drop-while empty? (map #(entry-with-url % url) (seq1 feeds)))))
懒惰的seqs 并不总是分块 - 这取决于它们的生成方式.
例如,此函数生成的lazy seq不会分块:
(defn integers-from [n]
(lazy-seq (cons n (do (print \.) (integers-from (inc n))))))
(take 3 (integers-from 3))
=> (..3 .4 5)
Run Code Online (Sandbox Code Playgroud)
但是出于性能原因(例如范围),许多其他clojure内置函数确实会生成分块序列
正如您上面提到的,取决于分块的模糊性似乎是不明智的。在您确实需要不进行分块的情况下,明确地“取消分块”也是明智的,因为如果在其他时候您的代码以分块的方式进行更改,那么事情就不会中断。另一方面,如果您需要按顺序执行操作,代理是一个很棒的工具,您可以将下载功能发送给代理,然后无论您如何评估该功能,它们都将一次运行一个且仅运行一次。在某些时候,您可能想要pmap
您的序列,然后即使使用原子将继续正常工作,但即使取消分块也不起作用。