【问题标题】:Swift function that takes in array giving error: '@lvalue $T24' is not identical to 'CGFloat'接受数组给出错误的Swift函数:'@lvalue $T24'与'CGFloat'不同
【发布时间】:2014-07-11 22:00:17
【问题描述】:

所以我正在编写一个低通加速度计函数来缓和加速度计的抖动。我有一个 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] = ... 形式的三行给了我错误。关于我为什么会收到此错误的任何想法?

【问题讨论】:

  • 作为我的旁注,我传递给它一个 [CGFloat] 数组
  • 您遇到了什么错误?发生了哪些错误?
  • 错误出现在标题中,并且所有这三行都完全相同。上面提到的 lastVector 也是一个 CGFloat 数组,被定义为一个全局变量。它不是错误的根源,因为没有它,同样的错误仍然存​​在。

标签: ios arrays swift cgfloat


【解决方案1】:

如果您使用 inout 修饰符传递数组,该代码似乎可以编译:

func lowPass(inout vector:[CGFloat]) -> [CGFloat] {
    ...
}

我不确定这是否是错误。本能地,如果我将一个数组传递给一个函数,我希望能够修改它。如果我使用 inout 修饰符传递,我希望能够使原始变量指向一个新数组 - 类似于 & 修饰符在 C 和 C++ 中所做的。

也许背后的原因是在 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 修饰符。这是违反直觉的。

但是在阅读了 swift book 中的this excerpt 之后,一切都得到了澄清

Swift 的 String、Array 和 Dictionary 类型是作为结构实现的。这意味着字符串、数组和字典在分配给新的常量或变量时,或者在传递给函数或方法时会被复制。

因此,当传递给 func 时,它不是原始数组,而是它的副本 - 因此对其进行的任何更改(即使可能)都不会在原始数组上进行。

所以,最后,我上面的原始答案是正确,而经历的行为不是错误

非常感谢@newacct :)

【讨论】:

  • yw - 但这听起来像一个错误,所以我认为你应该提交一个雷达
  • 绝对不是错误。这是正确的行为。为了修改一个数组(从 Xcode 6 beta 3 开始),它需要是可赋值的,而不是一个常量(修改它在语义上等同于分配一个新数组)。参数可赋值的唯一方法是声明它是var(在这种情况下,对其进行更改不会影响调用者)或inout(在这种情况下,对其进行更改确实会影响调用者范围内的变量。所有的出于这个原因,排序函数现在必须将数组作为inout
  • @newacct 感谢您指出这一点。在我的原始答案中添加了一些注释,解释了为什么它不是错误
【解决方案2】:

从 Xcode 6 beta 3 开始,修改 Array 的内容是一种变异操作。你不能修改一个常量(即let)数组;您只能修改非常量(即var)数组。

默认情况下,函数的参数是常量。因此,您不能修改vector 的内容,因为它是一个常量。与其他参数一样,有两种方法可以更改参数:

  • 声明为var,这种情况下可以赋值给它,但还是传值,所以对参数的任何改动都不会影响调用范围。
  • 声明为inout,在这种情况下参数是通过引用传递的,对参数的任何更改就像您在调用范围内对变量所做的更改一样。

您可以在 Swift 标准库中看到所有接受 Array 并对其进行变异的函数,例如 sort(),将 Array 视为 inout

附:这就像数组在 PHP 中的工作方式一样

【讨论】:

  • 修改数组是指修改数组的大小,不是下标访问。数组的大小没有被修改。
  • @wbennett:不再。他们改变了它。
  • 我正在更新到 beta 3 ATM。当你用xcrun swift 运行它时会发生什么?让 arr = [0,1,2]; arr[0] = 2
  • @wbennett:它不会让你
  • 太棒了。离开一个星期,你会觉得你生活在一块岩石下。你可以编辑它,以便我可以撤回我的反对票吗?我投反对票做你说不能做的事,以为我有最新的。
【解决方案3】:

编辑:以下内容适用于 Xcode Beta 2。显然,数组的语法和行为在 Beta 3 中发生了变化。如果数组是不可变的(未声明 inout 或变量):

对语言的最新更改无效

我可以让它在操场上工作的唯一方法是改变你声明数组的方式。我建议试试这个(在操场上工作):

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]);

【讨论】:

  • 有趣——这些应该是等价的,所以听起来这可能是新数组类型语法的错误。
  • 我同意,当我尝试使用该语法时,在xcrun swift 下运行会给我一个段错误。尽管语言手册给出了 [Type] 用法的明确示例。
  • 该语法在新的 swift 中无效,但我应该报告该错误
  • 确实如此。如果错误仅影响另一种情况,这可能部分解释了为什么将向量作为 inout 传递有效
  • 绝对不是错误。这是正确的行为。
【解决方案4】:

主要作为后续参考,@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
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 1970-01-01
    • 2021-05-07
    • 2019-09-26
    • 2013-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多