如何使用递归遍历大纲中的所有孩子?

Posted

技术标签:

【中文标题】如何使用递归遍历大纲中的所有孩子?【英文标题】:How to traverse all children in outline using recursion? 【发布时间】:2019-07-10 14:12:44 【问题描述】:

我想通过递归重写代码,它可以不受限制地遍历大纲中的所有子级。

我当前的代码最多可以遍历 3 个级别(并且我知道我可以添加更多循环并增加遍历的最大级别数),但我认为可以更有效地重写。我想知道重写 traverseOutline (outline: PDFOutline?) 方法更好。

import UIKit
import PDFKit


protocol OutlineDelegate: class 
    func goTo(page: PDFPage)


class OutlineViewController 

    @IBOutlet weak var tableView: UITableView!
    weak var delegate: OutlineDelegate?

    var outline: PDFOutline?
    var bookmarks = [Bookmark]()


    override func viewDidLoad() 
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self
    


    override func viewDidAppear(_ animated: Bool) 
        traverseOutline(outline: outline)
        tableView.reloadData()
    

    func traverseOutline (outline: PDFOutline?) 
        // 1st level
        guard let outline = outline else  return

        for i in 0...outlineCycleItems(outline) 
            if let bookmark = Bookmark(outline: outline, index: i) 
                bookmarks.append(bookmark)
                let subOutline = outline.child(at: i)

                // 2nd level
                for j in 0...outlineCycleItems(subOutline!) 
                    if let bookmark = Bookmark(outline: subOutline, index: j) 
                        bookmark.name = "- " + bookmark.name!
                        bookmarks.append(bookmark)
                        let subSubOutline = subOutline?.child(at: j)

                        // 3rd level
                        for k in 0...outlineCycleItems(subSubOutline!) 
                            if let bookmark = Bookmark(outline: subSubOutline, index: k)
                                bookmark.name = "-- " + bookmark.name!
                                bookmarks.append(bookmark)
                            
                        
                    
                
            
        
    

    func outlineCycleItems(_ outline: PDFOutline) -> Int 
        let amount = outline.numberOfChildren
        if amount == 0 
            return amount
         else 
            return amount - 1
        
    


extension OutlineViewController: UITableViewDelegate, UITableViewDataSource 
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return bookmarks.count
    

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "ChapterCell", for: indexPath) as! ItemOutlineCell
        cell.configureCell(name: bookmarks[indexPath.row].name!)
        return cell
    

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        delegate?.goTo(page: bookmarks[indexPath.row].link!)
    



class ItemOutlineCell: UITableViewCell 

    @IBOutlet weak var chapterName: UILabel!

    func configureCell (name: String)
        chapterName.text = name
    


class Bookmark 
    var link: PDFPage?
    var name: String?

    init(link: PDFPage, name: String) 
        self.link = link
        self.name = name
    

    init?(outline: PDFOutline?, index: Int) 
        guard let child = outline?.child(at: index) else 
            return nil
        
        link = child.destination?.page
        name = child.label
    


感谢@Erik 的主要思想,我用一个函数重写了:

 override func viewDidAppear(_ animated: Bool) 

        if let outline = outline 
            traverseOutline(outline: outline, currentLevel: 0)
            tableView.reloadData()
        
    


    func traverseOutline (outline: PDFOutline, currentLevel: Int) 
        for i in 0...outlineCycleItems(outline) 
            if let bookmark = Bookmark(outline: outline, index: i) 
                let dashes = String(repeating: "-", count: currentLevel)
                bookmark.name = dashes + bookmark.name!
                bookmarks.append(bookmark)

                let subOutline = outline.child(at: i)
                traverseOutline(outline: subOutline!, currentLevel: currentLevel + 1)
            
        
    

【问题讨论】:

【参考方案1】:

您可以通过添加一个不断调用自身的额外函数来使您的代码真正递归。我将第一层分开,因为它与其他层有些不同。在你原来的功能:

func traverseOutline (outline: PDFOutline?) 
     guard let outline = outline else  return

     for i in 0...outlineCycleItems(outline) 
        if let bookmark = Bookmark(outline: outline, index: i) 
           bookmarks.append(bookmark)
           let subOutline = outline.child(at: i)
           recursiveTraverse(outline: subOutline)                               
        
     

然后定义一个名为recursiveTraverse的新函数:

func recursiveTraverse(outline:PDFOutline)
   for j in 0...outlineCycleItems(subOutline!) 
      if let bookmark = Bookmark(outline: outline, index: j) 
         bookmark.name = "- " + bookmark.name!
         bookmarks.append(bookmark)
         recursiveTraverse(outline: outline.child(at: j))
      
   

这将通过在 for 循环中为每个单独的大纲调用自身来继续降低一个级别。只有当它不能从路径端点上的任何轮廓中创建书签时(当循环中所有轮廓的 if 语句为 false 时),它才会在某个路径上停止。

【讨论】:

以上是关于如何使用递归遍历大纲中的所有孩子?的主要内容,如果未能解决你的问题,请参考以下文章

如何递归地找到孩子的所有ID?

如何递归遍历 R 列表中的所有键值对并修改值? [复制]

如何递归遍历目录,通过 node.js 中的套接字发送所有文件名?

层次遍历递归和非递归方法

如何优雅的使用 Python 实现文件递归遍历

如何枚举 Sprite Kit 场景中的所有节点?