在 Swift 中调用递归函数中的重载函数

Calling an overloaded function in recursive function in Swift

我正在做一个在线学习练习 Swift。以下代码是一个单独的测试用例,用于显示调用在递归情况下调用的函数的重载版本的问题。

import Foundation

//Solution goes in Sources
extension Array where Element: Comparable {
    func accumulate (_ acc: (Element) throws -> Element) rethrows -> [Element] {
        var newArray : [Element]?
        
        try self.forEach{try newArray?.append(acc([=11=]))}
        
        return newArray!
    }
    func accumulate (_ acc: (Element) throws -> [Element]) rethrows -> [[Element]] {
        var newArray : [[Element]]?
        
        try self.forEach{try newArray?.append(acc([=11=]))}
        
        return newArray!
    }
}
let input =   ["a", "b", "c"]
let expected = [
    ["a1", "a2", "a3"],
    ["b1", "b2", "b3"],
    ["c1", "c2", "c3"]
] // The expected result of the statement on the bottom of this code
func recurse(_ input: String) -> [String] {
    func appendTo(_ innerInput: String) -> String {
        return input+innerInput
    }
    let result = ["1", "2", "3"].accumulate(appendTo)
    print("3")
    return result
}
let result = input.accumulate(recurse)

当运行时,编译器没有报错,但运行时显示如下错误信息:

Fatal error: Unexpectedly found nil while unwrapping an Optional value
Current stack trace:
0    libswiftCore.so                    0x00007fa0d799c920 _swift_stdlib_reportFatalError + 69
1    libswiftCore.so                    0x00007fa0d78aaa06 <unavailable> + 3279366
2    libswiftCore.so                    0x00007fa0d78aad85 <unavailable> + 3280261
3    libswiftCore.so                    0x00007fa0d76d2810 _fatalErrorMessage(_:_:file:line:flags:) + 19
4    main                               0x000055df0a54ef3a <unavailable> + 7994
5    main                               0x000055df0a54e63e <unavailable> + 5694
6    libc.so.6                          0x00007fa0d6052ab0 __libc_start_main + 231
7    main                               0x000055df0a54e09a <unavailable> + 4250
exited, illegal instruction

请告诉我我的代码有什么问题并解释原因。

非常感谢!

您遇到的问题与超载本身无关。您在尝试强制展开的每个变体中只有 newArray = nil,例如:

func accumulate (_ acc: (Element) throws -> Element) rethrows -> [Element] {
    var newArray : [Element]? // here, newArray = nil
    
    // newArray is still nil, and newArray?.append does nothing
    try self.forEach{try newArray?.append(acc([=10=]))}
    
    // new array is still nil, and you force unwrap nil, hence Unexpectedly found nil while unwrapping an Optional value
    return newArray!
}

要解决这个问题,您可以在开始时为 newArray 分配一些值:

var newArray : [Element] = []

如果您仍在学习 swift,我建议采用“永远不要强制展开(除非完全必要)”规则。此外,我建议也不要强制转换。您几乎总是可以不费力地重写代码 unwrap/cast.

有几点需要注意:

  1. 这里最直接的问题是你要强制解包 newArray,这是一个你用 nil 初始化的可选变量,但永远不会分配 non-nil 值。
  2. 您必须重载 accumulate,但它们可以与泛型合并。
  3. 你的 accumulate 实际上是 map。术语“累积”通常与 reduce/fold 操作相关联。
  4. 您的 appendTo(_:) 功能不可靠。
    1. 根据Swift的命名规则,应该是append(to:)。但这没有意义,因为 arg 不是附加到的内容,而是附加的内容。我建议简单地命名它 append(_:).
    2. 它并没有比直接使用 + 运算符增加多少价值
  5. 你的 recurse 函数实际上并没有递归。

我会这样写:

import Foundation

//Solution goes in Sources
extension Array where Element: Comparable {
    func myMap<T>(_ transform: (Element) throws -> T) rethrows -> [T] {
        var newArray = [T]()
        newArray.reserveCapacity(self.count)
        
        for element in self { 
            try newArray.append(transform(element))
        }
        
        return newArray
    }
}

let input = ["a", "b", "c"]
let expected = [
    ["a1", "a2", "a3"],
    ["b1", "b2", "b3"],
    ["c1", "c2", "c3"],
]

func nameMeBetter(_ input: String) -> [String] {
    return ["1", "2", "3"].myMap { input + [=10=] }
}

let result = input.myMap(nameMeBetter)
print(result)

由于myMap(_:)与内置的Sequence.map(_:)一样,我们可以直接使用它。

所有这些代码简单地归结为:


let input = ["a", "b", "c"]
let expected = [
    ["a1", "a2", "a3"],
    ["b1", "b2", "b3"],
    ["c1", "c2", "c3"],
]

let result = input.map { char in
    ["1", "2", "3"].map { digit in
        char + digit
    }
}
print(result)