所以我正在编写一个低通加速度计功能来缓和加速度计的抖动。我有一个CGFloat数组来表示数据,我想用这个函数来阻止它:
// Damps the gittery motion with a lowpass filter.
func lowPass(vector:[CGFloat]) -> [CGFloat]
{
let blend:CGFloat = 0.2
// Smoothens out the data input.
vector[0] = vector[0] * blend + lastVector[0] * (1 - blend)
vector[1] = vector[1] * blend + lastVector[1] * (1 - blend)
vector[2] = vector[2] * blend + lastVector[2] * (1 - blend)
// Sets the last vector to be the current one.
lastVector = vector
// Returns the lowpass vector.
return vector
}
在这种情况下,lastVector在我的程序顶部定义如下:
var lastVector:[CGFloat] = [0.0, 0.0, 0.0]
表格vector [a]中的三行= ...给我错误。关于我为什么会收到此错误的任何想法?
答案 0 :(得分:6)
如果使用inout
修饰符传递数组,那么代码似乎会编译:
func lowPass(inout vector:[CGFloat]) -> [CGFloat] {
...
}
我不确定这是不是一个错误。本能地,如果我将数组传递给函数,我希望能够修改它。如果我使用inout
修饰符,我希望能够使原始变量指向一个新数组 - 类似于&
修饰符在C和C ++中的作用。 / p>
可能背后的原因是在Swift中有可变和不可变的数组(和字典)。没有inout
它被认为是不可变的,因此无法修改它。
附录1 - 这不是错误
@newacct表示预期的行为。经过一番研究,我同意他的观点。但即使不是我最初认为错误的错误(读到最后的结论)。
如果我有这样的课程:
class WithProp {
var x : Int = 1
func SetX(newVal : Int) {
self.x = newVal
}
}
我可以将该类的实例传递给函数,该函数可以修改其内部状态
var a = WithProp()
func Do1(p : WithProp) {
p.x = 5 // This works
p.SetX(10) // This works too
}
,而不必将实例作为inout
传递。
我可以使用inout
来使a
变量指向另一个实例:
func Do2(inout p : WithProp) {
p = WithProp()
}
Do2(&a)
使用该代码,在Do2
内,我将p
参数(即a
变量)指向新创建的WithProp
实例。
对于数组不能做同样的事情(我也假设一个字典)。要更改其内部状态(修改,添加或删除元素),必须使用inout
修饰符。这是违反直觉的。
但在阅读快速书籍中的this excerpt之后,一切都得到了澄清:
Swift的String,Array和Dictionary类型实现为结构。这意味着字符串,数组和字典在分配给新常量或变量时,或者传递给函数或方法时,都会被复制。
因此,当传递给func时,它不是原始数组,而是它的副本 - 因此对其进行的任何更改(即使可能)都不会在原始数组上完成。< / p>
所以,最后,我上面的原始答案是正确,经验丰富的行为不是错误
非常感谢@newacct:)
答案 1 :(得分:6)
从Xcode 6 beta 3开始,修改Array
的内容是一种变异操作。您无法修改常量(即let
)数组;你只能修改一个非常数(即var
)数组。
默认情况下,函数的参数是常量。因此,您无法修改vector
的内容,因为它是常量。与其他参数一样,有两种方法可以更改参数:
var
,在这种情况下你可以分配它,但它仍然按值传递,因此对参数的任何更改都不会对调用范围产生影响。inout
,在这种情况下参数通过引用传递,对参数的任何更改就像你在调用范围内对变量进行了更改一样。你可以在Swift标准库中看到所有带有数组并对其进行变异的函数,如sort()
,将数组作为inout
。
P.S。这就像数组如何在PHP中工作一样
答案 2 :(得分:0)
编辑:以下适用于Xcode Beta 2.显然,Beta 3中数组的语法和行为已发生变化。如果数组是不可变的,则不能再使用下标修改数组的内容(参数未声明为inout或VAR):
对语言的最新更改无效
我能让它在游戏场上工作的唯一方法就是改变你如何声明阵列。我建议尝试这个(在游乐场工作):
import Cocoa
let lastVector: CGFloat[] = [0.0,0.0,0.0]
func lowPass(vector:CGFloat[]) -> CGFloat[] {
let blend: CGFloat = 0.2
vector[0] = vector[0] * blend + lastVector[0] * ( 1 - blend)
vector[1] = vector[1] * blend + lastVector[1] * ( 1 - blend)
vector[2] = vector[2] * blend + lastVector[2] * ( 1 - blend)
return vector
}
var test = lowPass([1.0,2.0,3.0]);
答案 3 :(得分:0)
主要作为未来参考的后续内容,@ newacct的回答是正确的。由于原始帖子显示了一个返回数组的函数,因此该问题的正确答案是使用var
标记参数:
func lowPass(var vector:[CGFloat]) -> [CGFloat] {
let blend:CGFloat = 0.2
// Smoothens out the data input.
vector[0] = vector[0] * blend + lastVector[0] * (1 - blend)
vector[1] = vector[1] * blend + lastVector[1] * (1 - blend)
vector[2] = vector[2] * blend + lastVector[2] * (1 - blend)
// Sets the last vector to be the current one.
lastVector = vector
// Returns the lowpass vector.
return vector
}