Передача массива в функцию с переменным числом аргументов в 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 еще не в языке , что подтверждается разработчиками. Обходной путь на данный момент заключается в использовании перегрузки или ожидания, если вы не можете добавить перегрузки.


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

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

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


1

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

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

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

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.