在Swift中将数组传递给具有可变参数的函数

176
《Swift编程语言》中提到:

函数还可以接收可变数量的参数,并将它们收集到一个数组中。

  func sumOf(numbers: Int...) -> Int {
      ...
  }
当我使用逗号分隔的数字列表调用这样一个函数 (`sumOf(1, 2, 3, 4)`),它们会在函数内部作为数组可用。
问题:如果我已经有一个数字数组想要传递给这个函数怎么办?
let numbers = [1, 2, 3, 4]
sumOf(numbers)

这会导致编译错误,“无法找到接受所提供参数的'__conversion'的重载”。是否有一种方法可以将现有的数组转换为元素列表,以便我可以将其传递给可变参数函数?


2
为什么不直接配置函数以取一个整数数组呢? - Mick MacCallum
39
可以,但我可能不是该函数的作者,可能无法(或不想)更改它。 - Ole Begemann
嗨,@OleBegemann,我在这里,无法更改函数并阅读您的问题...仍然没有解决方案...向柏林问候。 - philipp
7个回答

112

5
Splatting在语言中已经可用了吗?我想调用sumOf(...numbers) - Noitidart
3
太令人失望了!我甚至在尝试将自己的日志调用委托给“print”时,都遇到了这个问题,即使这么简单的一件事! - Mark A. Donohoe

77

我找到了一个解决方法。虽然不完全符合你的要求,但它似乎有效。

步骤1:使用数组而不是可变参数声明所需的函数:

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

步骤2:从您的可变参数函数内调用此函数:

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

第三步:无论如何都要进行呼叫:

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

看起来很奇怪,但在我的测试中它是有效的。如果这对任何人造成了未预料的问题,请告诉我。Swift似乎能够区分同一函数名的两个调用之间的差异。

此外,如果苹果更新语言,如@manojid的答案建议的那样,使用这种方法只需要更新这些函数。否则,您将不得不进行大量重命名。


谢谢,我喜欢这个解决方法。但是,我仍然会把“正确”的答案奖给Manojlds,因为他找到了官方确认该功能尚未可用的链接。希望你能理解。 - Ole Begemann
你可能正在遵循指南,而你的函数 sumOf(numbers: [Int]) -> Int 实际上正在计算平均值。 - gfelisberto

20

您可以调用该函数:

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

太棒了!这也适用于多个(命名的)参数;例如:func sumOf(foo numbers: Int..., bar: Bool) -> Int {};需要 typealias Function = (foo: [Int], bar: Bool) -> Int; - ThomasR
太好了!救了我的命。 - JerryZhou
我该如何使用 AnyObject... 来做类似的事情呢?谢谢。 - JerryZhou
2
这是一个非常糟糕的想法。完全没有任何保证它会起作用。 - idmean
4
@MattMc 当我查看unsafeBitCast函数的严重警告时,我认为没有任何东西可以允许您仅使用unsafeBitCast将一个可调用类型转换为另一个。这可能在今天起作用,但除非有参考资料,否则下一个版本的编译器完全可以在此处执行任何操作(编译器错误/崩溃/随机执行代码...)。 - idmean
显示剩余2条评论

20

你可以使用以下的辅助函数:

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

18
如果有适用于数组的版本的话,我认为问题的前提是原始函数采用 Int... 参数并且不能(轻易地)更改? - user395760
2
@delnan:没错。在我看来,既然可变参数最终会转换成数组,那么应该有一种方法可以将数组传递给可变参数函数。 - Ole Begemann
1
接受函数可变数量参数的语言(例如Scheme)有一个apply过程。我了解到有些人称其为“splatting”。 - GoZoner
1
这里所引用的 sumArray 是什么? - Rob

3
我用这种方法(封装器+身份映射)实现了以下功能:
func addBarButtonItems(types: REWEBarButtonItemType...) {
    addBarButtonItems(types: types.map { $0 })
}

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

2

Swift 5

这是一种使用 @dynamicCallable 特性的方法,它允许避免重载或 unsafeBitCast,但你需要创建一个特定的 struct 进行调用:

@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

你也可以在结构体中添加另一种方法:func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, [Int]>) -> Int { let values = args.first?.value ?? Int return values.reduce(0, +) }这样,你就可以选择使用参数标签(任何一个都行)来调用它。例如:sum(arr: [1,2,3]) - Scott Carter

2
我知道这个回答并没有直接回答你的问题,但我认为这也值得注意。我也开始尝试使用Swift,并立即遇到了类似的问题。Manojlds的回答更适合你的问题,我同意,但是我想提供另一个解决方法。我碰巧更喜欢Logan的方法。
在我的情况下,我只想传递一个数组:
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])

我想分享一下,如果有人和我一样考虑这个问题。大多数时候,我更喜欢像这样传递数组,但我认为“Swiftly”还没有到位。 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接