我目前正在为Clojure中的Euler项目问题(即Eratosthenes筛网(https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes)之一实施解决方案。这是我的代码:
(defn cross-first-element [coll]
(filter #(not (zero? (rem % (first coll)))) coll))
(println
(last
(map first
(take-while
(fn [[primes sieve]] (not (empty? sieve)))
(iterate
(fn [[primes sieve]] [(conj primes (first sieve)) (cross-first-element sieve)])
[[] (range 2 2000001)])))))
基本思想是拥有两个集合-已经从筛子中检索到的素数和其余的筛子本身。我们从空的primes
开始,直到筛子为空,我们选择其第一个元素并将其附加到primes
,然后从筛子中删除它的倍数。用尽后,我们知道素数中的所有素数都在200万以下。
不幸的是,尽管它可以用于筛子的较小上限(例如1000),但它会导致java.lang.StackOverflowError
的堆栈跟踪很长,重复序列为:
...
clojure.lang.RT.seq (RT.java:531)
clojure.core$seq__5387.invokeStatic (core.clj:137)
clojure.core$filter$fn__5878.invoke (core.clj:2809)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:51)
...
解决方案中的概念错误在哪里?如何解决?
答案 0 :(得分:1)
其原因如下:由于import firebase_admin
from firebase_admin import credentials
from firebase_admin import db
myvalue = {"apple" : "red", "orange": "yellow"}
for i in xrange(100):
db.reference('mykey/').set(myvalue)
myvalue["orange"] = "orange"
中的filter
函数是惰性的,因此它实际上并不会在每个cross-first-element
步骤中过滤您的集合,而是“堆叠” '过滤函数调用。这就导致了这样一种情况,当您实际需要结果元素时,将执行测试功能的全部负载,大致如下:
iterate
导致堆栈溢出。
您所遇到的最简单的解决方案是使过滤变得更加渴望。您只需使用(#(not (zero? (rem % (first coll1))))
(#(not (zero? (rem % (first coll2))))
(#(not (zero? (rem % (first coll3))))
;; and 2000000 more calls
而不是filterv
即可,也可以将其包装到filter
但是您的解决方案仍然很慢。我宁愿为此使用循环和本机数组。
答案 1 :(得分:0)
您已经(重新)发现嵌套的惰性序列有时可能会出现问题。 Here is one example可能出问题的地方(这是不直观的)。
如果您不介意使用库,则通过在命令性循环周围使用一个惰性包装器可以简化问题。这就是lazy-gen
和yield
给你的东西(Python中的“生成器”):
(ns tst.demo.core
(:use demo.core tupelo.test)
(:require [tupelo.core :as t]))
(defn unprime? [primes-so-far candidate]
(t/has-some? #(zero? (rem candidate %)) primes-so-far))
(defn primes-generator []
(let [primes-so-far (atom [2])]
(t/lazy-gen
(t/yield 2)
(doseq [candidate (drop 3 (range))] ; 3..inf
(when-not (unprime? @primes-so-far candidate)
(t/yield candidate)
(swap! primes-so-far conj candidate))))))
(def primes (primes-generator))
(dotest
(is= (take 33 primes)
[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 ])
; first prime over 10,000
(is= 10007 (first (drop-while #(< % 10000) primes)))
; the 10,000'th prime (https://primes.utm.edu/lists/small/10000.txt)
(is= 104729 (nth primes 9999)) ; about 12 sec to compute
)
我们还可以使用loop/recur
来控制循环,但是使用atom
来保持状态更容易阅读。
除非您真的真的需要一个懒惰且无限的解决方案 ,否则命令式解决方案要简单得多:
(defn primes-upto [limit]
(let [primes-so-far (atom [2])]
(doseq [candidate (t/thru 3 limit)]
(when-not (unprime? @primes-so-far candidate)
(swap! primes-so-far conj candidate)))
@primes-so-far))
(dotest
(is= (primes-upto 100)
[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]) )