bearMountain
bearMountain

Reputation: 3980

How to forward functions with variadic parameters?

In Swift, how do you convert an Array to a Tuple?

The issue came up because I am trying to call a function that takes a variable number of arguments inside a function that takes a variable number of arguments.

// Function 1
func sumOf(numbers: Int...) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}
// Example Usage
sumOf(2, 5, 1)

// Function 2
func averageOf(numbers: Int...) -> Int {
    return sumOf(numbers) / numbers.count
}

This averageOf implementation seemed reasonable to me, but it does not compile. It gives the following error when you try to call sumOf(numbers):

Could not find an overload for '__converstion' that accepts the supplied arguments

Inside averageOf, numbers has the type Int[]. I believe sumOf is expecting a Tuple rather than an Array.

Thus, in Swift, how do you convert an Array to a Tuple?

Upvotes: 23

Views: 5676

Answers (4)

Gregory Ling
Gregory Ling

Reputation: 195

I realize this is an older post, but this came up rather high in the search results and I found a working solution.

You can write the sumOf function to accept an array of integers as the number parameter and overload the sumOf function to accept a variadic input for the numbers parameter which will be passed to the first version as an array. This way the averageOf function can pass its variadic input as array to sumOf.

This does not seem very ideal because you need to overload each function that works like this, but it will work in the way you wanted.

func sumOf(numbers: [Int]) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}

// Function 1
func sumOf(numbers: Int...) -> Int {
    return sumOf(numbers: numbers)
}
// Example Usage
sumOf(2, 5, 1)

// Function 2
func averageOf(numbers: Int...) -> Int {
    return sumOf(numbers: numbers) / numbers.count
}

Upvotes: 0

Tony Wan
Tony Wan

Reputation: 21

As of Swift 4.1 (in Xcode 9.2), there is no need to overload with sumOf(_ numbers: Int...), the function that forward variadic parameter(s) will IMPLICITLY change it to a single parameter of array of individual parameter(s). E.g. the following code will work without the overloading:

// This function does the actual work
func sumOf(_ numbers: [Int]) -> Int {
    return numbers.reduce(0, +) // functional style with reduce
}

func averageOf(_ numbers: Int...) -> Int {
    // This calls the first function directly
    return sumOf(numbers) / numbers.count
}

print(averageOf(2, 5, 1))

Don't know whether this is a bug of the compiler or not :)

Upvotes: 0

Jean-Philippe Pellet
Jean-Philippe Pellet

Reputation: 59994

This has nothing to do with tuples. Anyway, it isn't possible to convert from an array to a tuple in the general case, as the arrays can have any length, and the arity of a tuple must be known at compile time.

However, you can solve your problem by providing overloads:

// This function does the actual work
func sumOf(_ numbers: [Int]) -> Int {
    return numbers.reduce(0, +) // functional style with reduce
}

// This overload allows the variadic notation and
// forwards its args to the function above
func sumOf(_ numbers: Int...) -> Int {
    return sumOf(numbers)
}

sumOf(2, 5, 1)

func averageOf(_ numbers: Int...) -> Int {
    // This calls the first function directly
    return sumOf(numbers) / numbers.count
}

averageOf(2, 5, 1)

Maybe there is a better way (e.g., Scala uses a special type ascription to avoid needing the overload; you could write in Scala sumOf(numbers: _*) from within averageOf without defining two functions), but I haven't found it in the docs.

Upvotes: 24

RobertL
RobertL

Reputation: 14874

I don't think he needs to use .reduce. Instead just change the parameter definition in his sumOf function. Instead of:

func sumOf(numbers: Int...) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}

write:

func sumOf(numbers: [Int]) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}

Upvotes: -2

Related Questions