Как пройти всех детей в общих чертах, используя рекурсию? - PullRequest
1 голос
/ 10 июля 2019

Я хочу переписать код с помощью рекурсии, которая может обойти всех дочерних элементов в общих чертах без ограничений.

Мой текущий код может проходить до 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 Ответ

2 голосов
/ 10 июля 2019

Вы можете сделать свой код действительно рекурсивным, добавив дополнительную функцию, которая постоянно вызывает себя.Я держал первый уровень отдельно, так как он немного отличается от других.В вашей исходной функции:

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 ложен для всех контуров в цикле).

...