Passare un array a una funzione con numero variabile di arg in Swift


151

In The Swift Programming Language , dice:

Le funzioni possono anche accettare un numero variabile di argomenti, raccogliendoli in un array.

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

Quando chiamo una tale funzione con un elenco di numeri separato da virgole (`sumOf (1, 2, 3, 4), vengono resi disponibili come array all'interno della funzione.

Domanda: cosa succede se ho già un array di numeri che voglio passare a questa funzione?

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

Questo non riesce con un errore del compilatore, "Impossibile trovare un sovraccarico per '__conversion' che accetta gli argomenti forniti". C'è un modo per trasformare un array esistente in un elenco di elementi che posso passare a una funzione variadica?


1
Perché non configurare semplicemente la funzione in modo che prenda invece un array intero?
Mick MacCallum,

27
Certo, ma potrei non essere l'autore della funzione e non essere in grado di (o voler) cambiarla.
Ole Begemann,

Risposte:


97

Splatting non è ancora nella lingua , come confermato dagli sviluppatori. La soluzione alternativa per ora è utilizzare un sovraccarico o attendere se non è possibile aggiungere sovraccarichi.


3
Splatting è ancora nella lingua? Sto provando a chiamaresumOf(...numbers)
Noitidart

Così deludente! Ci sono riuscito anche con qualcosa di semplice come provare a delegare le mie chiamate di registro fino a print!
Mark A. Donohoe,

65

Ecco un lavoro che ho trovato. So che non è esattamente quello che vuoi, ma sembra funzionare.

Passaggio 1: dichiarare la funzione che si desidera con un array anziché con argomenti variadici:

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

Step 2: chiama questo dalla tua funzione variadic:

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

Passaggio 3: chiamare in entrambi i modi:

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

Sembra strano, ma sta funzionando nei miei test. Fammi sapere se questo causa problemi imprevisti a nessuno. Swift sembra essere in grado di separare la differenza tra le due chiamate con lo stesso nome di funzione.

Inoltre, con questo metodo se Apple aggiorna la lingua come suggerisce la risposta di @ manojid, dovrai solo aggiornare queste funzioni. Altrimenti, dovrai passare e rinominare molto.


Grazie, mi piace la soluzione alternativa. Assegnerò comunque la risposta "corretta" a Manojlds per aver trovato il link alla conferma ufficiale che la funzione non è ancora disponibile. Spero che tu capisca.
Ole Begemann,

Probabilmente stai seguendo la Guida e la tua funzione (numeri: [Int]) -> Int sta effettivamente calcolando la media
gfelisberto

18

Puoi lanciare la funzione:

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

Grande! Funziona anche con più parametri (nominati); ad es .: func sumOf(foo numbers: Int..., bar: Bool) -> Int {};richiedetypealias Function = (foo: [Int], bar: Bool) -> Int;
Thomas R

Grande! salvami la vita.
JerryZhou,

Come posso fare cose simili con AnyObject ...? stackoverflow.com/questions/42016358/… Grazie.
JerryZhou,

Questa è una pessima idea. Non c'è assolutamente alcuna garanzia che funzioni.
idmean,

1
@MattMc Certo. Per quanto ne so, non c'è nulla che ti consenta di trasmettere semplicemente un tipo richiamabile a un altro usando unsafeBitCast. Questo può funzionare oggi, ma a meno che un riferimento non lo dica, la prossima versione del compilatore è libera di fare letteralmente qualsiasi cosa qui (errore del compilatore / crash / esecuzione casuale del codice ...). Dai un'occhiata all'avvertimento serio su unsafeBitCast .
idmean

15

È possibile utilizzare una funzione di supporto in quanto tale:

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

12
A condizione che esista una versione per array. Ho pensato che la premessa della domanda è che la funzione originale prende Int...e non può (facilmente) essere cambiata?

2
@delnan: corretto. Mi sembra che ci dovrebbe essere il modo di passare una matrice in una funzione variadica, dato che gli argomenti variadici vengono comunque trasformati in una matrice.
Ole Begemann,

1
Le lingue che accettano un numero variabile di argomenti nelle funzioni, come Scheme, hanno una applyprocedura. Incontro alcune persone che chiamano "splatting".
GoZoner,

1
Cosa viene sumArrayindicato qui?
Rob,

2

So che questa risposta non risponde alla tua domanda esatta, ma ne sento la pena notare. Anch'io stavo iniziando a giocare con Swift e ho subito incontrato una domanda simile. La risposta di Manojlds è migliore per la tua domanda, sono d'accordo, ma ancora una volta ho trovato un'altra soluzione. Anche a me piace che Logan stia meglio.

Nel mio caso volevo solo passare un array:

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

Volevo solo condividere, nel caso qualcun altro stesse pensando come me. Il più delle volte preferirei passare l'array in questo modo, ma non credo ancora "Swiftly". :)


1

Ho fatto questo (Wrapper + Identity Mapping):

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

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

0

Swift 5

Questo è un approccio con @dynamicCallablefunzionalità che consente di evitare il sovraccarico o unsafeBitCastma è necessario effettuare uno specifico structper chiamare:

@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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.