Ü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?

Ole Begemann
quelle
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.

Manojlds
quelle
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.

Logan
quelle
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])
Guoye Zhang
quelle
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) }
GoZoner
quelle
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. :) :)

gregthegeek
quelle
1

Ich habe dies getan (Wrapper + Identity Mapping):

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

func addBarButtonItems(types: [REWEBarButtonItemType]) {
    // actual implementation
}
schirrmacher
quelle
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
iUrii
quelle