Przekazywanie tablicy do funkcji ze zmienną liczbą argumentów w języku Swift


151

W języku szybkiego programowania mówi:

Funkcje mogą również pobierać zmienną liczbę argumentów, zbierając je w tablicę.

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

Kiedy wywołuję taką funkcję z listą liczb oddzielonych przecinkami (sumOf (1, 2, 3, 4), są one udostępniane jako tablica wewnątrz funkcji.

Pytanie: co jeśli mam już tablicę liczb, które chcę przekazać do tej funkcji?

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

To kończy się niepowodzeniem z powodu błędu kompilatora: „Nie można znaleźć przeciążenia dla '__conversion', który akceptuje podane argumenty”. Czy istnieje sposób na przekształcenie istniejącej tablicy w listę elementów, które mogę przekazać do funkcji wariadycznej?


1
Dlaczego po prostu nie skonfigurować funkcji, aby zamiast tego pobierała tablicę liczb całkowitych?
Mick MacCallum

27
Jasne, ale mogę nie być autorem funkcji i mogę nie być w stanie (lub chcieć) jej zmienić.
Ole Begemann

Odpowiedzi:


97

Splatting nie jest jeszcze w języku , co potwierdzają twórcy. Na razie obejściem jest użycie przeciążenia lub czekanie, jeśli nie możesz dodać przeciążeń.


3
Czy Splatting jest już w języku? Próbuję zadzwonićsumOf(...numbers)
Noitidart

Tak rozczarowujące! Udało mi się to nawet w czymś tak prostym, jak próba delegowania moich własnych wywołań dziennika print!
Mark A. Donohoe

65

Oto obejście, które znalazłem. Wiem, że to nie jest dokładnie to, czego chcesz, ale wydaje się, że działa.

Krok 1: Zadeklaruj żądaną funkcję za pomocą tablicy zamiast argumentów wariadycznych:

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

Krok 2: Wywołaj to z poziomu funkcji wariadycznej:

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

Krok 3: Zadzwoń w dowolny sposób:

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

Wydaje się dziwne, ale działa w moich testach. Daj mi znać, jeśli powoduje to nieprzewidziane problemy dla kogoś. Wydaje się, że Swift jest w stanie rozdzielić różnicę między dwoma wywołaniami o tej samej nazwie funkcji.

Ponadto, dzięki tej metodzie, jeśli Apple aktualizuje język, jak sugeruje odpowiedź @ manojid, wystarczy zaktualizować te funkcje. W przeciwnym razie będziesz musiał przejść i wiele zmienić nazwy.


Dzięki, podoba mi się obejście. Nadal udzielę „prawidłowej” odpowiedzi manojldom za znalezienie linku do oficjalnego potwierdzenia, że ​​funkcja nie jest jeszcze dostępna. Mam nadzieję że rozumiesz.
Ole Begemann

Prawdopodobnie postępujesz zgodnie z Przewodnikiem i swoją func sumOf (liczby: [Int]) -> Int faktycznie oblicza średnią
gfelisberto

18

Możesz przesłać funkcję:

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

Wspaniały! Działa to również z wieloma (nazwanymi) parametrami; np .: func sumOf(foo numbers: Int..., bar: Bool) -> Int {};wymagatypealias Function = (foo: [Int], bar: Bool) -> Int;
ThomasR

Wspaniały! Ocal moje życie.
JerryZhou

Jak mogę zrobić podobne rzeczy z AnyObject ...? stackoverflow.com/questions/42016358/ ... Dzięki.
JerryZhou

To bardzo zły pomysł. Nie ma absolutnie żadnej gwarancji, że to zadziała.
idmean

1
@MattMc Jasne. O ile wiem, nie ma nic, co pozwoliłoby ci po prostu przerzucić jeden typ wywoływalny na inny za pomocą unsafeBitCast. To może działać dzisiaj, ale jeśli nie mówi tak, następna wersja kompilatora może dosłownie zrobić wszystko tutaj (błąd kompilatora / awaria / losowe wykonanie kodu ...). Spójrz na poważnie wyglądające ostrzeżenie na stronie unsafeBitCast .
idmean

15

Możesz użyć funkcji pomocniczej jako takiej:

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

12
Pod warunkiem, że istnieje wersja dla tablic. Pomyślałem, że podstawą pytania jest to, że pierwotna funkcja przyjmuje Int...i nie można jej (łatwo) zmienić?

2
@delnan: Dobrze. Wydaje mi się, że powinien istnieć sposób na przekazanie tablicy do funkcji wariadycznej, biorąc pod uwagę, że argumenty wariadyczne i tak są przekształcane w tablicę.
Ole Begemann

1
Języki, które akceptują zmienną liczbę argumentów w funkcjach, takich jak Schemat, mają applyprocedurę. Niektórzy nazywają to „splataniem”.
GoZoner

1
O czym sumArraytu mowa?
Rob

2

Wiem, że ta odpowiedź nie odpowiada dokładnie na twoje pytanie, ale czuję, że warto ją odnotować. Ja też zacząłem bawić się Swiftem i od razu wpadłem na podobne pytanie. Odpowiedź Manojlda jest lepsza na twoje pytanie, zgadzam się, ale znowu mam inne obejście, które wymyśliłem. Tak się składa, że ​​bardziej lubię Logana.

W moim przypadku chciałem tylko przekazać tablicę:

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

Chciałem się tylko podzielić, na wypadek gdyby ktoś inny pomyślał tak jak ja. Przez większość czasu wolałbym przekazywać tablicę w ten sposób, ale nie sądzę jeszcze, że opcja „Szybko”. :)


1

Zrobiłem to (Wrapper + Identity Mapping):

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

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

0

Szybki 5

Jest to podejście z @dynamicCallablefunkcją, która pozwala uniknąć przeciążenia, unsafeBitCastale powinieneś zrobić konkretny structsposób wywołania:

@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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.