懒惰序列取决于以前的元素

时间:2011-02-10 09:21:15

标签: functional-programming clojure

学习clojure,试图创建所有素数的懒惰无限序列。 我知道有更高效的算法;我作为POC /课程做的不仅仅是理想的解决方案。

我有一个函数,给定一系列素数,告诉我下一个素数是什么:

(next-prime [2 3 5]) ; result: 7

因此,我的懒惰序列必须将自身传递给此函数,然后获取结果并将其添加到自身。

我的第一次尝试:

(def lazy-primes
  (lazy-cat [2 3 5] (find-next-prime lazy-primes)))

..导致IllegalArgumentException:不知道如何从以下创建ISeq:java.lang.Integer

我的第二次尝试:

(def lazy-primes
  (lazy-cat [2 3 5] [(find-next-prime lazy-primes)]))

..当被问到10个元素时,它给了我[2 3 5 7]。

尝试3:

(def lazy-primes
  (lazy-cat [2 3 5]
    (conj lazy-primes (find-next-prime lazy-primes))))

(take 10 lazy-primes) ; result: (2 3 5 7 2 3 5 7 2 3)

所有这些似乎都应该起作用(或者至少应该工作,前提是前者不起作用)。为什么我得到每个案件的虚假输出?

4 个答案:

答案 0 :(得分:1)

您最初尝试无效的原因:

  1. (find-next-prime lazy-primes)返回一个整数但lazy-cat需要一个序列
  2. [(find-next-prime lazy-primes)]创建一个向量(因此可以选择)但它只在首次访问时被评估一次
  3. conj正在向序列的开头添加新的素数(因为lazy-cat因此lazy-primes返回一个序列)...这可能不是你想要的!它也可能会混淆find-next-prime,具体取决于实现的方式,并且在分块序列周围可能存在一些微妙的问题......
  4. 您可能希望使用以下内容:

    (defn seq-fn [builder-fn num ss] 
      (cons 
        (builder-fn (take num ss)) 
        (lazy-seq (seq-fn builder-fn (inc num) ss))))
    
    (def lazy-primes 
      (lazy-cat [2 3 5] (seq-fn next-prime 3 lazy-primes)))
    

    有点复杂,但基本上我正在做的是使用高阶辅助函数来提供一组参数的闭包,其中包括到目前为止创建的素数的数量,以便它可以递增地生成下一个素数在每一步。

    P.S。因为我相信你知道有更快的算法来生成质数!我假设这主要是作为Clojure中的练习和懒惰序列的使用,在这种情况下一切都很好!但如果你真的关心产生很多素数,我建议你看一下Sieve of Atkin

答案 1 :(得分:1)

或者,您可以使用iterate:内置函数,它懒惰地获取函数的输出并将其再次应用于函数

clojure.core/iterate                                                                                                                                                         
([f x])                                                                                                                                                                
Returns a lazy sequence of x, (f x), (f (f x)) etc.
f must be free of side-effects

为了让它工作,next-prime函数应该将其结果连接到它的输入,并返回连接。

然后你可以调用(取100(iterate list-primes [1]))来得到前100个列表 素数。

答案 2 :(得分:1)

使用next-prime函数,您可以使用以下代码片段生成所有素数的延迟序列:

(def primes (map peek (iterate #(conj % (next-prime %)) [2])))

答案 3 :(得分:0)

您要找的组合是concat + lazy-seq +本地fn。

在Clojure Contrib图书馆中查看Erathostenes'Sieve的实施:https://github.com/richhickey/clojure-contrib/blob/78ee9b3e64c5ac6082fb223fc79292175e8e4f0c/src/main/clojure/clojure/contrib/lazy_seqs.clj#L66

还有一个词:此实现使用更多sophisticated algorithm for the Sieve in a functional language

Clojure的另一个实现可以在Rosetta code中找到。但是,我不喜欢那个,因为它使用原子,你不需要在Clojure中解决这个算法。