2016-08-07 10 views
1

Ich habe diese Erweiterung auf SequenceType geschrieben, um Python's collections.Counter nachzuahmen.Warum ist dieser Code nicht eindeutig?

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] 

Hier ist der Code:

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 
     } 
    } 
} 

ich die folgende Fehlermeldung erhalten:

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 
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

Antwort

6

dass eine Beschränkung zu sein scheint, wenn eine Variable mit einem verschachtelten Typ definieren. Während

let foo1: [Self.Generator.Element : Int] = [:] 

in Ihrer Methode kompiliert, tut dies nicht:

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

Als Abhilfe können Sie eine typealias definieren:

typealias E = Self.Generator.Element 
let foo3 = [E : Int]() 

zu Ihrem Fall angewandt:

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 
     } 
    } 
} 

(Beachten Sie, dass Schließung Param Eters sind Konstanten, so müssen Sie eine veränderbare Kopie zuerst machen. Auch der Verschluss muss einen Wert zurückgeben)

Aber eigentlich kann man vermeiden das Problem und lassen Sie den Compiler den Typ ableiten.

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 
     } 
    } 
} 

Beachten Sie auch, dass reduce erstellt ein neues Wörterbuch in jeder Iteration Schritt . Eine effizientere Lösung wäre

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 
    } 
} 

sein, wo ich auch die (redundant) Self. weggelassen.

+0

Interessant. Haben Sie eine Idee, warum diese Einschränkung existiert? Und hah bitte entschuldigen Sie die Fehler, die Sie im zweiten Teil adressieren, der Compiler nicht bekommen, diese noch zu überprüfen xD – Alexander

+0

@AlexanderMomchliov: Leider kenne ich den Grund nicht. –

+0

Kennen Sie übrigens eine Möglichkeit, eine Kopie eines Wörterbuchs zurückzugeben, das ein neues Mapping in einem Ausdruck enthält? Das Wörterbuch entspricht 'Return-Array + [NewItem]' – Alexander