- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我刚刚学习 Clojure,像往常一样学习新的编程语言,我尝试的第一件事就是实现埃拉托斯特尼筛法。
我想出了以下解决方案:
(defn primes
"Calculate all primes up to the given number"
[n]
(loop
[
result []
numbers (range 2 (inc n))
]
(if (empty? numbers)
result
(let [[next & rest] numbers]
(recur (conj result next) (filter (fn [n] (not= 0 (mod n next))) rest)))
)
)
)
对于较小的数字,它工作得很好并且相当快,但对于较大的输入,会引发 StackOverflowError 并带有可疑的短堆栈跟踪,例如:
(primes 100000)
Execution error (StackOverflowError) at (REPL:1).
null
(pst)
StackOverflowError
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:51)
clojure.lang.RT.seq (RT.java:531)
clojure.core/seq--5387 (core.clj:137)
clojure.core/filter/fn--5878 (core.clj:2809)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:51)
clojure.lang.RT.seq (RT.java:531)
clojure.core/seq--5387 (core.clj:137)
clojure.core/filter/fn--5878 (core.clj:2809)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:51)
=> nil
我的印象是,如果recur最后以循环形式进行评估,那么recur就实现了尾递归,我的第一个问题是这里是否确实如此。我的第二个问题是为什么 StackOverflowError 的堆栈跟踪如此短。我在解释堆栈跟踪时也遇到问题,即。哪一行对应什么形式。
我只对更好或更像 Clojure 的解决方案感兴趣,如果它们为这些问题提供了见解,否则我想自己找到它们。谢谢!
最佳答案
稍微修改一下,用注释来描述每一行发生的事情,这是你的函数:
(defn primes
"Calculate all primes up to the given number"
[n]
;; `loop` is not lazy, it runs until it produces a result:
(loop [result []
;; a lazy sequence implemented by clojure.lang.LongRange:
numbers (range 2 (inc n))]
(if (not (nil? (seq numbers)))
result
(let [current (first numbers)
remaining (rest numbers)]
(recur
;; `conj` on a vector returns a vector (non-lazy):
(conj result current)
;; `filter` on a lazy sequence returns a new lazy sequence:
(filter (fn [n] (not= 0 (mod n next)))
remaining))))))
关键是最后的过滤器
。
大多数惰性序列操作(例如filter
)通过将一个惰性序列包装在另一个惰性序列中来工作。在循环的每次迭代中,filter
添加另一层惰性序列,如下所示:
(filter (fn [n] (not= 0 (mod n 5))) ; returns a LazySeq
(filter (fn [n] (not= 0 (mod n 4))) ; returns a LazySeq
(filter (fn [n] (not= 0 (mod n 3))) ; returns a LazySeq
(filter (fn [n] (not= 0 (mod n 2))) ; returns a LazySeq
remaining))))
LazySeq
对象堆叠在一起,每个对象都保存对前一个对象的引用。
对于大多数惰性序列,包装并不重要,因为一旦您请求一个值,它们就会自动“展开”。这发生在 LazySeq.seq .
这是一种确实很重要的情况,因为您的循环构建了如此多的延迟序列对象层,以至于对 LazySeq.seq
和 的嵌套调用.sval
溢出 JVM 允许的最大堆栈大小。这就是您在堆栈跟踪中看到的内容。
(这也有内存影响,因为对序列开头的引用会阻止任何其他序列被垃圾收集,Clojure 程序员将其称为“保留序列的头部”。)
此函数更常见的问题是混合惰性(过滤器
)和非惰性(循环
)操作。这通常是问题的根源,因此 Clojure 程序员出于习惯而学会避免它。
正如 Alan 所建议的,您可以通过仅使用非惰性操作来避免该问题,例如 filterv
而不是 filter
,这会强制将惰性序列转换为向量。
几乎任何类型的惰性求值都可能表现出此问题的一些变化。我在Clojure don'ts: concat中描述过它。另一个例子请参阅 foldr versus foldl在 haskell 。
即使没有惰性,深度嵌套的对象树也可能导致 StackOverflow,例如在 Java 中我发现 xstream#88或circe#1074 .
关于clojure - 为什么这个 Clojure 素数生成器会引发 StackOverflowError?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60327936/
我有一个使用 play scala 2.0 开发的项目,它工作正常,我需要将版本升级到 2.3.8。所以我通过此链接迁移了我的应用程序版本 https://www.playframework.com/
为此我需要一些帮助。 首先我有一个 BinarySearchTree 类 import java.util.ArrayList; import java.util.List; public class
我正在尝试使用递归方法计算字母“e”在给定字符串中出现的次数。我的测试字符串是 请数我的 e!。这是到目前为止的代码: public static int showE(String s, int co
您将如何调整这个简单的递归示例,以便进行尾调用优化(而不是 StackOverflowError)? count 0 = 0 count n = succ (count (pred n)) count
我根据自身定义流(递归定义)。当试图访问流的第二个元素时,StackOverflowError被抛出。来自Scala控制台的代码: scala> val s1 = Stream.iterate(1)(
我在 Java 中有一个 StackOverflowError,它没有告诉我我自己的代码中的任何一行,堆栈跟踪的相关部分是: java.lang.StringBuilder.append(String
这个隐式 val 如何导致 StackOverFlowError? (削减我的原始代码,仍然导致错误) object Complicit { // a class with name, defau
在 Groovy Console我有这个: import groovy.util.* import org.codehaus.groovy.runtime.* def gse = new Groovy
为什么此代码片段执行会导致 StackOverflowError: lazy val primes: Stream[Int] = 2 #:: Stream.from(3, 2) filter { pc
(reduce concat (repeat 10000 [])) 我知道展平可能是执行此操作的更好方法,但我仍然很好奇为什么这会导致错误。 最佳答案 因为concat产生一个惰性序列。 所以,当你打
当我使用 (avg-bids 4000 10 5) 调用以下 Clojure 代码时,会导致 java.lang.StackOverflowError。我试图找出原因,因为 sum-bids 是作为尾
我在运行递归程序时遇到了 Java StackOverFlowError。程序正确,需要实现递归。我尝试使用命令查找当前堆栈大小 java -XX:+PrintFlagsFinal -vers
美好的一天!运行快速排序算法时,我收到 StackOverflowError 错误。当数组中的元素 > 50 000 时,会发生此错误。 我的代码如下: public void recQuickSor
我正在删除一个 Android 应用程序,其中有一个无限重复的动画,导致 StackOverflowError。当同一对象上的另一个动画开始时,它会执行此操作。 private fun pulse()
我创建了一个公共(public)类PermissionManager来管理来自一个地方的所有权限,通常它工作正常,但上传后它显示崩溃分析的错误报告我无法重现,详细信息是下面提到 Fatal Excep
我得到了一组称为“字典”的字符串,存储为字段,代表单词字典。 我要编写一个方法,它接受一个字符串参数(“短语”)并返回一个包含字典集中所有单词的集合,这些单词可以通过重新排列给定短语中的字符来实现。基
我正在尝试生成一个相对较小(1296 个元素)的向量列表,本质上枚举从 [0 0 0 0] 到 [5 5 5 5] 的 4 个基数 6 数字 [0 0 0 0], [1 0 0 0] ... [5 0
我正在尝试用java编写二进制插入排序。 public static int binarySearch(double[] a, int max, int min, double k) {
我目前正在 Clojure 中实现欧拉项目问题之一的解决方案,即埃拉托斯特尼筛法 ( https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes )。这是我
我遇到了与错误递归和 StackOverflowError 相关的编程问题。我在一个单独的线程中处理了这个案例: public void subscribe(final String channel)
我是一名优秀的程序员,十分优秀!