在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($0))}
        
        return newArray!
    }
    func accumulate (_ acc: (Element) throws -> [Element]) rethrows -> [[Element]] {
        var newArray : [[Element]]?
        
        try self.forEach{try newArray?.append(acc($0))}
        
        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($0))}
    
    // 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] = []

如果您仍在学习敏捷,我建议您采用“绝对不要解包(除非完全必要)”规则。另外,我建议也不要强制转换。您几乎可以随时重写代码,而无需强行拆包/投射。

,

有几件事要注意:

  1. 这里最直接的问题是,您要强制展开newArray,这是一个可选的var,您使用nil进行了初始化,但是从不分配非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 + $0 }
}

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

由于myMap(_:)与内置Sequence.map(_:)相同,因此我们可以直接使用它。

所有这些代码都可以简化为:


let input = ["a",]

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