我正在尝试实现一个简单的通用方法:
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中,minElement
和maxElement
方法从前者转换为后者。
考虑到@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
方法采用两个参数:
- 初始值,在本例中是一个二维元组,两个元素都设置为数组的第一个元素
- 闭包,应用于数组的每个元素,返回一个新的元组,通过验证数组元素是否小于/大于相应的元组元素来确定