6

我需要找到一个可以隐藏在深度嵌套列表中的特定值,并且永远不会在同一个地方。甚至同样的深度;这是列表的一种形式:

(setq my-list '(((partnum . 1) (type (TEXT . plain)) (body (charset UTF-8))
                 (disposition nil) (transfer-encoding QUOTED-PRINTABLE))
                ((partnum . 2) (type (TEXT . html)) (body (charset UTF-8))
                 (disposition nil) (transfer-encoding QUOTED-PRINTABLE)))) 

现在我需要检索 "charset" 的值;如果有的话,第一个。在这个非常配置中,很容易:

(car (cdr (cadr (third (car my-list)))))
   => UTF-8

但这是我确切知道“身体”细胞在哪里的时候。

我尝试像这样递归地使用 mapcar:

(defun search-rec (list)
  (mapcar
     (lambda (x)
       (if (listp x)
           (search-rec x)
         (message "OY %s" x)))
     list))

但是每次,(wrong-type-argument listp 1)当递归到达第一个 cons 单元的第一个原子时,我都会收到错误。我想我的问题真的是它是什么:

如何在列表中搜索?

编辑现在列表看起来像这样,“charset”仍然在 (body) 中(告诉你那是唯一不变的东西)并且它不再被发现:(

(setq my-list '(((partnum . 1)
                (1.1 (type (TEXT . plain)) (body (charset UTF-8))
                     (disposition nil) (transfer-encoding 7BIT))
                (1.2 (type (TEXT . html)) (body (charset UTF-8))
                     (disposition nil) (transfer-encoding 7BIT))
                (type . alternative) (body (boundary e89a8fb2067eba300404c63c5f7f))
                (disposition nil) (transfer-encoding nil))
               ((partnum . 1.1) (type (TEXT . plain)) (body (charset UTF-8))
                (disposition nil) (transfer-encoding 7BIT))
               ((partnum . 1.2) (type (TEXT . html)) (body (charset UTF-8))
                (disposition nil) (transfer-encoding 7BIT))
               ((partnum . 2) (type (IMAGE . x-xpixmap)) (body (name ladybug.xpm))
                (disposition nil) (transfer-encoding BASE64))))

编辑这里是一些更多的 IRL 示例:

    (setq my-list haystack-list)
    (setq my-needle (tree-assoc 'charset my-list))
    (message "
-------------\n
- my-list: %s\n
- my-needle: %s\n
-------------\n" my-list my-needle)

产生:


  • my-list: ((TEXT plain (charset UTF-8) nil nil 7BIT 260 18 nil nil nil) (TEXT html (charset UTF-8) nil nil QUOTED-PRINTABLE 738 17 nil nil nil) Alternative (boundary e89a8fb1f8061a6be404c70a24a0) nil nil )

  • 我的针:无


另一方面,当:

(tree-assoc 'charset '((TEXT plain (charset UTF-8) nil nil 7BIT 260 18 nil nil nil)
(TEXT html (charset UTF-8) nil nil QUOTED-PRINTABLE 738 17 nil nil nil) 
alternative (boundary e89a8fb1f8061a6be404c70a24a0) nil nil))
  =>(charset UTF-8)

所以真的,我不知道这里发生了什么:有人可能会争论“这个干草堆列表是什么,它来自哪里?” 但它是否相关?我正在制作这个 haystack-list 的副本(我的列表),那么是什么给出了这些不同的结果?列表的引用?伙计们,我真的迷路了

注意(此行为(在直接评估中有效,但在 defun/let 生产情况下无效)发生在所有给出的解决方案中)

编辑:我最终提取了找到的第一个列表,然后从该列表中提取(而不是搜索)元素。我证明更快;当然这是你可以说“我的元素总是在找到的第一个列表中);感谢大家,通过这一切我学到了很多。

4

4 回答 4

8

看起来您想要Association Lists的树模拟。通过遵循assoc函数的约定,它检索包含给定键作为其头部的列表元素,这是一个适用于树的 assoc 版本:

(defun tree-assoc (key tree)
  (when (consp tree)
    (destructuring-bind (x . y)  tree
      (if (eql x key) tree
        (or (tree-assoc key x) (tree-assoc key y))))))

例子:

(let ((my-list '(((partnum . 1)
                  (1.1 (type (TEXT . plain)) (body (charset UTF-8))
                   (disposition nil) (transfer-encoding 7BIT))
                  (1.2 (type (TEXT . html)) (body (charset UTF-8))
                   (disposition nil) (transfer-encoding 7BIT))
                  (type . alternative) (body (boundary e89a8fb2067eba300404c63c5f7f))
                  (disposition nil) (transfer-encoding nil))
                 ((partnum . 1.1) (type (TEXT . plain)) (body (charset UTF-8))
                  (disposition nil) (transfer-encoding 7BIT))
                 ((partnum . 1.2) (type (TEXT . html)) (body (charset UTF-8))
                  (disposition nil) (transfer-encoding 7BIT))
                 ((partnum . 2) (type (IMAGE . x-xpixmap)) (body (name ladybug.xpm))
                  (disposition nil) (transfer-encoding BASE64)))))
  (tree-assoc 'charset my-list))

=> (charset UTF-8)
于 2012-08-12T12:10:02.603 回答
2

这取决于您想要做什么以及列表结构的相似程度(也就是说,您是否总是有一个 HTML 部分列表?字符集是否总是在 body 元素中?)

第一步可能是:

(defun list-query (list-of-keys data)
  (let ((data data))
    (while (and data list-of-keys)
      (setq data (assoc (car list-of-keys) data))
      (setq list-of-keys (cdr list-of-keys)))
    data))

(list-query '(body charset) (car my-list))结果是调用(charset UTF-8)。遍历 my-list 以查找正文列表中的第一个(或所有)字符集应该相对容易。

于 2012-08-11T06:41:13.860 回答
2

这是我对这个问题的看法,也许你会发现它很有用:

(defun depth-first-search (tree searched &optional comparator)
  "TREE is the nested list of elements to search, SEARCHED
is the element to search for, COMPARATOR is the function used
to compare elements of the tree to the searched element, if
you don't provide any, then `equal' is used.
Returns a list of subscripts to be used with `nth' to find the
searched element. If the result is `nil', the list itself
is the searched element. If the result is not a list,
the `not-found' symbol, then the element was not found."
  (unless comparator (setq comparator #'equal))
  (let ((operations 'not-found))
    (labels ((%df-search
              (item ops)
              (if (funcall comparator item searched)
                  (setq operations (reverse ops))
                (let ((offset 0))
                  (when (consp item)
                    (dolist (i item)
                      (%df-search i (cons offset ops))
                      (unless (eq operations 'not-found)
                        (return))
                      (incf offset)))))))
      (%df-search tree nil)
      operations)))

(defun nth-repeat (subscripts tree)
  "Given the list of SUBSCRIPTS, will subsequently evaluate
`nth' with every subscript on the result of the previous evaluation
 such as to find the element in the TREE."
  (let ((result tree))
    (dolist (i subscripts result)
      (setq result (nth i result)))))

(nth-repeat 
 (depth-first-search '(1 (1 1 2) (1 1 1 3)) 3)
 '(1 (1 1 2) (1 1 1 3)))

它需要您使用cl,但这很常见,您可能甚至不会注意到,您很可能已经拥有它。

编辑:好的,这样你可以避免完全查看不正确列表的最后一个元素,但是,这意味着你也不能在那里搜索:

(defun depth-first-search (tree searched &optional comparator)
  "TREE is the nested list of elements to search, SEARCHED
is the element to search for, COMPARATOR is the function used
to compare elements of the tree to the searched element, if
you don't provide any, then `equal' is used.
Returns a list of subscripts to be used with `nth' to find the
searched element. If the result is `nil', the list itself
is the searched element. If the result is not a list,
the `not-found' symbol, then the element was not found."
  (unless comparator (setq comparator #'equal))
  (let ((operations 'not-found))
    (labels ((%df-search
              (item ops)
              (if (funcall comparator item searched)
                  (setq operations (reverse ops))
                (let ((offset 0))
                  (when (consp item)
                    (block outer
                      (maplist
                       (lambda (x)
                         (%df-search (car x) (cons offset ops))
                         (when (or (not (eq operations 'not-found))
                                   (not (listp (cdr x))))
                           (return-from outer))
                         (incf offset))
                       item)))))))
      (%df-search tree nil)
      operations)))

(defun nth-repeat (subscripts tree)
  "Given the list of SUBSCRIPTS, will subsequently evaluate
`nth' with every subscript on the result of the previous evaluation
 such as to fint the element in the TREE."
  (let ((result tree))
    (dolist (i subscripts result)
      (setq result (nth i result)))))

(defvar my-list '(((partnum . 1)
                   (1.1 (type (TEXT . plain)) (body (charset UTF-8))
                        (disposition nil) (transfer-encoding 7BIT))
                   (1.2 (type (TEXT . html)) (body (charset UTF-8))
                        (disposition nil) (transfer-encoding 7BIT))
                   (type . alternative) (body (boundary e89a8fb2067eba300404c63c5f7f))
                   (disposition nil) (transfer-encoding nil))
                  ((partnum . 1.1) (type (TEXT . plain)) (body (charset UTF-8))
                   (disposition nil) (transfer-encoding 7BIT))
                  ((partnum . 1.2) (type (TEXT . html)) (body (charset UTF-8))
                   (disposition nil) (transfer-encoding 7BIT))
                  ((partnum . 2) (type (IMAGE . x-xpixmap)) (body (name ladybug.xpm))
                   (disposition nil) (transfer-encoding BASE64))))

(depth-first-search
 my-list '(charset UTF-8))              ; (0 1 2 1)

(nth-repeat
 (depth-first-search
  my-list '(charset UTF-8)) my-list)    ; (charset UTF-8)

可能,这不是解决问题的最佳方法,但更好的解决方案需要更改算法以记录cars 和cdrs 的序列,从而将您带到有问题的元素。在这种情况下,您还可以在列表的“不当”部分进行搜索。但现在为时已晚:) 也许明天。

编辑 2

(defun tree-to-proper-tree (tree)
  (cond
   ((null tree) nil)
   ((consp tree)
    (let ((head
           (if (consp (car tree))
               (tree-to-proper-tree (car tree))
             (car tree))))
    (cons head
          (tree-to-proper-tree (cdr tree)))))
   (t (list tree))))

(defun find-path-to (tree node &optional comparator)
  (unless comparator (setq comparator #'equal))
  (let ((operations 'not-found))
    (labels ((%df-search
              (item ops)
              (if (funcall comparator item node)
                  (setq operations (reverse ops))
                (when (consp item)
                      (%df-search (car item) (cons 'car ops))
                      (%df-search (cdr item) (cons 'cdr ops))))))
      (%df-search tree nil)
      operations)))

(defun c*r-path (path tree)
  (dolist (i path tree)
    (setq tree (funcall i tree))))

(defvar my-list '(((partnum . 1)
                   (1.1 (type (TEXT . plain)) (body (charset UTF-8))
                        (disposition nil) (transfer-encoding 7BIT))
                   (1.2 (type (TEXT . html)) (body (charset UTF-8))
                        (disposition nil) (transfer-encoding 7BIT))
                   (type . alternative) (body (boundary e89a8fb2067eba300404c63c5f7f))
                   (disposition nil) (transfer-encoding nil))
                  ((partnum . 1.1) (type (TEXT . plain)) (body (charset UTF-8))
                   (disposition nil) (transfer-encoding 7BIT))
                  ((partnum . 1.2) (type (TEXT . html)) (body (charset UTF-8))
                   (disposition nil) (transfer-encoding 7BIT))
                  ((partnum . 2) (type (IMAGE . x-xpixmap)) (body (name ladybug.xpm))
                   (disposition nil) (transfer-encoding BASE64))))

(tree-to-proper-tree my-list) ; the same lists as above but made into a proper lists

(c*r-path (find-path-to my-list 'UTF-8) my-list) ; UTF-8
(c*r-path (find-path-to my-list 'plain) my-list) ; plain

好的,所以,在这里tree-to-proper-tree,如果您选择它,它将以所有不正确的子树都将成为正确树的方式转换树。或者,您可以使用find-path-to查找什么序列car并将cdr您带到您搜索的元素,c*r-path并将评估该序列以返回以这种方式记录的元素。

请注意,以这种方式搜索相同节点的重复出现将非常具有挑战性。您必须提供一些比较器功能来计算找到该项目的次数。

于 2012-08-11T09:06:15.413 回答
2

正如 Rainer 的回答暗示的那样,您遇到的问题是 cons 单元格的 cdr 可能指向一个列表,或者它可能指向某种其他类型的对象;您的search-rec功能不能防止后一种可能性。

这是您正在寻找的 Elisp 版本(未经彻底测试;适用于您的示例数据):

(defun find-charset (l)
  (catch 'my-result
    (find-charset-do l)))

(defun find-charset-do (l)
  (when (and (consp l) 
             (listp (cdr l)))
    (if (and (eq (car l) 'charset)
             (symbolp (cadr l)))
        (throw 'my-result (cadr l))
      (dolist (e l)
        (find-charset-do e)))))
于 2012-08-12T02:06:32.677 回答