为什么这段代码含糊不清?

Ale*_*ica 1 generics ambiguous swift swift2

我已经写了这个扩展SequenceType来模仿Python的collections.Counter.

let input = [
    "a", "a", "a", "a", "a",
    "b", "b", "b", "b",
    "c", "c", "c",
    "d", "d",
    "e"
]


let counts = input.countRepetitions()

print(counts) //expected result: ["a": 5 , "b" : 4, "c" : 3, "d" : 2, "e" : 1]
Run Code Online (Sandbox Code Playgroud)

这是代码:

extension SequenceType where Self.Generator.Element : Hashable {
    func countRepetitions() -> [Self.Generator.Element : Int] {
        return self.reduce([Self.Generator.Element : Int]()) { dict, element in
            dict[key: element] = (dict[element] ?? 0) + 1
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

Playground execution failed: OS X.playground:26:22: error: type of expression is ambiguous without more context
                return self.reduce([Self.Generator.Element : Int]()) { dict, element in
                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

Mar*_*n R 6

在使用嵌套类型定义变量时,这似乎是一个限制.而

let foo1: [Self.Generator.Element : Int] = [:]
Run Code Online (Sandbox Code Playgroud)

在你的方法中编译,这不会:

let foo2 = [Self.Generator.Element : Int]()
//  error: type of expression is ambiguous without more context
Run Code Online (Sandbox Code Playgroud)

作为解决方法,您可以定义一个类型:

typealias E = Self.Generator.Element
let foo3 = [E : Int]()
Run Code Online (Sandbox Code Playgroud)

适用于您的案例:

extension SequenceType where Self.Generator.Element : Hashable {
    func countRepetitions() -> [Self.Generator.Element : Int] {
        typealias E = Self.Generator.Element
        return self.reduce([E : Int]()) { (dict, element) ->  [E : Int] in
            var dict = dict
            dict[element] = (dict[element] ?? 0) + 1
            return dict
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(请注意,闭包参数是常量,因此您必须首先创建一个可变副本.此外,闭包必须返回一个值.)

但实际上你可以避免这个问题并让编译器推断出类型:

extension SequenceType where Self.Generator.Element : Hashable {
    func countRepetitions() -> [Self.Generator.Element : Int] {
        return self.reduce([:]) { (dict, element) in
            var dict = dict
            dict[element] = (dict[element] ?? 0) + 1
            return dict
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

另请注意,reduce在每个迭代步骤中创建一个新字典.一个更有效的解决方案是

extension SequenceType where Generator.Element : Hashable {
    func countRepetitions() -> [Generator.Element : Int] {
        var dict: [Generator.Element: Int] = [:]
        self.forEach {
            dict[$0] = (dict[$0] ?? 0) + 1
        }
        return dict
    }
}
Run Code Online (Sandbox Code Playgroud)

我在哪里也省略了(冗余)Self..