Swift如何使方法参数可变?

175

如何在不创建额外变量的情况下解决这个错误?

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

我不想创建额外的变量来存储 x 的值。我想知道是否有可能做到我想要的。


4
请见以下更新后的答案,Swift 3 已经废弃了您之前接受的答案。 - esreli
7个回答

254

如其他答案所述,自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 声明指南可以在这里找到。


2
非常感谢!!!我在一个递归问题上卡住了。你救了我的命。 - JW.ZG
3
使用时需谨慎,因为这会修改函数作用域外的变量。理想情况下,您应该明确返回函数内更改的值。 - Chris Gunawardena
2
在当前的Swift 3版本中,应该将inout关键字放置在参数名称和参数类型之间,就像这样:func reduceToZero(x: inout Int) - Agustí Sánchez
除了这似乎对闭包无效,因为闭包显然只通过值捕获inout参数(至少这是Xcode给我的错误消息)。在这种情况下,我使用@GeRyCh的解决方案。 - wcochran
1
这并不是在参数列表中删除 var 关键字的合适替代方法。你应该使用一个变量来遮蔽原始(常量)参数:var x = x 应该是函数的第一行。 - BallpointBen
显示剩余4条评论

54

'var'参数已被弃用,并将在Swift 3中移除。 因此,现在似乎分配给一个新参数是最好的方法:

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

如此提到:'var'参数已被弃用,并将在Swift 3中删除


1
在这种情况下,它实际上会复制 x 到新的 var x 中吗?还是 Swift 做了更有效率的事情? - Genki
3
这个方法可以行得通,而且这也是我所做的,但它似乎非常不自然。 - wcochran
2
@Gomfucius 在 Swift 3.1 指南中没有任何相关说明。在这种情况下(x适合于寄存器),几乎没有成本。如果 x 是数组、结构体或被修改的对象,则几乎肯定需要执行复制(除非优化器可以内联分析并重命名它)。 - wcochran
2
@wcochran 这是一个很棒的技巧,但实际上并没有什么特别的。它只是用本地变量副本来遮盖输入参数。在 OP 的情况下,这比使用 inout 更好地替代了 var args,因为后者可能会产生意想不到的副作用,尤其是当变量是指针时。 - Echelon
那么我们应该使用这个方法还是inout方法? - anonymouse69

46

对于Swift 1和2(有关Swift 3,请参见achi的答案,使用一个inout参数):在Swift中,函数的参数默认为 let,因此如果需要修改该值,请将其更改为var

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

2
为什么这个答案被点赞的这么疯狂?另一个答案比这个更早发布,并且包含比这个更多的信息。 - Cristik
18
使用"var"关键字将创建传递参数变量的副本。因此,修改它不会修改原始值。此外,根据https://github.com/apple/swift-evolution/blob/master/proposals/0003-remove-var-parameters-patterns.md,在较新的Swift版本中,函数参数中的"var"关键字很可能会消失。 - Matthieu Riegler
18
在Swift 3中,方法参数中的var关键字将被弃用。 - Boon
4
我认为使用Swift 3,我们将不能再这样做了。我们必须创建一个数组变量的副本,并返回修改后的数组。 - C0D3
这个答案是正确的答案:https://dev59.com/yWAf5IYBdhLWcg3w_G2r#36993835 - esreli

18

传递可变数组指针的 Swift 3 解决方案。

函数:

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

函数调用:

var a = Array<Int>()
foo(array:&a)

说实话,我不确定这是否正确,因为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书籍中“函数”章节的“常量和变量参数”小节(今天的iBook页面为210页)。点击此处

8
“var”参数已被Swift 3弃用并将被移除。 - Regis St-Gelais
3
不适用于Swift 4及更新版本。 - ilkayaktas

6

有些情况下,我们不需要使用 inout

如果你希望更改/作用域仅限于函数内部,可以采用以下方法:

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

0

使用Swift5和函数式编程的解决方案...

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接