协议符合Sequence协议,同时消除了通用约束问题

时间:2017-03-31 15:45:25

标签: swift generics tree protocols sequence

尝试创建用于在树中创建不同节点的协议。简化版本是:

protocol NodeElement {
    var children: [NodeElement] { get }
}

此协议可用于创建 struct ,可用于在树中创建节点。

struct TextNode: NodeElement {
    var text: String
    var children: [NodeElement]
}

struct NumberNode: NodeElement {
    var number: Int
    var children: [NodeElement]
}

使用这些结构,可以创建包含不同类型的树。

let root = TextNode(text: "Hello", children: [NumberNode(...), ...])

我希望 NodeElement 符合序列协议,以允许在for...in循环内进行预订树遍历。

这可以通过

完成
protocol NodeElement: Sequence { ... }

extension NodeElement {
    func makeIterator() -> AnyIterator<NodeElement> {
        var stack: [NodeElement] = [self]

        return AnyIterator {
            if let next = stack.first {
                stack.remove(at: 0)
                stack.insert(contentsOf: next.content, at: 0)
                return next
            }
            return nil
        }
    }
}

如果这样做,则会发生以下 ERROR

error: protocol 'NodeElement' can only be used as a generic constraint
because it has Self or associated type requirements

var content: [NodeElement] { get }
              ^

即使我认为我完全符合序列协议,也会发生这种情况。

如果没有让NodeElement成为 struct 并让所有节点继承它,有什么方法可以让它工作吗?

1 个答案:

答案 0 :(得分:0)

NodeElement具有对自身的引用,因此不能用于定义具体类型。由于这个原因,你将无法使它成为一个序列。

然而,您可以通过简单地将一个计算变量添加到协议而不是使其成为序列本身来实现类似的功能。

例如:

extension NodeElement 
{
    var treeNodes : AnyIterator<NodeElement>  
    {
        var stack: [NodeElement] = [self]

        return AnyIterator {
            if let next = stack.first {
                stack.remove(at: 0)
                stack.insert(contentsOf: next.children, at: 0)
                return next
            }
            return nil
        }
    }
}

然后,您将能够使用该变量在for ... in循环中使用您的协议:

for node in root.treeNodes
{
   ...
}