此代码将所有整数和number
相加,但它会在Segmentation fault: 11
或更大时与104829
(或内存访问不良)崩溃。为什么呢?
import Foundation
func sigma(_ m: Int64) -> Int64 {
if (m <= 0 ) {
return 0
} else {
return m + sigma(m - 1)
}
}
let number: Int64 = 104829
let answer = sigma(number)
nb: sigma(104828) = 5494507206
在带有8GB Ram的CoreDuo 2 Macbook Pro上的macOS 10.11终端上运行(这是相关的!)
答案 0 :(得分:3)
您正在获得Stack Overflow。您可以使用getrlimit(2)/setrlimit(2)获取/设置当前进程的堆栈大小。以下是一个示例用法:
import Darwin // Unnecessary if you already have Foundation imported
func getStackByteLimit() -> rlimit? {
var limits = rlimit()
guard getrlimit(RLIMIT_STACK, &limits) != -1 else {
perror("Error with getrlimit")
return nil
}
return limits
}
func setStackLimit(bytes: UInt64) -> Bool {
guard let max = getStackByteLimit()?.rlim_max else { return false }
var limits = rlimit(rlim_cur: bytes, rlim_max: max)
guard setrlimit(RLIMIT_STACK, &limits) != -1 else {
perror("Error with setrlimit")
return false
}
return true
}
默认情况下,它是8,388,608
个字节,2,048
字节的4,096
个页面。
你的是一个无法进行尾调用优化的算法的教科书示例。递归调用的结果不是直接返回,而是用作加法的操作数。因此,编译器无法生成代码以在递归期间消除堆栈帧。他们必须留下来,以便跟踪最终需要完成的添加。可以使用累加器参数来改进此算法:
func sigma(_ m: Int64, acc: Int64 = 0) -> Int64 {
if (m <= 0 ) {
return acc
} else {
return sigma(m - 1, acc: acc + m)
}
}
在此代码中,直接返回递归调用的结果。因此,编译器可以编写删除中间堆栈帧的代码。这应该可以防止堆栈溢出。
但实际上,你可以在恒定的时间内完成这项工作,而不需要任何递归的无意义:p
func sum(from start: Int64 = 0, to end: Int64) -> Int64 {
let count = end - start + 1
return (start * count + end * count) / 2
}
print(sum(to: 50))