【问题标题】:Printing a Tree With Indents. Swift打印带有缩进的树。迅速
【发布时间】:2017-09-22 18:56:29
【问题描述】:

使用 swift 实现 Tree 数据结构:

class Node {

    var value: String
    var children: [Node] = []
    weak var parent: Node?

    init(_ value: String) {
        self.value = value
    }

    func add(_ child: Node){
        children.append(child)
        child.parent = self
    }

    func printTree() {
        var text = self.value
        if !self.children.isEmpty {
            text += "\n  " + self.children.map{$0.printTree()}.joined(separator: ", ")
        }
        print(text)
    }

}

我的目标是看到这样的东西:

A1
    B2
    C3
        G6
    K0
        H7
            L8
        L9

我知道应该有一些聪明的方法来插入缩进,但我也为“地图”而苦恼。编译器给了我“对成员 'map' 的模棱两可的引用”。

【问题讨论】:

  • 代替(或附加于)printTree() 方法,您可以实现CustomStringConvertibledescription 方法,然后您可以使用print(node) 打印树

标签: swift data-structures tree


【解决方案1】:

如果你想让它漂亮,你可以这样做:

extension Node
{
   func treeLines(_ nodeIndent:String="", _ childIndent:String="") -> [String]
   {
      return [ nodeIndent + value ]
           + children.enumerated().map{ ($0 < children.count-1, $1) }
             .flatMap{ $0 ? $1.treeLines("┣╸","┃ ") : $1.treeLines("┗╸","  ") }
             .map{ childIndent + $0 } 
   }

   func printTree()
   { print(treeLines().joined(separator:"\n")) }
}

a1.printTree()

// A1
// ┣╸B2
// ┣╸C3
// ┃ ┗╸G6
// ┗╸K0
//   ┣╸H7
//   ┃ ┗╸L8
//   ┗╸L9

您还可以将其概括为任何树结构的打印函数,让您选择为每个节点打印什么:

func printTree<T>(_ node:T, _ nodeInfo:@escaping (T)->(String,[T]) ) 
{
  func lines(_ aNode:T, _ nodeIndent:String="", _ childIndent:String="") -> [String]
  {
    let (label,children) = nodeInfo(aNode)
    return [ nodeIndent + label]
         + children.enumerated().map{ ($0 < children.count-1, $1) }
           .flatMap{ $0 ? lines($1,"┣╸","┃ ") :lines($1,"┗╸","  ") }
           .map{ childIndent + $0 }
  }
  print( lines(node).joined(separator:"\n") )
}

// print a root node providing a capture to obtain the node's label
// and its array of children

printTree(a1){ ($0.value,$0.children) }

// works for any tree structure.  for example, views :

printTree(view){( "\(type(of:$0)) \($0.frame)", $0.subviews )} 

【讨论】:

    【解决方案2】:

    我觉得 vacawama 的回答很聪明,但是如果你想练习递归调用包括maps,你可以这样写:

    class Node {
        //...
    
        func treeLines() -> [String] {
            return [self.value] + self.children.flatMap{$0.treeLines()}.map{"    "+$0}
        }
        func printTree() {
            let text = treeLines().joined(separator: "\n")
            print(text)
        }
    }
    

    测试:

    let a1 = Node("A1")
    let b2 = Node("B2")
    let c3 = Node("C3")
    let g6 = Node("G6")
    let k0 = Node("K0")
    let h7 = Node("H7")
    let l8 = Node("L8")
    let l9 = Node("L9")
    a1.add(b2)
    a1.add(c3)
    a1.add(k0)
    c3.add(g6)
    k0.add(h7)
    k0.add(l9)
    h7.add(l8)
    a1.printTree()
    

    输出:

    A1
        B2
        C3
            G6
        K0
            H7
                L8
            L9
    

    几点:

    • 您的 printTree() 是一个 void 函数,因此在 map 中使用它会产生一个空元组数组,它​​不能是 joined

      (在 Swift 中,void 函数可以被视为返回空元组。当找不到合适的重载时,Swift 会生成对成员的模糊引用。)

      您可能需要一个中间函数,其结果可以表示部分树结构。

    • 每行都需要缩进,所以中间结果应该很容易分成多行。

    【讨论】:

    • 感谢您指出我不能在“地图”中使用 Void func。
    • 还是不明白flatMap{$0.treeLines()}的用法...我以为flatMap中的$0是数组本身,而不是它的成员?
    • @Evgeny,当闭包为每次迭代返回一个数组时,flatMap 将所有数组连接成一个平面数组。在每次迭代中,$0 成为self.children 中的一个子级,因此$0.treeLines() 生成一个String 数组,flatMap 将所有这些数组连接成一个String 数组。如果您需要更多图解说明,我会更新我的答案。
    【解决方案3】:

    这样的事情应该可以工作:

    func printTree(_ indent: String = "") {
    
        print(indent + self.value)
    
        for child in children {
            child.printTree(indent + "    ")
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-11
      • 1970-01-01
      相关资源
      最近更新 更多