Reputation: 1330
I'm trying to implement a simple generic method:
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!)
}
I am trying to access the method like this:
let result = findMax([3.4, 5, -7, -7.8])
But whenever I run this code I get the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
I think thats because I haven't assigned a value to var max_number : T? and var min_number : T?
I can't assign a value to them because they are of generic type and I think the garbage value is messing with the logic of this function... I might be wrong but that is what I have been able to assess from my debugging session.
Thanks in advance, Any help is greatly appreciated.
Upvotes: 3
Views: 360
Reputation: 72820
Given that all considerations made by @AirspeedVelocity are correct, and his solution works great, if you are a fan of more compact but a little more cryptic code you can replace his if
branch with the following code:
return dropFirst(numbers_array).reduce((numbers_array[0], numbers_array[0])) {
($1 < $0.0 ? $1 : $0.0, $1 > $0.1 ? $1 : $0.1)
}
which uses the reduce
method to iteratively transform the array into a tuple of 2 elements.
The dropFirst
discards the first element of the array, and the reduce
method is applied to the resulting array.
The reduce
method takes 2 parameters:
Upvotes: 3
Reputation: 40973
Swift is different to some other C-based languages in that it will not let you use an uninitialized variable that might contain garbage. If you declare but don’t initialize a variable, then use it before it’s guaranteed to be initialized (based on the possible code paths), you get a compiler error.
But var
optionals are implicitly initialized to nil
. So the problem here is your forced unwraps at the end (e.g. min_number!
). There are code paths that result in the nil
s not getting reassigned to a real value, and then when you unwrap the nil
with !
you get an error.
The reason for this is this line here:
if(min_number > i)
There is a version of >
that takes optionals. If the two values are both non-nil
, then it compares them. But if one is nil
, it is always less than a non-nil
version. So in your loop, you start with min_number
as nil
, so your if
statement will never evaluate to true.
Here’s an alternative way of writing it that accounts for the possibility of an empty array:
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")
}
}
Alternatively, instead of fatalError
you could have your function return an optional, with nil
if the sequence was empty. In Swift 2.0 the minElement
and maxElement
methods switched from doing the former to doing the latter.
Upvotes: 4