Übergeben eines Arrays an eine Funktion mit variabler Anzahl von Argumenten in Swift


151

In der Swift-Programmiersprache heißt es:

Funktionen können auch eine variable Anzahl von Argumenten annehmen und diese in einem Array sammeln.

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

Wenn ich eine solche Funktion mit einer durch Kommas getrennten Liste von Zahlen (`sumOf (1, 2, 3, 4)) aufrufe, werden sie als Array innerhalb der Funktion verfügbar gemacht.

Frage: Was ist, wenn ich bereits ein Array von Zahlen habe, die ich an diese Funktion übergeben möchte?

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

Dies schlägt mit dem Compilerfehler fehl, "Es konnte keine Überladung für '__conversion' gefunden werden, die die angegebenen Argumente akzeptiert". Gibt es eine Möglichkeit, ein vorhandenes Array in eine Liste von Elementen umzuwandeln, die ich an eine variable Funktion übergeben kann?


1
Warum konfigurieren Sie die Funktion nicht einfach so, dass stattdessen ein ganzzahliges Array verwendet wird?
Mick MacCallum

26
Sicher, aber ich bin möglicherweise nicht der Autor der Funktion und kann (oder möchte) sie möglicherweise nicht ändern.
Ole Begemann

Antworten:


96

Splatting ist noch nicht in der Sprache , wie von den Entwicklern bestätigt. Problemumgehung besteht derzeit darin, eine Überladung zu verwenden oder zu warten, wenn Sie keine Überladungen hinzufügen können.


3
Ist Splatting schon in der Sprache? Ich versuche anzurufensumOf(...numbers)
Noitidart

So enttäuschend! Ich habe dies sogar mit etwas so Einfachem erreicht, wie dem Versuch, meine eigenen Protokollaufrufe an zu delegieren print!
Mark A. Donohoe

65

Hier ist eine Arbeit, die ich gefunden habe. Ich weiß, es ist nicht genau das, was Sie wollen, aber es scheint zu funktionieren.

Schritt 1: Deklarieren Sie die gewünschte Funktion mit einem Array anstelle von variadischen Argumenten:

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

Schritt 2: Rufen Sie dies innerhalb Ihrer variadischen Funktion auf:

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

Schritt 3: Rufen Sie so oder so an:

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

Es scheint seltsam, aber es funktioniert in meinen Tests. Lassen Sie mich wissen, ob dies für jemanden unvorhergesehene Probleme verursacht. Swift scheint in der Lage zu sein, den Unterschied zwischen den beiden Aufrufen mit demselben Funktionsnamen zu trennen.

Wenn Apple bei dieser Methode die Sprache gemäß der Antwort von @ manojid aktualisiert, müssen Sie nur diese Funktionen aktualisieren. Andernfalls müssen Sie viel umbenennen.


Danke, ich mag die Problemumgehung. Ich werde den Manojlds weiterhin die "richtige" Antwort geben, wenn sie den Link zur offiziellen Bestätigung finden, dass die Funktion noch nicht verfügbar ist. Ich hoffe, Sie verstehen.
Ole Begemann

Sie folgen wahrscheinlich dem Leitfaden und Ihre Funktion (Zahlen: [Int]) -> Int berechnet tatsächlich den Durchschnitt
gfelisberto

18

Sie können die Funktion umwandeln:

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

Toll! Dies funktioniert auch mit mehreren (benannten) Parametern. zB: func sumOf(foo numbers: Int..., bar: Bool) -> Int {};erforderttypealias Function = (foo: [Int], bar: Bool) -> Int;
ThomasR

Toll! Rette mein leben.
JerryZhou

Wie kann ich mit AnyObject ähnliche Dinge tun ...? stackoverflow.com/questions/42016358/… Danke.
JerryZhou

Das ist eine sehr schlechte Idee. Es gibt absolut keine Garantie dafür, dass dies funktioniert.
idmean

1
@ MattMc Sicher. Soweit ich das beurteilen kann, gibt es nichts, was es Ihnen erlauben würde, einfach einen aufrufbaren Typ mit einem anderen zu übertragen unsafeBitCast. Dies mag heute funktionieren, aber wenn eine Referenz dies nicht sagt, kann die nächste Version des Compilers hier buchstäblich alles tun (Compilerfehler / Absturz / zufälliges Ausführen von Code ...). Schauen Sie sich die ernsthaft aussehende Warnung auf unsafeBitCast an .
idmean

15

Sie können eine Hilfsfunktion als solche verwenden:

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

12
Vorausgesetzt, es gibt eine Version für Arrays. Ich dachte, die Prämisse der Frage ist, dass die ursprüngliche Funktion übernommen wird Int...und nicht (leicht) geändert werden kann?

2
@delnan: Richtig. Es scheint mir, dass es eine Möglichkeit geben sollte, ein Array an eine Variadic-Funktion zu übergeben, da die Variadic-Argumente sowieso in ein Array umgewandelt werden.
Ole Begemann

1
Sprachen, die eine variable Anzahl von Argumenten in Funktionen akzeptieren, wie z. B. Schema, haben eine applyProzedur. Ich nehme an, einige Leute nennen das "Splattern".
GoZoner

1
Worauf wird sumArrayhier verwiesen?
Rob

2

Ich weiß, dass diese Antwort Ihre genaue Frage nicht beantwortet, aber ich denke, es ist erwähnenswert. Auch ich begann mit Swift zu spielen und stieß sofort auf eine ähnliche Frage. Manojlds Antwort ist besser für Ihre Frage, ich stimme zu, aber auch hier eine andere Problemumgehung, die ich mir ausgedacht habe. Ich mag Logans auch besser.

In meinem Fall wollte ich nur ein Array übergeben:

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

Ich wollte nur teilen, falls jemand anders wie ich dachte. Die meiste Zeit würde ich es vorziehen, das Array so zu übergeben, aber ich denke das "Swiftly" noch nicht. :) :)


1

Ich habe dies getan (Wrapper + Identity Mapping):

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

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

0

Swift 5

Dies ist ein Ansatz mit einer @dynamicCallableFunktion, mit der eine Überlastung unsafeBitCastvermieden werden kann. Sie sollten jedoch einen bestimmten structAufruf vornehmen :

@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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.