ChatGPT解决这个技术问题 Extra ChatGPT

Passing an array to a function with variable number of args in Swift

In The Swift Programming Language, it says:

Functions can also take a variable number of arguments, collecting them into an array. func sumOf(numbers: Int...) -> Int { ... }

When I call such a function with a comma-separated list of numbers (`sumOf(1, 2, 3, 4), they are made available as an array inside the function.

Question: what if I already have an array of numbers that I want to pass to this function?

let numbers = [1, 2, 3, 4]
sumOf(numbers)

This fails with a compiler error, “Could not find an overload for '__conversion' that accepts the supplied arguments”. Is there a way to turn an existing array into a list of elements that I can pass to a variadic function?

Why not just configure the function to take an integer array instead?
Sure, but I may not be the author of the function and may not be able to (or want to) change it.
Ach @OleBegemann here I am, not being able to change the function and reading your question... Still now solution... greetings to Berlin

H
Hasaan Ali

Splatting is not in the language yet, as confirmed by the devs. Workaround for now is to use an overload or wait if you cannot add overloads.


Is Splatting in the language yet? I'm trying to call sumOf(...numbers)
So disappointing! I hit this even with something as simple as trying to delegate my own log calls down to print!
L
Logan

Here's a work around that I found. I know it's not exactly what you want, but it seems to be working.

Step 1: Declare the function you'd like with an array instead of variadic arguments:

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

Step 2: Call this from within your variadic function:

func sumOf(numbers: Int...) -> Int {
    return sumOf(numbers)
}

Step 3: Call Either Way:

var variadicSum = sumOf(1, 2, 3, 4, 5)
var arraySum = sumOf([1, 2, 3, 4, 5])

It seems strange, but it is working in my tests. Let me know if this causes unforeseen problems for anyone. Swift seems to be able to separate the difference between the two calls with the same function name.

Also, with this method if Apple updates the language as @manojid's answer suggests, you'll only need to update these functions. Otherwise, you'll have to go through and do a lot of renaming.


Thanks, I like the workaround. I'll still award the "correct" answer to manojlds for finding the link to the official confirmation that the feature is not available yet. I hope you understand.
You are probably following the Guide and your func sumOf(numbers: [Int]) -> Int is actually calculating the average
G
Guoye Zhang

You can cast the function:

typealias Function = [Int] -> Int
let sumOfArray = unsafeBitCast(sumOf, Function.self)
sumOfArray([1, 2, 3])

Great! This works with multiple (named) parameters, too; e.g.: func sumOf(foo numbers: Int..., bar: Bool) -> Int {}; requires typealias Function = (foo: [Int], bar: Bool) -> Int;
Great! save my life.
How can I do the similar things with AnyObject... ? stackoverflow.com/questions/42016358/… Thanks.
This is a very bad idea. There is absolutely zero guarantee that this will work.
@MattMc Sure. As far as I can tell there is nothing that would permit you to simply cast one callable type to another using unsafeBitCast. This may work today, but unless a reference says so, the next version of the compiler is free to literally do anything here (compiler error/crash/randomly executing code...). Have a look at the serious-looking warning on unsafeBitCast.
G
GoZoner

You can use a helper function as such:

func sumOf (numbers : [Int])  -> Int { return numbers.reduce(0, combine: +) }
func sumOf (numbers : Int...) -> Int { return sumOf (numbers) }

Provided there is a version for arrays. I thought the premise of the question is that the original function takes Int... and can't (easily) be changed?
@delnan: Correct. It seems to me that there should be way to pass an array into a variadic function, given that the variadic arguments are turned into an array anyway.
Languages that accept variable number of arguments in functions, like Scheme, have an apply procedure. I gather some people call that 'splatting'.
What is sumArray referenced here?
s
schirrmacher

I did this (Wrapper + Identity Mapping):

func addBarButtonItems(types: REWEBarButtonItemType...) {
    addBarButtonItems(types: types.map { $0 })
}

func addBarButtonItems(types: [REWEBarButtonItemType]) {
    // actual implementation
}

g
gregthegeek

I know this response does not answer your exact question, but I feel its worth noting. I too was starting to play with Swift and immediately ran into a similar question. Manojlds answer is better for your question, I agree, but again, another workaround I came up with. I do happen to like Logan's better too.

In my case I just wanted to pass an array:

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

var someNums = [8,7,2,9,12]
sumOf(someNums)
sumOf([10, 15, 20])

Just wanted to share, in case anyone else was thinking like me. Most of the time I would prefer pass the array like this, but I don't think the "Swiftly" yet. :)


i
iUrii

Swift 5

This is an approach with @dynamicCallable feature that allows to avoid overloading or unsafeBitCast but you should make a specific struct to call:

@dynamicCallable
struct SumOf {
    func dynamicallyCall(withArguments args: [Int]) -> Int {
        return args.reduce(0, +)
    }
}

let sum = SumOf()

// Use a dynamic method call.
sum(1, 2, 3) // 6

// Call the underlying method directly.
sum.dynamicallyCall(withArguments: [1, 2, 3]) // 6

You could also add another method to struct: func dynamicallyCall(withKeywordArguments args: KeyValuePairs) -> Int { let values = args.first?.value ?? [Int]() return values.reduce(0, +) } You would then have the option to call with an argument label (any will work). Ex: sum(arr: [1,2,3])

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now