Swift Generic Variables fatal error: unexpectedly found nil while unwrapping an Optional value

Swift Generic Variables fatal error: unexpectedly found nil while unwrapping an Optional value

我正在尝试实现一个简单的通用方法:

func findMax<T: Comparable>(numbers_array : [T]) -> ( min_tuple : T , max_tuple : T )
{
    var max_number : T?
    var min_number : T?

    for i in numbers_array
    {
        if(max_number < i)
        {
            max_number = i
        }
        if(min_number > i)
        {
            min_number = i
        }
    }
    return (min_number! , max_number!)
}

我正在尝试访问这样的方法:

let result = findMax([3.4, 5, -7, -7.8])

但是每当我 运行 这段代码时,我都会收到以下错误:

fatal error: unexpectedly found nil while unwrapping an Optional value

我想那是因为我还没有给 var max_number : T?var min_number 赋值: T ?

我不能给它们赋值,因为它们是通用类型,我认为垃圾值扰乱了这个函数的逻辑……我可能是错的,但这就是我能够做到的从我的调试会话中评估。

提前致谢,非常感谢任何帮助。

Swift 与其他一些基于 C 的语言的不同之处在于它不会让您使用可能包含垃圾的未初始化变量。如果声明但不初始化变量,然后在保证初始化之前使用它(基于可能的代码路径),则会出现编译器错误。

但是 var 可选值被隐式初始化为 nil。所以这里的问题是你最后强制解包(例如 min_number!)。有些代码路径会导致 nil 未重新分配给实际值,然后当您使用 ! 解包 nil 时会出现错误。

原因是这里的这一行:

if(min_number > i)

有一个 > 版本接受可选值。如果这两个值都是非 nil,则比较它们。但如果一个是 nil,它总是 小于非 nil 版本。所以在你的循环中,你从 min_number 作为 nil 开始,所以你的 if 语句永远不会计算为真。

这是考虑到空数组可能性的另一种写法:

func findMax<T: Comparable>(numbers_array : [T]) -> ( min_tuple : T , max_tuple : T )
{
    if let first_number = numbers_array.first {
        var min_number = first_number, max_number = first_number
        for i in dropFirst(numbers_array) {
            max_number = max(max_number, i)
            min_number = min(min_number, i)
        }
        return (min_number,max_number)
    }
    else {
        // the user has passed in an empty array... 
        // so there is no minimum, you must take some 
        // action like:
        fatalError("empty array passed")
    }
}

或者,您可以将函数 return 设置为可选函数,而不是 fatalError,如果序列为空,则使用 nil。在 Swift 2.0 中,minElementmaxElement 方法从前者切换到后者。

鉴于@AirspeedVelocity 所做的所有考虑都是正确的,并且他的解决方案效果很好,如果您喜欢更紧凑但更神秘的代码,您可以将他的 if 分支替换为以下代码:

return dropFirst(numbers_array).reduce((numbers_array[0], numbers_array[0])) {
    ( < [=10=].0 ?  : [=10=].0,  > [=10=].1 ?  : [=10=].1)
}

它使用 reduce 方法迭代地将数组转换为 2 个元素的元组。

dropFirst丢弃数组的第一个元素,reduce方法应用于结果数组。

reduce方法有两个参数:

  • 初始值,在本例中是一个二维元组,两个元素都设置为数组的第一个元素
  • 一个闭包,应用于数组的每个元素,其中 returns 一个新的元组,通过验证数组元素是否小于 than/greater 相应的元组元素来确定。