Swift Generic Variables致命错误:在展开可选值时意外发现nil



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

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])

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

致命错误:在展开可选值时意外发现nil

我想这是因为我没有给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语句永远不会求值为true。

这里有一种替代的编写方法,可以解释空数组的可能性:

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")
    }
}

或者,您可以让函数返回一个可选的,而不是fatalError,如果序列为空,则返回nil。在Swift 2.0中,minElementmaxElement方法从前者转换为后者。

考虑到@AirspeedVelocity所做的所有考虑都是正确的,并且他的解决方案非常有效,如果你喜欢更紧凑但更神秘的代码,你可以用以下代码替换他的if分支:

return dropFirst(numbers_array).reduce((numbers_array[0], numbers_array[0])) {
    ($1 < $0.0 ? $1 : $0.0, $1 > $0.1 ? $1 : $0.1)
}

其使用CCD_ 19方法将数组迭代地变换为2个元素的元组。

dropFirst丢弃数组的第一个元素,并将reduce方法应用于生成的数组。

reduce方法采用两个参数:

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

最新更新