Reputation: 376
I was doing an online exercise for learing Swift. The following code is a test case singled out to show the issue with calling an overloaded version of a function that is called in a recursive situation.
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)
When run, the compiler didn't complain, but the runtime showed the following error message:
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
Please kindly tell me what wrong there is with my code and explain the reason.
Thank you very much!
Upvotes: 0
Views: 108
Reputation: 6032
The issue you're experiencing has nothing to do with overloading per se. You just have newArray = nil
in every variant that you try to force unwrap, for example:
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!
}
To fix that you can just assign some value to newArray
in the beginning:
var newArray : [Element] = []
If you're still learning swift, I would suggest to adopt the "never force unwrap (unless completely necessary)" rule. Additionally, I would suggest to never force cast as well. You can almost always rewrite your code without force unwrap/cast.
Upvotes: 1
Reputation: 63264
There's a few things to note:
newArray
, which is an optional var that you initialize with nil, but never assign a non-nil value.accumulate
, but they can be merged with a generic.accumulate
is actually map
. The term "accumulate" is usually associated with the reduce
/fold
operation.appendTo(_:)
function is wonky.
append(to:)
. But that doesn't make sense, because the arg isn't what's being appended to, it's what's being appended. I would suggest naming it simply append(_:)
.+
operator directlyrecurse
function doesn't ever actually recurse.Here's how I would write this:
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)
Since myMap(_:)
is just the same as the built in Sequence.map(_:)
, we can use that directly.
All this code boils down to simply:
let input = ["a", "b", "c"]
let expected = [
["a1", "a2", "a3"],
["b1", "b2", "b3"],
["c1", "c2", "c3"],
]
let result = input.map { char in
["1", "2", "3"].map { digit in
char + digit
}
}
print(result)
Upvotes: 1