Swift hace que el parámetro del método sea mutable

123

¿Cómo puedo lidiar con este error sin crear una variable adicional?

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

No quiero crear una variable adicional solo para almacenar el valor de x. ¿Es posible hacer lo que quiero?

Gabriel
fuente
3
Vea las respuestas actualizadas a continuación, Swift 3 ha desaprobado su respuesta aceptada.
achi

Respuestas:

191

Como se indicó en otras respuestas, a partir de Swift 3 colocando var antes de que una variable haya quedado en desuso. Aunque no se indica en otras respuestas, es la capacidad de declarar un inoutparámetro. Piensa: pasar un puntero.

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

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

Esto puede ser particularmente útil en la recursividad.

Las inoutpautas de declaración de Apple se pueden encontrar aquí .

achi
fuente
2
¡¡¡¡Muchas gracias!!!! Estoy atrapado aquí en una pregunta de recursión. Me salvaste la vida.
JW.ZG
2
Debe usar esto con precaución ya que modifica variables fuera del alcance de la función. Idealmente, desea devolver explícitamente el valor que cambió dentro de la función.
Chris Gunawardena
2
inoutLa palabra clave debe colocarse entre el nombre del parámetro y el tipo de parámetro de esta manera: func reduceToZero(x: inout Int) en la versión actual de Swift 3.
Agustí Sánchez
Excepto que esto no parece funcionar para los cierres, ya que los cierres evidentemente solo capturan los parámetros de entrada por valor (al menos ese es el mensaje de error que me da Xcode). Yo uso la solución @GeRyCh en este caso.
wcochran
Gracias. Esto funcionó por ahora, pero es como usar punteros en C. ¿Sobreviviría a otra versión de Swift?
Krishna Vedula
45

Los parámetros 'var' están en desuso y se eliminarán en Swift 3. Por lo tanto, asignar a un nuevo parámetro parece ser la mejor manera ahora:

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

como se menciona aquí: los parámetros 'var' están en desuso y se eliminarán en Swift 3

GeRyCh
fuente
1
En este caso, ¿realmente copia el xen el nuevo var x? ¿O Swift está haciendo algo más eficiente que eso?
Genki
3
Esto funciona y es lo que hago, pero parece muy incómodo.
wcochran
1
@Gomfucius Ni una palabra sobre esto en la guía Swift 3.1. En este caso ( xcabe en el registro) prácticamente no hay costo. Si se xtrata de una matriz, estructura u objeto que está mutado, es casi seguro que se debe realizar una copia (a menos que el optimizador pueda analizarlo en línea y alias).
wcochran
1
@wcochran Este es un buen truco, pero realmente no está pasando nada especial. Simplemente está eclipsando un parámetro de entrada con una copia var local. En la situación del OP, es un mejor reemplazo para los varargs que el uso inoutque puede tener efectos secundarios no deseados, especialmente. si la var fuera un puntero.
Echelon
45

Para Swift 1 y 2 (para Swift 3, vea la respuesta de achi usando un parámetro inout): El argumento de una función en Swift es letpor defecto, así que cámbielo a varsi necesita alterar el valor, es decir,

func reduceToZero(var x:Int) -> Int {
    while (x != 0) {
        x = x-1     
    }
    return x
}
LML
fuente
2
¿Por qué esta respuesta es votada como el infierno? La otra respuesta se colocó antes de esta y contiene más información que esta.
Cristik
16
/! \ El uso de var creará una copia de la variable pasada en los parámetros. Por lo tanto, modificarlo no modificará el valor original. También vares muy probable que los parámetros desaparezcan en las nuevas versiones de Swift según github.com/apple/swift-evolution/blob/master/proposals/…
Matthieu Riegler
17
palabra clave var en Paremeter método será obsoleto en Swift 3.
Boon
44
Creo que con Swift 3, ya no podremos hacer esto. Tendremos que crear una copia variable de la matriz y devolver esa matriz modificada.
C0D3
Esta respuesta es la respuesta correcta: stackoverflow.com/questions/24077880/…
achi
14

Respuesta Swift3 para pasar el puntero de matriz mutable.

Función:

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

Llamado a la función:

var a = Array<Int>()
foo(array:&a)
joshd
fuente
Honestamente, no estoy seguro de si esto es correcto ya que el terreno Swift está cambiando constantemente. ¿Pensé esto mejor que hacer var array = array dentro de la función porque eso hace una copia (y en realidad no afecta la estructura de matriz original)? ¿Es un mejor enfoque de diseño para el enfoque var antes mencionado y luego devolver la nueva matriz mutada?
joshd
7

En Swift, simplemente agrega la varpalabra clave antes del nombre de la variable en la declaración de función:

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

Consulte la subsección "Parámetros constantes y variables" en el capítulo "Funciones" del libro Swift (página 210 del iBook tal como está hoy).

DK_
fuente
77
Los parámetros 'var' están en desuso y se eliminarán en Swift 3
Regis St-Gelais
1
No es válido para Swift 4 y versiones posteriores.
ilkayaktas
0

Hay algunos casos en los que no necesitamos usar inout

Podemos usar algo como esto si desea que los cambios / alcance estén solo dentro de la función:

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

Solución usando Swift5 con programación funcional ...

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