【问题标题】:Swift array reduction: cannot use mutating member on immutable valueSwift 数组缩减:不能在不可变值上使用变异成员
【发布时间】:2016-06-28 05:36:47
【问题描述】:

我有以下代码尝试合并数组的冗余元素:

var items : [String] = ["hello", "world", "!", "hello"]

var mutableSet = Set<String>()

items.reduce(mutableSet, combine: { (set: Set<String>, element: String) in
    return set.insert(element)
})

set.insert(element) 给了我错误Cannot use mutating member on immutable value: 'set' is a 'let' constant。出了什么问题,我该如何解决?

【问题讨论】:

  • let uniqueItems = Set(items) 会更简单...
  • 很好的解决方案,马丁。我的代码实际上不使用文字而是使用对象,因此 Set(items) 不起作用。那是因为我需要为对象定义相等运算符吗?编辑:刚刚看到它们需要是可散列的
  • @coolcade,是的,您的对象需要实现Hashable 协议(这反过来意味着您的对象是Equatable)。
  • 当我打算比较对象存储的枚举时,我意识到我正在比较对象,因此您的解决方案适用于我正在尝试做的事情。谢谢,马丁。

标签: swift functional-programming immutability


【解决方案1】:

OP 代码的问题在于 reduce 中的累加器是不可变的,因此它不允许您使用变异函数 insert()。

一个整洁的解决方案是在 Set 的扩展中定义一个与 insert() 等效的非变异,称为 inserting(),如下所示。

extension Set {
    //returns a new set with the element inserted
    func inserting(_ element: Element) -> Set<Element> {
        var set = self
        set.insert(element)
        return set
    }
}

现在我们可以编写如下reduce

var items : [String] = ["hello", "world", "!", "hello"]

let set = items.reduce(Set<String>()){ accumulator, element in
    accumulator.inserting(element)
}

【讨论】:

    【解决方案2】:

    在 Swift 中,集合是值类型。不能修改用 let 声明的值类型变量(隐含地是函数参数)。此外,您的闭包不会返回任何内容,因此 reduce 可能不会成功。

    我相信reduce 不是最适合这项任务的工具。请考虑这个for 循环:

    var set = Set<String>()
    for element in items { set.insert(element) }
    

    另一个更简单的选择是使用unionInPlace 方法:

    var set = Set<String>()
    set.unionInPlace(items)
    

    也许更好的是,直接从集合中创建集合:

    var set = Set<String>(items)
    

    【讨论】:

    • 感谢您提供更好的解决方案,zneak。我修改了原帖,在reduce调用前创建了mutableSet,并添加了return调用,但还是报错。
    • @coolcade,那是因为您的修改没有解决函数参数(和闭包参数)隐式不可变的事实。此外,即使您现在有一个 return 语句,set.insert 仍然没有返回值并且不能与 reduce 一起使用(或者至少,不是那么容易)。
    • 啊,我看到了你的两个观点,参数是不可变的,插入什么也不返回。我现在在我的代码中使用 Set(items) 解决方案,它运行良好。谢谢,zneak。
    【解决方案3】:

    返回的 'set' 值是一个常数。这很重要,因为它是累加器,它代表到目前为止已经积累的值。它不应该在你的关闭中改变。

    这是我目前正在进行的一个项目的示例,我想在其中找到所有独特的表演者,跨越许多戏剧表演。请注意我是如何使用 union 的,它不会修改常量值“performers”,而是使用它来产生一个新值。

    let uniquePerformers = performances.reduce(Set<Performer>(), { (performers: Set<Performer>, performance) -> Set<Performer> in
        return performers.union(Set(performance.performers))
    })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-23
      • 2021-10-12
      • 2019-03-23
      • 1970-01-01
      • 1970-01-01
      • 2020-08-08
      相关资源
      最近更新 更多