Передача масиву до функції зі змінною кількістю аргументів у 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

Бризкання ще не в мові , як це підтвердили деви. Наразі вирішити питання - використовувати перевантаження або чекати, якщо ви не можете додати перевантаження.


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 (числа: [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. Це може працювати сьогодні, але якщо посилання не говорить про це, наступна версія компілятора вільна буквально робити що-небудь тут (помилка компілятора / збій / випадкове виконання коду ...). Погляньте на серйозне зовнішнє попередження на небезпечнийBitCast .
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
Мови, які приймають змінну кількість аргументів у функціях, як-от Схема, мають 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 + Manding Identity Mapping):

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

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

0

Швидкий 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.