Michael May
Michael May

Reputation: 376

Calling an overloaded function in recursive function in Swift

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

Answers (2)

MANIAK_dobrii
MANIAK_dobrii

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

Alexander
Alexander

Reputation: 63264

There's a few things to note:

  1. The most immediate problem here is that you're force unwrapping newArray, which is an optional var that you initialize with nil, but never assign a non-nil value.
  2. You have to overloads accumulate, but they can be merged with a generic.
  3. Your accumulate is actually map. The term "accumulate" is usually associated with the reduce/fold operation.
  4. Your appendTo(_:) function is wonky.
    1. According to Swift's naming conventions, it should be 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(_:).
    2. It doesn't add much value over just using the + operator directly
  5. Your recurse 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

Related Questions