如何将嵌套树状列表压平为alist(包含层级信息)

目前尝试自动导出epub大纲,现有嵌套列表:

(html nil (body nil (ol nil (li nil (a ((href . text/part0001.html #UGI0-76596b877cf94daaa7f15571d95446d2)) 版权信息)) (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_2)) 第Ⅰ部分 人民信任,来之不易) (ol nil (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_4)) 第一章 司法审查是反民主的吗?) (ol nil (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_5)) 听听制宪者怎么说)) (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_6)) 霍茨波的追问)))) (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_8)) 第二章 司法审查的建立:马伯里诉麦迪逊案) (ol nil (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_9)) 被逼入死角的最高法院)) (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_10)) 马歇尔的伟大创举)) (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_11)) 最高法院成功扩权了吗?)) (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_12)) 首席大法官的忧虑)))) (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_31)) 第六章 最高法院何以最高:布什诉戈尔案) (ol nil (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_32)) 最高法院的 “自残 ”)) (li nil (a ((href . text/part0002_split_001.html #sigil_toc_id_33)) 当服从判决成为公共习惯)))))))))

期望得到类似下面的alist:

( ((depth . 1) (title . “版权信息”) (href . “text/part0001.html #UGI0-76596b877cf94daaa7f15571d95446d2”)) ((depth . 1) (title . “第Ⅰ部分 人民信任,来之不易”) (href . “text/part0001.html #UGI0-76596b877cf94daaa7f15571d95446d2”)) ((depth . 2) (title . “第一章 司法审查是反民主的吗?”) (href . “text/part0001.html #UGI0-76596b877cf94daaa7f15571d95446d2”)) ((depth . 3) (title . “听听制宪者怎么说”) (href . “text/part0001.html #UGI0-76596b877cf94daaa7f15571d95446d2”)) ((depth . 3) (title . “霍茨波的追问”) (href . “text/part0001.html #UGI0-76596b877cf94daaa7f15571d95446d2”)) )

请问有无现成的方法,比如dash里?感觉是map或遍历之类的功能。才疏学浅,没有入手点。任何指导,感激不尽!

先处理 ol 底下的 li,然后 li 底下包含 aol,用递归处理 ol,最后用 flatten-tree 拉平。

(defun foo (ol depth)
  (mapcar (lambda (li)
            (mapcar (lambda (a-or-ol)
                      (pcase-exhaustive (dom-tag a-or-ol)
                        ('a
                         (vector
                          :depth depth
                          :title (dom-text a-or-ol)
                          :href (dom-attr a-or-ol 'href)))
                        ('ol
                         (foo a-or-ol (1+ depth)))))
                    (dom-children li)))
          (dom-children ol)))

(flatten-tree
 (foo '(ol nil
           (li nil
               (a ((href . "text/part0001.html #UGI0-76596b877cf94daaa7f15571d95446d2"))
                  "版权信息"))
           (li nil
               (a ((href . "text/part0002_split_001.html #sigil_toc_id_2"))
                  "第Ⅰ部分 人民信任,来之不易")
               (ol nil
                   (li nil
                       (a ((href . "text/part0002_split_001.html #sigil_toc_id_4"))
                          "第一章 司法审查是反民主的吗?")
                       (ol nil
                           (li nil
                               (a ((href . "text/part0002_split_001.html #sigil_toc_id_5"))
                                  "听听制宪者怎么说"))
                           (li nil
                               (a ((href . "text/part0002_split_001.html #sigil_toc_id_6"))
                                  "霍茨波的追问"))))
                   (li nil
                       (a ((href . "text/part0002_split_001.html #sigil_toc_id_8"))
                          "第二章 司法审查的建立:马伯里诉麦迪逊案")
                       (ol nil
                           (li nil
                               (a ((href . "text/part0002_split_001.html #sigil_toc_id_9"))
                                  "被逼入死角的最高法院"))
                           (li nil
                               (a ((href . "text/part0002_split_001.html #sigil_toc_id_10"))
                                  "马歇尔的伟大创举"))
                           (li nil
                               (a ((href . "text/part0002_split_001.html #sigil_toc_id_11"))
                                  "最高法院成功扩权了吗?"))
                           (li nil
                               (a ((href . "text/part0002_split_001.html #sigil_toc_id_12"))
                                  "首席大法官的忧虑"))))
                   (li nil
                       (a ((href . "text/part0002_split_001.html #sigil_toc_id_31"))
                          "第六章 最高法院何以最高:布什诉戈尔案")
                       (ol nil
                           (li nil
                               (a ((href . "text/part0002_split_001.html #sigil_toc_id_32"))
                                  "最高法院的自残"))
                           (li nil
                               (a ((href . "text/part0002_split_001.html #sigil_toc_id_33"))
                                  "当服从判决成为公共习惯")))))))
      1))
;; =>
([:depth 1 :title "版权信息" :href "text/part0001.html #UGI0-76596b877cf94daaa7f15571d95446d2"]
 [:depth 1 :title "第Ⅰ部分 人民信任,来之不易" :href "text/part0002_split_001.html #sigil_toc_id_2"]
 [:depth 2 :title "第一章 司法审查是反民主的吗?" :href "text/part0002_split_001.html #sigil_toc_id_4"]
 [:depth 3 :title "听听制宪者怎么说" :href "text/part0002_split_001.html #sigil_toc_id_5"]
 [:depth 3 :title "霍茨波的追问" :href "text/part0002_split_001.html #sigil_toc_id_6"]
 [:depth 2 :title "第二章 司法审查的建立:马伯里诉麦迪逊案" :href "text/part0002_split_001.html #sigil_toc_id_8"]
 [:depth 3 :title "被逼入死角的最高法院" :href "text/part0002_split_001.html #sigil_toc_id_9"]
 [:depth 3 :title "马歇尔的伟大创举" :href "text/part0002_split_001.html #sigil_toc_id_10"]
 [:depth 3 :title "最高法院成功扩权了吗?" :href "text/part0002_split_001.html #sigil_toc_id_11"]
 [:depth 3 :title "首席大法官的忧虑" :href "text/part0002_split_001.html #sigil_toc_id_12"]
 [:depth 2 :title "第六章 最高法院何以最高:布什诉戈尔案" :href "text/part0002_split_001.html #sigil_toc_id_31"]
 [:depth 3 :title "最高法院的自残" :href "text/part0002_split_001.html #sigil_toc_id_32"]
 [:depth 3 :title "当服从判决成为公共习惯" :href "text/part0002_split_001.html #sigil_toc_id_33"])
1赞

太感谢了!第一次了解有dom这个包专门用来处理网页元素,foo这个函数的recursive方法真巧妙,好好学习学习~

问一个蠢问题:是不是取 vector 中的元素是通过类似 (aref vector 3) 的方法。如果想实现类似字典key取value的方法,需要用alist + alist-get 或 plist + plist-get?

对,用 elt 也行。我上面之所以用数组,是因为 flatten-tree 会展开所有的列表。你把这个数组转化层 plist 或 alist,然后用 plist-get alist-get 访问。

(seq-into [:x 1 :y 2] 'list)
;; => (:x 1 :y 2)

(plist-get (seq-into [:x 1 :y 2] 'list) :x)
;; => 1

又学到啦。plist本质真的只是一个list啊,跟其他语言有dict类型好不一样

Emacs有hashmap类型

(make-hash-table :test #'equal)