我实现了一个函数,它将给定输入集合的n元语法作为惰性序列返回。
(defn gen-ngrams
[n coll]
(if (>= (count coll) n)
(lazy-seq (cons (take n coll) (gen-ngrams n (rest coll))))))
当我用更大的输入集合调用此函数时,我预计会看到执行时间的线性增加。但是,我观察到的时间比这更糟糕:
user> (time (count (gen-ngrams 3 (take 1000 corpus))))
"Elapsed time: 59.426 msecs"
998
user> (time (count (gen-ngrams 3 (take 10000 corpus))))
"Elapsed time: 5863.971 msecs"
9998
user> (time (count (gen-ngrams 3 (take 20000 corpus))))
"Elapsed time: 23584.226 msecs"
19998
user> (time (count (gen-ngrams 3 (take 30000 corpus))))
"Elapsed time: 54905.999 msecs"
29998
user> (time (count (gen-ngrams 3 (take 40000 corpus))))
"Elapsed time: 100978.962 msecs"
39998
corpus
是Cons
字符串标记。
导致此行为的原因是什么?如何改善效果?
答案 0 :(得分:5)
我认为你的问题是“(count coll)”,它在每次调用ngrams时迭代了coll。
解决方案是使用build in partition函数:
user=> (time (count (gen-ngrams 3 (take 20000 corpus))))
"Elapsed time: 6212.894932 msecs"
19998
user=> (time (count (partition 3 1 (take 20000 corpus))))
"Elapsed time: 12.57996 msecs"
19998
如果对实现http://clojuredocs.org/clojure_core/clojure.core/partition
感到好奇,请查看分区源代码答案 1 :(得分:0)
我远离Clojure专家,但我认为cons函数会导致这个问题。 请尝试使用列表:
(defn gen-ngrams
[n coll]
(if (>= (count coll) n)
(lazy-seq (list (take n coll) (gen-ngrams n (rest coll))))))
我认为cons构造了一个比列表更通用的新seq,因此速度较慢。
编辑:如果“语料库是字符串标记的缺点”,那么尝试将其设为列表......