Swift Methodenparameter veränderbar machen?

123

Wie kann ich mit diesem Fehler umgehen, ohne eine zusätzliche Variable zu erstellen?

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

Ich möchte keine zusätzliche Variable erstellen, nur um den Wert von x zu speichern. Kann ich überhaupt machen, was ich will?

Gabriel
quelle
3
Siehe die aktualisierten Antworten unten. Swift 3 hat Ihre akzeptierte Antwort abgelehnt.
Achi

Antworten:

191

Wie in anderen Antworten angegeben, wird ab Swift 3 var platziert, bevor eine Variable veraltet ist. Obwohl in anderen Antworten nicht angegeben, ist die Möglichkeit, einen inoutParameter zu deklarieren . Denken Sie: einen Zeiger übergeben.

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

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

Dies kann besonders bei der Rekursion nützlich sein.

Die inoutDeklarationsrichtlinien von Apple finden Sie hier .

Achi
quelle
2
Vielen Dank!!!! Ich stecke hier in einer Rekursionsfrage fest. Du hast mein Leben gerettet.
JW.ZG
2
Sollte mit Vorsicht verwendet werden, da dadurch Variablen außerhalb des Funktionsumfangs geändert werden. Idealerweise möchten Sie den Wert, den Sie innerhalb der Funktion geändert haben, explizit zurückgeben.
Chris Gunawardena
2
inoutDas Schlüsselwort sollte wie folgt zwischen Parametername und Parametertyp platziert werden: func reduceToZero(x: inout Int) in der aktuellen Swift 3-Version.
Agustí Sánchez
Abgesehen davon scheint dies bei Schließungen nicht zu funktionieren, da Schließungen offenbar nur Inout-Parameter nach Wert erfassen (zumindest ist dies die Fehlermeldung, die Xcode mir gibt). In diesem Fall verwende ich die @ GeRyCh-Lösung.
wcochran
Danke dir. Dies funktionierte vorerst, aber es ist wie die Verwendung von Zeigern in C. Würde dies eine andere Swift-Version überleben?
Krishna Vedula
45

Für Swift 1 und 2 (für Swift 3 siehe Antwort von achi mit einem inout-Parameter): Das Argument einer Funktion in Swift ist letstandardmäßig. Ändern Sie es daher in, varwenn Sie den Wert ändern müssen, z.

func reduceToZero(var x:Int) -> Int {
    while (x != 0) {
        x = x-1     
    }
    return x
}
LML
quelle
2
Warum wird diese Antwort höllisch hochgestimmt? Die andere Antwort wurde vor diese gestellt und enthält mehr Informationen als diese.
Cristik
16
/! \ Mit var wird eine Kopie der in den Parametern übergebenen Variablen erstellt. Wenn Sie es ändern, wird der ursprüngliche Wert nicht geändert. Auch varin Parametern wird sehr wahrscheinlich in neueren Swift-Versionen per github.com/apple/swift-evolution/blob/master/proposals/…
Matthieu Riegler
17
var Schlüsselwort in Methode Paremeter wird in Swift 3 veraltet.
Boon
4
Ich denke, mit Swift 3 werden wir das nicht mehr können. Wir müssen eine variable Kopie des Arrays erstellen und das geänderte Array zurückgeben.
C0D3
Diese Antwort ist die richtige Antwort: stackoverflow.com/questions/24077880/…
achi
44

'var'-Parameter sind veraltet und werden in Swift 3 entfernt. Die Zuweisung zu einem neuen Parameter scheint jetzt der beste Weg zu sein:

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

wie hier erwähnt: 'var'-Parameter sind veraltet und werden in Swift 3 entfernt

GeRyCh
quelle
1
Kopiert es in diesem Fall tatsächlich xdas Neue var x? Oder macht Swift etwas effizienteres?
Genki
3
Das funktioniert und ist das, was ich mache, aber es scheint sehr umständlich.
Wcochran
1
@Gomfucius Kein Wort dazu im Swift 3.1-Handbuch. In diesem Fall ( xpasst in das Register) fallen praktisch keine Kosten an. Wenn xein Array, eine Struktur oder ein Objekt mutiert ist, muss mit ziemlicher Sicherheit eine Kopie ausgeführt werden (es sei denn, der Optimierer kann sie inline analysieren und als Alias ​​verwenden).
Wcochran
1
@wcochran Das ist ein ordentlicher Trick, aber es ist wirklich nichts Besonderes los. Es verdunkelt einfach einen Eingabeparameter mit einer lokalen var-Kopie. In der Situation des OP ist es ein besserer Ersatz für varArgumente als die Verwendung, inoutdie unbeabsichtigte Nebenwirkungen haben kann, insb. wenn die var ein Zeiger war.
Echelon
14

Swift3-Antwort für die Übergabe eines veränderbaren Array-Zeigers.

Funktion:

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

Aufruf zur Funktion:

var a = Array<Int>()
foo(array:&a)
Joshd
quelle
Ehrlich gesagt bin ich mir nicht sicher, ob dies richtig ist, da sich der Swift-Boden ständig ändert. Ich dachte, das wäre besser als var array = array innerhalb der Funktion, weil das eine Kopie macht (und die ursprüngliche Array-Struktur nicht wirklich beeinflusst)? Ist ein besserer Entwurfsansatz für den oben genannten var-Ansatz und geben Sie dann das neue mutierte Array zurück?
Joshd
7

In Swift fügen Sie einfach das varSchlüsselwort vor dem Variablennamen in die Funktionsdeklaration ein:

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

Weitere Informationen finden Sie im Unterabschnitt "Konstante und variable Parameter" im Kapitel "Funktionen" des Swift-Buches (Seite 210 des heutigen iBook).

DK_
quelle
7
'var'-Parameter sind veraltet und werden in Swift 3
Regis St-Gelais
1
Nicht gültig für Swift 4 und höher.
Ilkayaktas
0

Es gibt einige Fälle, in denen wir nicht verwendet haben inout

Wir können so etwas verwenden, wenn Sie möchten, dass sich Änderungen / Umfang nur innerhalb der Funktion befinden:

func manipulateData(a: Int) -> Int {
    var a = a
    // ...
}
dheeru
quelle
0

Lösung mit Swift5 mit funktionaler Programmierung ...

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