Swift数组缩减:无法在不可变值上使用可变成员

3
我有以下尝试合并数组冗余元素的代码:
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。发生了什么问题,我应该怎样解决它?


2
let uniqueItems = Set(items) 更简单... - Martin R
很好的解决方案,Martin。我的代码实际上不使用字面量而是对象,所以 Set(items) 不起作用。那是因为我需要为对象定义等号运算符吗?编辑:刚刚看到它们需要是可哈希的。 - raphaelrk
@coolcade,是的,你的对象需要实现Hashable协议(这也意味着你的对象是Equatable)。 - zneak
我意识到我在比较对象时本应该比较它们存储的枚举,所以你的解决方案适用于我想要做的事情。谢谢,马丁。 - raphaelrk
3个回答

3

这个代码的问题在于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 调用,但错误仍然出现。 - raphaelrk
@coolcade,那是因为你的修改没有解决函数参数(包括闭包参数)隐式不可变的问题。此外,即使你现在有了一个返回语句,set.insert 仍然不返回值,不能与 reduce 一起使用(或者至少不是那么容易)。 - zneak
1
啊,我明白你们两个的观点了,参数是不可变的,而且插入没有返回值。现在我在我的代码中使用了Set<String>(items)的解决方案,它完美地工作了。谢谢,zneak。 - raphaelrk

0

返回的'set'值是一个常量。这很重要,因为它是累加器,代表迄今积累的值。在闭包中它不应该改变。

下面是我目前正在处理的项目中的一个示例,我想要找到所有独特的表演者,跨越多个剧场演出。请注意,我使用的是union,它不会修改常量值'performers',而是使用它来生成一个新值。

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

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