Передача массива в функцию с переменным числом аргументов в Swift

151

В языке программирования Swift говорится:

Функции также могут принимать переменное количество аргументов, собирая их в массив.

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

Когда я вызываю такую ​​функцию с разделенным запятыми списком чисел (`sumOf (1, 2, 3, 4)), они становятся доступными в виде массива внутри функции.

Вопрос: что если у меня уже есть массив чисел, который я хочу передать этой функции?

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

Это приводит к ошибке компилятора: «Не удалось найти перегрузку для« __conversion », которая принимает предоставленные аргументы». Есть ли способ превратить существующий массив в список элементов, которые я могу передать в функцию с переменным числом аргументов?

Оле Бегеманн
источник
1
Почему бы просто не настроить функцию так, чтобы она взяла целочисленный массив?
Мик МакКаллум
27
Конечно, но я, возможно, не являюсь автором функции и не могу (или не хочу) изменить ее.
Оле Бегеманн

Ответы:

97

Splatting еще не в языке , что подтверждается разработчиками. Обходной путь на данный момент заключается в использовании перегрузки или ожидания, если вы не можете добавить перегрузки.

manojlds
источник
3
Сплаттинг уже есть в языке? Я пытаюсь позвонитьsumOf(...numbers)
Noitidart
Так разочаровывает! Я справляюсь с этим даже с помощью чего-то столь же простого, как попытка делегировать свои журналы print!
Марк А. Донохо
65

Вот работа, которую я нашел. Я знаю, что это не совсем то, что вы хотите, но, похоже, работает.

Шаг 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)
}

Шаг 3: Звоните в любую сторону:

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

Это кажется странным, но это работает в моих тестах. Дайте мне знать, если это приведет к непредвиденным проблемам для кого-либо. Swift, кажется, способен разделить разницу между двумя вызовами с одинаковым именем функции.

Кроме того, с помощью этого метода, если Apple обновляет язык в соответствии с ответом @ manojid, вам нужно будет только обновить эти функции. В противном случае вам придется пройти и сделать много переименований.

логан
источник
Спасибо, мне нравится обходной путь. Я по-прежнему присуждаю «правильный» ответ manojlds за нахождение ссылки на официальное подтверждение того, что функция еще не доступна. Я надеюсь, вы понимаете.
Оле Бегеманн
Вероятно, вы следуете Руководству, и ваш функционал sumOf (numbers: [Int]) -> Int фактически вычисляет среднее значение
gfelisberto
18

Вы можете разыграть функцию:

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 ...? stackoverflow.com/questions/42016358/… Спасибо.
JerryZhou
Это очень плохая идея. Существует абсолютно нулевая гарантия, что это будет работать.
idmean
1
@MattMc Конечно. Насколько я могу судить, нет ничего, что позволило бы вам просто приводить один вызываемый тип к другому, используя unsafeBitCast. Это может работать сегодня, но если ссылка не говорит об этом, следующая версия компилятора может делать здесь буквально все, что угодно (ошибка компилятора / сбой / случайное выполнение кода ...). Посмотрите на серьезное предупреждение на unsafeBitCast .
idmean
15

Вы можете использовать вспомогательную функцию как таковую:

func sumOf (numbers : [Int])  -> Int { return numbers.reduce(0, combine: +) }
func sumOf (numbers : Int...) -> Int { return sumOf (numbers) }
GoZoner
источник
12
При условии, что есть версия для массивов. Я думал, что предпосылка вопроса в том, что исходная функция принимает Int...и не может (легко) быть изменена?
2
@delnan: Верно. Мне кажется, что должен быть способ передать массив в функцию с переменными параметрами, учитывая, что аргументы с переменными значениями все равно превращаются в массив.
Оле Бегеманн
1
Языки, которые принимают переменное число аргументов в функциях, например Scheme, имеют applyпроцедуру. Я полагаю, некоторые люди называют это "плескаться".
GoZoner
1
На что sumArrayздесь ссылаются?
Роб
2

Я знаю, что этот ответ не отвечает на ваш точный вопрос, но я чувствую, что стоит отметить. Я тоже начал играть со Свифтом и сразу столкнулся с похожим вопросом. Ответ Манойлда лучше на ваш вопрос, я согласен, но, опять же, я нашел другой обходной путь. Мне тоже нравится Логан.

В моем случае я просто хотел передать массив:

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])

Просто хотел поделиться, на случай, если кто-то еще подумает, как я. Большую часть времени я бы предпочел передать массив таким образом, но пока не думаю, что это «быстро». :)

gregthegeek
источник
1

Я сделал это (Wrapper + Identity Mapping):

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

func addBarButtonItems(types: [REWEBarButtonItemType]) {
    // actual implementation
}
schirrmacher
источник
0

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
Iurii
источник