Swift 4 个泛型

Swift 4 Generics

我正在尝试学习 Swift 4 个泛型,并且编写了一些无法编译的代码。我不明白为什么。这是代码:

protocol NodeType {
    associatedtype T
    associatedtype E
    var parent: T {get set}
    var children: [T] {get set}
    var item: E {get set}
    func getItem()->E
    mutating func setItem(_ newItem: E)
    func getParent()->T
    mutating func setParent(_ theParent:T)
    func getChild(at index:Int)->T
    mutating func insertChild(at index:Int, child: T)
    func find(_ node:T)->Int
}

protocol ItemType:Equatable {
    associatedtype E
    var payload: E {get set}
    func matches(_ item:E)->Bool
}

struct Item<E:Equatable>: ItemType {
    var payload: E
    func matches(_ item: E) -> Bool {
        return payload == item
    }
}

struct Node<T:NodeType, E:ItemType>: NodeType {
    var parent: T
    var children: [T]
    var item: E

    func getItem()->E {
        return item
    }

    func find(_ node: T) -> Int {
        for aNode in children {
            // ********************************************************************
            // the following line fails: Value of type 'T.E' has no member 'matches'
            if aNode.getItem().matches(node) {
            }
        }
    }

    mutating func setItem(_ newItem: E) {
        item = newItem
    }

    func getParent()->T {
        return parent
    }

    mutating func setParent(_ theParent:T) {
        parent = theParent
    }

    func getChild(at index:Int)->T {
        precondition(0..<children.count ~= index)
        return children[index]
    }

    mutating func insertChild(at index:Int, child: T) {
        precondition(0...children.count ~= index)
        if index == children.count {
            // append at end
            children.insert(child, at: children.endIndex)
        }
        else {
            children.insert(child, at: index)
        }
    }
}

我的理解是,aNode 的类型 T 约束为 NodeType,而 aNode.getItem() 的类型 E 约束为 ItemType,其中包含函数 matches。谁能告诉我哪里错了。

我认为问题在于您一直使用占位符名称 TE 搞糊涂了。此声明中的E

struct Node<T:NodeType, E:ItemType>

... 不是此声明中的 E

protocol NodeType {
    associatedtype T
    associatedtype E

没有理由相信 NodeType 的 E 是 ItemType 采用者,或者它是 Equatable。

如果您在不同的泛型中为占位符选择不同的名称,这对您来说会更加明显(您可以使用 Xcode 的重构功能来帮助您)。例如:

protocol NodeType {
    associatedtype NodeTypeParent
    associatedtype NodeTypeItem
    // ...
}
protocol ItemType:Equatable {
    associatedtype ItemTypePayload
    // ...
}
struct Item<ItemPayload:Equatable>: ItemType {
    // ...
}
struct Node<NodeParent:NodeType, NodeItem:ItemType>: NodeType {
    // ...
}

现在您会收到消息

Value of type 'NodeParent.NodeTypeItem' has no member 'matches'

我觉得这样就清楚多了。

看了 Matt 的回答后,我意识到 NodeType 的基本结构是错误的,部分原因是我混淆了通用占位符。下面的代码现在可以编译了。

fileprivate let NotFound = -1

protocol NodeType {
    associatedtype Node
    associatedtype Element:Equatable
    var parent: Node? {get set}
    var children: [Node] {get set}
    var item: Element {get set}
    func isRoot()->Bool
    func getItem()->Element
    mutating func setItem(_ newItem: Element)
    func getParent()->Node?
    mutating func setParent(_ theParent:Node)
    func getChild(at index:Int)->Node
    mutating func insertChild(at index:Int, child: Node)
    func matches(_ node:Node)->Bool
    func find(_ node:Node)->Int
}

class TreeNode<ThisNodeElement:Equatable>: NodeType {
    var parent: TreeNode?
    var children: [TreeNode]
    var item: ThisNodeElement

    init(withNode node:TreeNode, value:ThisNodeElement) {
        parent = node
        item = value
        children = []
    }

    init(root value:ThisNodeElement) {
        parent = nil
        item = value
        children = []
    }

    func isRoot() -> Bool {
        return parent == nil
    }

    func getItem()->ThisNodeElement {
        return item
    }

    func setItem(_ newItem: ThisNodeElement) {
        item = newItem
    }

    func getParent()->TreeNode? {
        return parent
    }

    func setParent(_ theParent:TreeNode) {
        parent = theParent
    }

    func getChild(at index:Int)->TreeNode {
        precondition(0..<children.count ~= index)
        return children[index]
    }

    func insertChild(at index:Int, child: TreeNode) {
        precondition(0...children.count ~= index)
        if index == children.count {
            // append at end
            children.insert(child, at: children.endIndex)
        }
        else {
            children.insert(child, at: index)
        }
    }

    func matches(_ node:TreeNode)->Bool {
        return node.item == item
    }

    func find(_ node: TreeNode) -> Int {
        let index = children.index { (thisNode:TreeNode) -> Bool in
            return node.matches(thisNode)
        }
        return index ?? NotFound
    }
}