Passer un tableau à une fonction avec un nombre variable d'arguments dans Swift

151

Dans le langage de programmation Swift , il est dit:

Les fonctions peuvent également prendre un nombre variable d'arguments, les rassemblant dans un tableau.

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

Quand j'appelle une telle fonction avec une liste de nombres séparés par des virgules (`sumOf (1, 2, 3, 4), ils sont rendus disponibles sous forme de tableau à l'intérieur de la fonction.

Question: que faire si j'ai déjà un tableau de nombres que je souhaite transmettre à cette fonction?

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

Cela échoue avec une erreur du compilateur, «Impossible de trouver une surcharge pour '__conversion' qui accepte les arguments fournis». Existe-t-il un moyen de transformer un tableau existant en une liste d'éléments que je peux passer à une fonction variadique?

Ole Begemann
la source
1
Pourquoi ne pas simplement configurer la fonction pour prendre un tableau d'entiers à la place?
Mick MacCallum
27
Bien sûr, mais je ne suis peut-être pas l'auteur de la fonction et je ne pourrai peut-être pas (ou ne voudrai pas) la modifier.
Ole Begemann

Réponses:

97

Splatting n'est pas encore dans la langue , comme l'ont confirmé les développeurs. Pour l'instant, la solution de contournement consiste à utiliser une surcharge ou à attendre si vous ne pouvez pas ajouter de surcharges.

manojlds
la source
3
Splatting est-il encore dans la langue? sumOf(...numbers)
J'essaye
Tellement décevant! J'ai frappé cela même avec quelque chose d'aussi simple que d'essayer de déléguer mes propres appels de journal à print!
Mark A. Donohoe le
65

Voici un travail que j'ai trouvé. Je sais que ce n'est pas exactement ce que vous voulez, mais cela semble fonctionner.

Étape 1: Déclarez la fonction que vous souhaitez avec un tableau au lieu d'arguments variadiques:

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

Étape 2: Appelez ceci depuis votre fonction variadic:

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

Étape 3: Appelez dans les deux sens:

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

Cela semble étrange, mais cela fonctionne dans mes tests. Faites-moi savoir si cela pose des problèmes imprévus à quelqu'un. Swift semble être en mesure de séparer la différence entre les deux appels avec le même nom de fonction.

De plus, avec cette méthode, si Apple met à jour la langue comme le suggère la réponse de @ manojid, vous n'aurez qu'à mettre à jour ces fonctions. Sinon, vous devrez passer par et faire beaucoup de renommage.

Logan
la source
Merci, j'aime la solution de contournement. Je vais quand même attribuer la réponse «correcte» aux manojlds pour avoir trouvé le lien vers la confirmation officielle que la fonctionnalité n'est pas encore disponible. J'espère que tu comprends.
Ole Begemann
Vous suivez probablement le Guide et votre func sumOf (numbers: [Int]) -> Int calcule en fait la moyenne
gfelisberto
18

Vous pouvez lancer la fonction:

typealias Function = [Int] -> Int
let sumOfArray = unsafeBitCast(sumOf, Function.self)
sumOfArray([1, 2, 3])
Guoye Zhang
la source
Génial! Cela fonctionne également avec plusieurs paramètres (nommés); par exemple: func sumOf(foo numbers: Int..., bar: Bool) -> Int {};nécessitetypealias Function = (foo: [Int], bar: Bool) -> Int;
ThomasR
Génial! sauve ma vie.
JerryZhou
Comment puis-je faire des choses similaires avec AnyObject ...? stackoverflow.com/questions/42016358/… Merci.
JerryZhou
C'est une très mauvaise idée. Il n'y a absolument aucune garantie que cela fonctionnera.
idmean
1
@MattMc Bien sûr. Pour autant que je sache, rien ne vous permettrait de simplement convertir un type appelable en un autre en utilisant unsafeBitCast. Cela peut fonctionner aujourd'hui, mais à moins qu'une référence ne le dise, la prochaine version du compilateur est libre de faire littéralement n'importe quoi ici (erreur du compilateur / crash / code d'exécution aléatoire ...). Jetez un œil à l'avertissement sérieux sur unsafeBitCast .
idmean
15

Vous pouvez utiliser une fonction d'assistance en tant que telle:

func sumOf (numbers : [Int])  -> Int { return numbers.reduce(0, combine: +) }
func sumOf (numbers : Int...) -> Int { return sumOf (numbers) }
GoZoner
la source
12
À condition qu'il existe une version pour les tableaux. Je pensais que la prémisse de la question est que la fonction d'origine prend Int...et ne peut pas (facilement) être modifiée?
2
@delnan: C'est exact. Il me semble qu'il devrait y avoir moyen de passer un tableau dans une fonction variadique, étant donné que les arguments variadiques sont de toute façon transformés en tableau.
Ole Begemann
1
Les langages qui acceptent un nombre variable d'arguments dans les fonctions, comme Scheme, ont une applyprocédure. Je crois comprendre que certaines personnes appellent cela «éclabousser».
GoZoner
1
Qu'est-ce qui est sumArrayréférencé ici?
Rob
2

Je sais que cette réponse ne répond pas exactement à votre question, mais je pense qu’il vaut la peine de le noter. Moi aussi, je commençais à jouer avec Swift et je suis immédiatement tombé sur une question similaire. La réponse de Manojld est meilleure pour votre question, je suis d'accord, mais encore une fois, une autre solution de contournement que j'ai trouvée. Il se trouve que j'aime mieux Logan aussi.

Dans mon cas, je voulais juste passer un tableau:

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

Je voulais juste partager, au cas où quelqu'un d'autre penserait comme moi. La plupart du temps, je préférerais passer le tableau comme ça, mais je ne pense pas encore le "Swiftly". :)

Gregthegeek
la source
1

J'ai fait ceci (Wrapper + Identity Mapping):

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

func addBarButtonItems(types: [REWEBarButtonItemType]) {
    // actual implementation
}
Schirrmacher
la source
0

Swift 5

C'est une approche avec @dynamicCallablefonctionnalité qui permet d'éviter la surcharge ou unsafeBitCastmais il faut faire un structappel spécifique :

@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
la source