Swift rend le paramètre de méthode mutable?

123

Comment puis-je gérer cette erreur sans créer de variable supplémentaire?

func reduceToZero(x:Int) -> Int {
    while (x != 0) {
        x = x-1            // ERROR: cannot assign to 'let' value 'x'
    }
    return x
}

Je ne veux pas créer de variable supplémentaire juste pour stocker la valeur de x. Est-il même possible de faire ce que je veux?

Gabriel
la source
3
Voir les réponses mises à jour ci-dessous, Swift 3 a désapprouvé votre réponse acceptée.
achi

Réponses:

192

Comme indiqué dans d'autres réponses, à partir de Swift 3, placer var avant une variable est obsolète. Bien que cela ne soit pas indiqué dans d'autres réponses, la possibilité de déclarer un inoutparamètre. Pensez: en passant un pointeur.

func reduceToZero(_ x: inout Int) {
    while (x != 0) {
        x = x-1     
    }
}

var a = 3
reduceToZero(&a)
print(a) // will print '0'

Cela peut être particulièrement utile en récursivité.

Les inoutdirectives de déclaration d'Apple sont disponibles ici .

achi
la source
2
Merci beaucoup!!!! Je suis coincé ici sur une question de récursivité. Tu m'as sauvé la vie.
JW.ZG
2
Doit l'utiliser avec prudence car cela modifie les variables en dehors de la portée de la fonction. Idéalement, vous souhaitez renvoyer explicitement la valeur que vous avez modifiée dans la fonction.
Chris Gunawardena
2
inoutLe mot-clé doit être placé entre le nom du paramètre et le type de paramètre comme ceci: func reduceToZero(x: inout Int) dans la version actuelle de Swift 3.
Agustí Sánchez
Sauf que cela ne semble pas fonctionner pour les fermetures, car les fermetures ne capturent évidemment que les paramètres d'entrée par valeur (du moins c'est le message d'erreur que Xcode me donne). J'utilise la solution @GeRyCh dans ce cas.
wcochran le
Je vous remercie. Cela a fonctionné pour le moment, mais c'est comme utiliser des pointeurs en C. Cela survivrait-il à une autre version de Swift?
Krishna Vedula
45

Les paramètres 'var' sont obsolètes et seront supprimés dans Swift 3. Ainsi, l'assignation à un nouveau paramètre semble être la meilleure façon maintenant:

func reduceToZero(x:Int) -> Int {
    var x = x
    while (x != 0) {
        x = x-1            
    }
    return x
}

comme mentionné ici: les paramètres 'var' sont obsolètes et seront supprimés dans Swift 3

GeRyCh
la source
1
Dans ce cas, copie-t-il réellement le xdans le nouveau var x? Ou Swift fait-il quelque chose de plus efficace que cela?
Genki
3
Cela fonctionne et c'est ce que je fais, mais cela semble très gênant.
wcochran
1
@Gomfucius Pas un mot à ce sujet dans le guide Swift 3.1. Dans ce cas ( xs'inscrit dans le registre), il n'y a pratiquement aucun coût. Si un xtableau, une structure ou un objet est muté, alors une copie doit presque certainement être effectuée (à moins que l'optimiseur ne puisse l'analyser en ligne et l'aliaser).
wcochran
1
@wcochran C'est une bonne astuce, mais il n'y a vraiment rien de spécial. Il s'agit simplement d'éclipser un paramètre d'entrée avec une copie var locale. Dans la situation de l'OP, c'est un meilleur remplacement pour les vararguments que l'utilisation inoutqui peut avoir des effets secondaires involontaires, en particulier. si le var était un pointeur.
Echelon
45

Pour Swift 1 et 2 (pour Swift 3 voir la réponse par achi en utilisant un paramètre inout): l'argument d'une fonction dans Swift est let par défaut, alors changez-le en varsi vous devez modifier la valeur, c'est-à-dire,

func reduceToZero(var x:Int) -> Int {
    while (x != 0) {
        x = x-1     
    }
    return x
}
LML
la source
2
Pourquoi cette réponse est-elle votée comme l'enfer? L'autre réponse a été placée avant celle-ci et contient plus d'informations que celle-ci.
Cristik
16
/! \ L'utilisation de var créera une copie de la variable passée en paramètres. Donc, le modifier ne modifiera pas la valeur d'origine. Il varest également très probable que les paramètres disparaissent dans les nouvelles versions de Swift par github.com/apple/swift-evolution/blob/master/proposals
Matthieu Riegler
17
Le mot clé var dans le paramètre de méthode sera obsolète dans Swift 3.
Boon
4
Je pense qu'avec Swift 3, nous ne pourrons plus faire ça. Nous devrons créer une copie variable du tableau et renvoyer ce tableau modifié.
C0D3
Cette réponse est la bonne réponse: stackoverflow.com/questions/24077880/…
achi
14

Réponse Swift3 pour passer le pointeur de tableau mutable.

Fonction:

func foo(array: inout Array<Int>) {
    array.append(1)
}

Appel à fonction:

var a = Array<Int>()
foo(array:&a)
Joshd
la source
Honnêtement, je ne suis pas sûr que ce soit correct car le terrain Swift est en constante évolution. Je pensais que c'était mieux que de faire var array = array dans la fonction car cela fait une copie (et n'affecte pas réellement la structure originale du tableau)? Une meilleure approche de conception est-elle une approche var susmentionnée, puis renvoie le nouveau tableau muté?
joshd
7

Dans Swift, vous ajoutez simplement le varmot - clé avant le nom de la variable dans la déclaration de fonction:

func reduceToZero(var x:Int) -> Int { // notice the "var" keyword
    while (x != 0) {
        x = x-1            
    }
    return x
}

Reportez-vous à la sous-section «Paramètres constants et variables» du chapitre «Fonctions» du livre Swift (page 210 de l'iBook tel qu'il est aujourd'hui).

NSP_
la source
7
Les paramètres 'var' sont obsolètes et seront supprimés dans Swift 3
Regis St-Gelais
1
Non valide pour Swift 4 et plus récent.
ilkayaktas
0

Il y a des cas où nous n'avons pas besoin d'utiliser inout

Nous pouvons utiliser quelque chose comme ceci si vous voulez que les changements / la portée soient uniquement à l'intérieur de la fonction:

func manipulateData(a: Int) -> Int {
    var a = a
    // ...
}
Dheeru
la source
0

Solution utilisant Swift5 avec programmation fonctionnelle ...

func reduceToZeroFP(x:Int) -> Int {
    x == 0 ? x : reduceToZeroFP(x: x - 1)
}
Jules Burt
la source