Swift torna o parâmetro do método mutável?

123

Como posso lidar com esse erro sem criar variável adicional?

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

Não quero criar variável adicional apenas para armazenar o valor de x. É possível fazer o que eu quero?

Gabriel
fonte
3
Veja as respostas atualizadas abaixo, o Swift 3 substituiu sua resposta aceita.
Achi

Respostas:

191

Conforme declarado em outras respostas, a partir do Swift 3, coloque var antes que uma variável tenha sido descontinuada. Embora não seja indicado em outras respostas, é a capacidade de declarar um inoutparâmetro. Pense: passando um ponteiro.

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

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

Isso pode ser particularmente útil na recursão.

As inoutdiretrizes de declaração da Apple podem ser encontradas aqui .

achi
fonte
2
Muito obrigado!!!! Estou preso aqui em uma pergunta de recursão. Você salvou minha vida.
19416 JW.ZG
2
Deve-se usar isso com cautela, pois isso modifica variáveis ​​fora do escopo da função. Idealmente, você deseja retornar explicitamente o valor alterado dentro da função.
Chris Gunawardena
2
inoutA palavra-chave deve ser colocada entre o nome e o tipo de parâmetro da seguinte forma: func reduceToZero(x: inout Int) na versão atual do Swift 3.
Agustí Sánchez
Exceto que isso não parece funcionar para fechamentos, pois os fechamentos capturam apenas os parâmetros inout por valor (pelo menos essa é a mensagem de erro que o Xcode me fornece). Eu uso a solução @GeRyCh neste caso.
wcochran
Obrigado. Isso funcionou por enquanto, mas é como usar ponteiros em C. Isso sobreviveria a outra versão Swift?
Krishna Vedula
45

Os parâmetros 'var' foram descontinuados e serão removidos no Swift 3. Portanto, a atribuição de um novo parâmetro parece a melhor maneira agora:

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

como mencionado aqui: os parâmetros 'var' foram descontinuados e serão removidos no Swift 3

GeRyCh
fonte
1
Nesse caso, ele realmente copia o xno novo var x? Ou a Swift está fazendo algo mais eficiente que isso?
Genki
3
Isso funciona e é o que eu faço, mas parece muito estranho.
Wcochran
1
@ Gomfucius Nenhuma palavra sobre isso no guia Swift 3.1. Nesse caso ( xcabe no registro), praticamente não há custo. Se xfor uma matriz, estrutura ou objeto que foi alterado, uma cópia quase certamente precisa ser executada (a menos que o otimizador possa analisá-la em linha e alias).
Wcochran
1
@wcochran Este é um truque legal, mas realmente não há nada de especial acontecendo. Simplesmente eclipsa um parâmetro de entrada com uma cópia var local. Na situação do OP, é melhor substituir os varargumentos do que usar os inoutque podem ter efeitos colaterais indesejados, esp. se o var fosse um ponteiro.
Echelon
45

Para Swift 1 e 2 (para Swift 3, consulte a resposta de achi usando um parâmetro inout): O argumento de uma função no Swift é letpor padrão; portanto, altere para varse você precisar alterar o valor, por exemplo,

func reduceToZero(var x:Int) -> Int {
    while (x != 0) {
        x = x-1     
    }
    return x
}
LML
fonte
2
Por que essa resposta foi votada como o inferno? A outra resposta foi colocada antes desta e contém mais informações que esta.
Cristik
16
/! \ O uso de var criará uma cópia da variável transmitida nos parâmetros. Portanto, modificá-lo não modificará o valor original. Também varnos parâmetros é muito provável que desapareça nas versões mais recentes do Swift por github.com/apple/swift-evolution/blob/master/proposals/…
Matthieu Riegler
17
var palavra-chave no paremeter método será depreciado em Swift 3.
Boon
4
Eu acho que com o Swift 3, não poderemos mais fazer isso. Teremos que criar uma cópia variável da matriz e retornar essa matriz modificada.
C0D3 18/05
Esta resposta é a resposta correta: stackoverflow.com/questions/24077880/...
Achi
14

Resposta Swift3 para passar o ponteiro do array mutável.

Função:

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

Chamada para funcionar:

var a = Array<Int>()
foo(array:&a)
Josh
fonte
Honestamente, não tenho certeza se isso está correto, pois o terreno do Swift está sempre mudando. Eu pensei isso melhor do que fazer var array = array dentro da função, porque isso faz uma cópia (e na verdade não afeta a estrutura original da matriz)? É uma abordagem de design melhor à abordagem var mencionada acima e depois retorna a nova matriz mutada?
joshd
7

No Swift, você apenas adiciona a varpalavra - chave antes do nome da variável na declaração da função:

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

Consulte a subseção "Parâmetros constantes e variáveis" no capítulo "Funções" do livro Swift (página 210 do iBook como é hoje).

DK_
fonte
7
Os parâmetros 'var' foram descontinuados e serão removidos no Swift 3
Regis St-Gelais
1
Não é válido para o Swift 4 e mais recente.
ilkayaktas
0

Existem alguns casos em que não precisamos usar inout

Podemos usar algo assim se você quiser que as alterações / escopo sejam apenas dentro da função:

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

Solução usando Swift5 com programação funcional ...

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