嵌套函数使 Swift 编译器崩溃
Nested function crashing the Swift compiler
我写了一个递归mergeSort
函数:
func mergeSort<T: Comparable>(inout array: [T]) {
if array.count <= 1 {
return
}
var leftSlice = [T](array[0..<array.count / 2])
var rightSlice = [T](array[array.count / 2...array.endIndex - 1])
mergeSort(&leftSlice)
mergeSort(&rightSlice)
array = merge(leftSlice, rightSlice)
}
func merge<T: Comparable>(var left: [T], var right: [T]) -> [T] {
var mergedValues = [T]()
while !left.isEmpty && !right.isEmpty {
mergedValues.append(left.first! < right.first! ? left.removeAtIndex(0) : right.removeAtIndex(0))
}
if !left.isEmpty {
mergedValues += left
} else if !right.isEmpty {
mergedValues += right
}
return mergedValues
}
现在,由于 merge()
只能由 mergeSort()
使用,我将它放在 mergeSort()
中,因此使 merge()
成为 nested function:
func mergeSort<T: Comparable>(inout array: [T]) {
func merge<T: Comparable>(var left: [T], var right: [T]) -> [T] {
var mergedValues = [T]()
while !left.isEmpty && !right.isEmpty {
mergedValues.append(left.first! < right.first! ? left.removeAtIndex(0) : right.removeAtIndex(0))
}
if !left.isEmpty {
mergedValues += left
} else if !right.isEmpty {
mergedValues += right
}
return mergedValues
}
if array.count <= 1 {
return
}
var leftSlice = [T](array[0..<array.count / 2])
var rightSlice = [T](array[array.count / 2...array.endIndex - 1])
mergeSort(&leftSlice)
mergeSort(&rightSlice)
array = merge(leftSlice, rightSlice)
}
您不需要将 merge
设为通用函数。通用 T
已经为 mergeSort
定义,因此您只需将 [T]
设置为内部函数中的参数:
func merge(var left: [T], var right: [T]) -> [T] {
var mergedValues = [T]()
...
}
您似乎在编译器中发现了一个与嵌套泛型函数相关的错误。这是一个也会使 1.2 编译器崩溃的缩减:
func f<T>(t: T) {
func g<U>(u: U) { }
}
但在这种情况下,您实际上不需要 merge
的通用版本。它的通用参数与外部函数相同,因此只需使用:
func mergeSort<T: Comparable>(inout array: [T]) {
// no generic placeholder needed, T is the T for mergeSort
func merge(var left: [T], var right: [T]) -> [T] {
// etc.
}
}
这似乎工作正常。
但是,还值得指出的是,在您的 merge
函数中,您在循环中调用 removeAtIndex
,这是一个 O(n) 功能。这意味着您的归并排序不会具有预期的复杂性。
这里有一个可供考虑的替代版本:
func mergeSort<T: Comparable>(inout array: [T], range: Range<Int>? = nil) {
func merge(left: Range<Int>, right: Range<Int>) -> [T] {
var tmp: [T] = []
tmp.reserveCapacity(count(left) + count(right))
var l = left.startIndex, r = right.startIndex
while l != left.endIndex && r != right.endIndex {
if array[l] < array[r] {
tmp.append(array[l++])
}
else {
tmp.append(array[r++])
}
}
// where left or right may be empty, this is a no-op
tmp += source[l..<left.endIndex]
tmp += source[r..<right.endIndex]
return tmp
}
// this allows the original caller to omit the range,
// the default being the full array
let r = range ?? indices(array)
if count(r) > 1 {
let mid = r.startIndex.advancedBy(r.startIndex.distanceTo(r.endIndex)/2)
let left = r.startIndex..<mid
let right = mid..<r.endIndex
mergeSort(&array, range: left)
mergeSort(&array, range: right)
let merged = merge(left, right)
array.replaceRange(r, with: merged)
}
}
我还要说的是,由于 merge
本身可能是一个通用的有用函数,您最好将其独立而不是嵌套(类似地,partition
当实施快速排序)。嵌套不会给你带来任何好处(除了我上面使用的从内部引用外部参数的技巧之外,无论如何这可能是一种不好的做法,我这样做主要是为了向你展示:)
我写了一个递归mergeSort
函数:
func mergeSort<T: Comparable>(inout array: [T]) {
if array.count <= 1 {
return
}
var leftSlice = [T](array[0..<array.count / 2])
var rightSlice = [T](array[array.count / 2...array.endIndex - 1])
mergeSort(&leftSlice)
mergeSort(&rightSlice)
array = merge(leftSlice, rightSlice)
}
func merge<T: Comparable>(var left: [T], var right: [T]) -> [T] {
var mergedValues = [T]()
while !left.isEmpty && !right.isEmpty {
mergedValues.append(left.first! < right.first! ? left.removeAtIndex(0) : right.removeAtIndex(0))
}
if !left.isEmpty {
mergedValues += left
} else if !right.isEmpty {
mergedValues += right
}
return mergedValues
}
现在,由于 merge()
只能由 mergeSort()
使用,我将它放在 mergeSort()
中,因此使 merge()
成为 nested function:
func mergeSort<T: Comparable>(inout array: [T]) {
func merge<T: Comparable>(var left: [T], var right: [T]) -> [T] {
var mergedValues = [T]()
while !left.isEmpty && !right.isEmpty {
mergedValues.append(left.first! < right.first! ? left.removeAtIndex(0) : right.removeAtIndex(0))
}
if !left.isEmpty {
mergedValues += left
} else if !right.isEmpty {
mergedValues += right
}
return mergedValues
}
if array.count <= 1 {
return
}
var leftSlice = [T](array[0..<array.count / 2])
var rightSlice = [T](array[array.count / 2...array.endIndex - 1])
mergeSort(&leftSlice)
mergeSort(&rightSlice)
array = merge(leftSlice, rightSlice)
}
您不需要将 merge
设为通用函数。通用 T
已经为 mergeSort
定义,因此您只需将 [T]
设置为内部函数中的参数:
func merge(var left: [T], var right: [T]) -> [T] {
var mergedValues = [T]()
...
}
您似乎在编译器中发现了一个与嵌套泛型函数相关的错误。这是一个也会使 1.2 编译器崩溃的缩减:
func f<T>(t: T) {
func g<U>(u: U) { }
}
但在这种情况下,您实际上不需要 merge
的通用版本。它的通用参数与外部函数相同,因此只需使用:
func mergeSort<T: Comparable>(inout array: [T]) {
// no generic placeholder needed, T is the T for mergeSort
func merge(var left: [T], var right: [T]) -> [T] {
// etc.
}
}
这似乎工作正常。
但是,还值得指出的是,在您的 merge
函数中,您在循环中调用 removeAtIndex
,这是一个 O(n) 功能。这意味着您的归并排序不会具有预期的复杂性。
这里有一个可供考虑的替代版本:
func mergeSort<T: Comparable>(inout array: [T], range: Range<Int>? = nil) {
func merge(left: Range<Int>, right: Range<Int>) -> [T] {
var tmp: [T] = []
tmp.reserveCapacity(count(left) + count(right))
var l = left.startIndex, r = right.startIndex
while l != left.endIndex && r != right.endIndex {
if array[l] < array[r] {
tmp.append(array[l++])
}
else {
tmp.append(array[r++])
}
}
// where left or right may be empty, this is a no-op
tmp += source[l..<left.endIndex]
tmp += source[r..<right.endIndex]
return tmp
}
// this allows the original caller to omit the range,
// the default being the full array
let r = range ?? indices(array)
if count(r) > 1 {
let mid = r.startIndex.advancedBy(r.startIndex.distanceTo(r.endIndex)/2)
let left = r.startIndex..<mid
let right = mid..<r.endIndex
mergeSort(&array, range: left)
mergeSort(&array, range: right)
let merged = merge(left, right)
array.replaceRange(r, with: merged)
}
}
我还要说的是,由于 merge
本身可能是一个通用的有用函数,您最好将其独立而不是嵌套(类似地,partition
当实施快速排序)。嵌套不会给你带来任何好处(除了我上面使用的从内部引用外部参数的技巧之外,无论如何这可能是一种不好的做法,我这样做主要是为了向你展示:)