Swift сделать параметр метода изменяемым?

Как я могу справиться с этой ошибкой, не создавая дополнительную переменную?

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

Я не хочу создавать дополнительную переменную только для хранения значения x. Возможно ли вообще делать то, что я хочу?


person Gabriel    schedule 06.06.2014    source источник
comment
Смотрите обновленные ответы ниже, Swift 3 устарел от вашего принятого ответа.   -  person achi    schedule 03.05.2016


Ответы (7)


Как указано в других ответах, начиная с Swift 3, помещая var перед тем, как переменная устарела. Хотя в других ответах не указано, это возможность объявить параметр inout. Подумайте: передача указателя.

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

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

Это может быть особенно полезно в рекурсии.

Руководство Apple по декларированию inout можно найти здесь.

person achi    schedule 03.05.2016
comment
Большое Вам спасибо!!!! Я застрял здесь на вопросе рекурсии. Вы спасли мою жизнь. - person JW.ZG; 20.07.2016
comment
Следует использовать это с осторожностью, так как это изменяет переменные вне области действия функции. В идеале вы хотите явно вернуть значение, которое вы изменили внутри функции. - person Chris Gunawardena; 14.08.2016
comment
Ключевое слово inout должно быть помещено между именем параметра и типом параметра следующим образом: func reduceToZero(x: inout Int) в текущей версии Swift 3. - person Agustí Sánchez; 24.09.2016
comment
За исключением того, что это, похоже, не работает для замыканий, поскольку замыкания, очевидно, захватывают параметры inout только по значению (по крайней мере, это сообщение об ошибке, которое дает мне Xcode). В этом случае я использую решение @GeRyCh. - person wcochran; 14.04.2017
comment
Спасибо. На данный момент это работает, но это похоже на использование указателей в C. Выживет ли это в другой версии Swift? - person Krishna Vedula; 26.07.2017
comment
Это не подходящая замена для удаления ключевого слова var в списках параметров. Вы должны затенить исходный (постоянный) аргумент переменной: var x = x должно быть первой строкой функции. - person BallpointBen; 27.03.2018
comment
Я бы не стал проводить сравнение с указателем по той простой причине, что вы не изменяете адрес, который должен быть разыменован (на языке C++). inout скорее делает поведение похожим на такой язык, как Java, который "передается по ссылке" и полностью изменчив. Разработчики Swift решили сделать аргументы метода неизменяемыми в качестве защиты, поэтому об обходе этого поведения нужно подумать немного больше (что, как вы можете утверждать, имеет некоторую мудрость). - person SilentDirge; 25.06.2018
comment
Это изменяет этот Int за пределами области действия функции, что плохо и может привести к неожиданному поведению. - person coolcool1994; 19.05.2021
comment
В чем смысл _ перед x? - person anonymouse69; 10.07.2021

Параметры 'var' устарели и будут удалены в Swift 3. Таким образом, назначение нового параметра кажется лучшим способом:

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

как указано здесь: параметры 'var' устарели и будет удалено в Swift 3

person GeRyCh    schedule 04.04.2016
comment
В этом случае действительно ли он копирует x в новый var x? Или Swift делает что-то более эффективное? - person Genki; 10.02.2017
comment
Это работает, и это то, что я делаю, но это кажется очень неудобным. - person wcochran; 14.04.2017
comment
@Gomfucius Ни слова об этом в руководстве Swift 3.1. В этом случае (x вписывается в регистр) затрат практически нет. Если x является измененным массивом, структурой или объектом, то почти наверняка необходимо выполнить копирование (если только оптимизатор не может проанализировать его как встроенный и псевдоним). - person wcochran; 14.04.2017
comment
@wcochran Это ловкий трюк, но на самом деле ничего особенного не происходит. Это просто затмевает входной параметр локальной копией var. В ситуации OP это лучшая замена var args, чем использование inout, которое может иметь непреднамеренные побочные эффекты, особенно. если var был указателем. - person Echelon; 23.06.2017
comment
Итак, мы должны использовать этот метод или метод inout? - person anonymouse69; 10.07.2021

Для Swift 1 и 2 (для Swift 3 см. ответ achi с использованием параметра inout): Аргумент функции в Swift по умолчанию равен let, поэтому измените его на var, если вам нужно изменить значение, т.е.

func reduceToZero(var x:Int) -> Int {
    while (x != 0) {
        x = x-1     
    }
    return x
}
person LML    schedule 06.06.2014
comment
Почему за этот ответ проголосовали, как в аду? Другой ответ был помещен перед этим и содержит больше информации, чем этот. - person Cristik; 16.12.2015
comment
/!\ Использование var создаст копию переменной, переданной в параметрах. Таким образом, его изменение не изменит исходное значение. Также var в параметрах, скорее всего, исчезнет в новых версиях Swift согласно github.com/apple/swift-evolution/blob/master/proposals/ - person Matthieu Riegler; 17.12.2015
comment
Ключевое слово var в параметре метода будет объявлено устаревшим в Swift 3. - person Boon; 15.05.2016
comment
Я думаю, со Swift 3 мы больше не сможем этого делать. Нам нужно будет создать переменную копию массива и вернуть этот измененный массив. - person C0D3; 18.05.2016
comment
Этот ответ является правильным ответом: stackoverflow. ком/вопросы/24077880/ - person achi; 28.10.2016

Ответ Swift3 для передачи указателя изменяемого массива.

Функция:

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

Вызов функции:

var a = Array<Int>()
foo(array:&a)
person joshd    schedule 31.07.2016
comment
Честно говоря, я не уверен, что это правильно, так как земля Swift постоянно меняется. Я подумал, что это лучше, чем выполнение var array = array внутри функции, потому что это создает копию (и фактически не влияет на исходную структуру массива)? Является ли лучший подход к дизайну вышеупомянутого подхода var, а затем возвращает новый мутированный массив? - person joshd; 31.07.2016

В Swift вы просто добавляете ключевое слово var перед именем переменной в объявлении функции:

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

См. подраздел «Постоянные и переменные параметры» в глава "Функции" книги Swift (страница 210 iBook в ее нынешнем виде).

person DK_    schedule 06.06.2014
comment
Параметры 'var' устарели и будут удалены в Swift 3. - person Regis St-Gelais; 19.05.2016
comment
Недействительно для Swift 4 и новее. - person ilkayaktas; 18.11.2018

В некоторых случаях нам не нужно использовать inout

Мы можем использовать что-то вроде этого, если вы хотите, чтобы изменения/область были только внутри функции:

func manipulateData(a: Int) -> Int {
    var a = a
    // ...
}
person dheeru    schedule 11.07.2019

Решение с использованием Swift5 с функциональным программированием...

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