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

123

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

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

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

Габриель
источник
3
См. Обновленные ответы ниже, Swift 3 устарел от вашего принятого ответа.
achi

Ответы:

193

Как указано в других ответах, с момента размещения в 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'

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

С inoutправилами декларации Apple можно ознакомиться здесь .

ACHI
источник
2
Большое спасибо!!!! Я застрял здесь на вопросе о рекурсии. Вы спасли мою жизнь.
JW.ZG
2
Следует использовать это с осторожностью, так как это изменяет переменные вне области действия функции. В идеале вы хотите явно вернуть значение, которое вы изменили внутри функции.
Крис Гунавардена,
2
inoutключевое слово должно быть помещено между именем параметра и типом параметра следующим образом: func reduceToZero(x: inout Int) в текущей версии Swift 3.
Агусти Санчес,
За исключением того, что, похоже, это не работает для закрытий, поскольку закрытие, очевидно, только захватывает входящие параметры по значению (по крайней мере, это сообщение об ошибке, которое дает мне Xcode). В этом случае я использую решение @GeRyCh.
wcochran
Спасибо. Пока это сработало, но это все равно, что использовать указатели в C. Выживет ли это в другой версии Swift?
Krishna Vedula
45

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

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

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

Gerych
источник
1
В этом случае, действительно ли он копирует xв новое var x? Или Swift делает что-то более эффективное?
Genki
3
Это работает, и я делаю это, но мне это кажется очень неудобным.
wcochran
1
@Gomfucius Ни слова об этом в руководстве по Swift 3.1. В этом случае ( xпомещается в регистр) практически нет затрат. Если xэто измененный массив, структура или объект, то почти наверняка необходимо выполнить копирование (если только оптимизатор не может проанализировать его в строке и присвоить ему псевдоним).
wcochran
1
@wcochran Это отличный трюк, но на самом деле здесь ничего особенного не происходит. Он просто заменяет входной параметр локальной копией var. В ситуации с OP это лучшая замена varаргументам, чем использование, inoutкоторое может иметь непредвиденные побочные эффекты, особенно. если var была указателем.
Echelon
45

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

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

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

Функция:

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

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

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

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

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

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

DK_
источник
7
Параметры 'var' устарели и будут удалены в Swift 3
Regis St-Gelais
1
Не действует для Swift 4 и новее.
ilkayaktas
0

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

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

func manipulateData(a: Int) -> Int {
    var a = a
    // ...
}
dheeru
источник
0

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

func reduceToZeroFP(x:Int) -> Int {
    x == 0 ? x : reduceToZeroFP(x: x - 1)
}
Жюль Берт
источник