简体   繁体   English

(emacs)lisp:搜索((嵌套)列表中的任何内容)

[英](emacs) lisp: search anything in a ((nested) list)

I need to locate a particular value that can be buried into a deeply nested list, and never at the same place. 我需要找到一个可以隐藏在深层嵌套列表中的特定值,而不是在同一个地方。 Or even the same depth ; 甚至相同的深度; Here is one form of the list: 这是列表的一种形式:

(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)))) 

Now I need to retrieve the value of "charset" ; 现在我需要检索“charset”的值; The first one if any. 第一个,如果有的话。 In this very configuration, it's easy: 在这个配置中,它很容易:

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

But this is when I know exactly where the "body" cell is. 但是,当我确切知道“身体”细胞的位置时。

I tried to use mapcar recursively like this : 我尝试像这样递归地使用mapcar:

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

but every time, I get the error (wrong-type-argument listp 1) when the recursion hits the first atom of the first cons cell. 但每次,当递归到达第一个cons单元的第一个原子时,我得到错误(wrong-type-argument listp 1) I guess my question really is what it is : 我猜我的问题确实是这样的:

How can I search in a list? 我如何在列表中搜索?

EDIT Now the list looks like this, "charset" is still in (body) (told you that was about the only constant thing) and it's no longer found :( 编辑现在列表看起来像这样,“charset”仍然在(身体)(告诉你这是唯一不变的事情)并且它不再被发现:(

(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))))

EDIT here is some more IRL example: 编辑这里是一些更多的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)

Produces: 生产:


  • 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) 我的清单:((TEXT plain(charset UTF-8)nil nil 7BIT 260 18 nil nil nil)(TEXT html(charset UTF-8)nil nil nil nil nil nil nil nil nil nil nil nil)alternative(boundary e89a8fb1f8061a6be404c70a24a0)nil nil )

  • my-needle: nil 我的针:没有


When on the other hand: 另一方面:

(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)

So really, I don't know what's going on here : One could argue "what is this haystack-list and where does it come from?" 所以真的,我不知道这里发生了什么:人们可以争辩说“这个干草堆列表是什么,它来自哪里?” But is it relevant ? 但这有关系吗? I'm working on a copy (my-list) of this haystack-list so what gives those different results ? 我正在研究这个干草堆列表的副本(我的列表),那么是什么给出了不同的结果呢? The quoting of the list ? 列表的引用? Guys, I'm really lost 伙计们,我真的输了

NB (This behaviour (Works in a direct eval, but not in a defun/let production situation) occurred with all the solution given) 注意(这种行为(在直接评估中工作,但不在defun / let生产情况下)与所有解决方案一起发生)

EDIT: I ended up extracting the first list found, and then extracting (not searching) elements from that list. 编辑:我最终提取了找到的第一个列表,然后从该列表中提取(不搜索)元素。 I proved faster ; 我证明更快; Of course this is when you can say "my element is always in the fist list found) ; thanks to everybody, I learned a lot through all this. 当然,这就是你可以说“我的元素总是在第一个列表中找到了”;感谢所有人,我通过这一切学到了很多东西。

It looks like you want the tree analog of Association Lists . 看起来你想要关联列表的树模拟。 By following the conventions of the assoc function, which retrieves the list element that contains the given key as its head, here is a version of assoc that works on tree: 通过遵循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))))))

Example: 例:

(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)

It depends a bit of what you're wanting to do and how similar the list structures are (that is, do you always have a list of HTML parts? is the charset always inside a body element?) 这取决于你想做什么以及列表结构有多相似(也就是说,你总是有一个HTML部分列表吗?是一个总是在body元素里面的charset?)

A first step may be: 第一步可能是:

(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))

Calling (list-query '(body charset) (car my-list)) gives (charset UTF-8) as a result. 作为结果,调用(list-query '(body charset) (car my-list))给出(charset UTF-8) Looping over my-list to find the first (or all) charsets inside body lists should be relatively easy. 循环遍历我的列表以找到正文列表中的第一个(或所有)字符集应该相对容易。

Here's my take at the problem, perhaps you'll find it useful: 这是我对这个问题的看法,也许你会发现它很有用:

(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)))

It will require that you use cl but this is so common, that you probably won't even notice that, most chances are you already have it. 这将要求你使用cl但这是如此常见,你甚至可能不会注意到,大多数机会你已经拥有它。

EDIT: OK, this way you can avoid entirely looking into the last element of improper lists, however, this means you cannot search there either: 编辑:好的,这样你可以避免完全查看不正确列表的最后一个元素,但是,这意味着你不能在那里搜索:

(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)

Probably, not the best way to approach the problem, but a better solution would require changing the algorithm to record a sequence of car s and cdr s that would take you to the element in question. 可能不是解决问题的最佳方法,但更好的解决方案是需要更改算法来记录carcdr的序列,这些序列会将您带到相关元素。 In which case you could also search in the "improper" part of the list. 在这种情况下,您还可以搜索列表中的“不正确”部分。 But it's too late here now :) Perhaps tomorrow. 但现在已经太晚了:)也许明天。

EDIT 2 : 编辑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

OK, so, here it goes, the tree-to-proper-tree , if you choose that, will transform the tree in the way that all improper sub-trees will become proper trees. 好的,所以,在这里, tree-to-proper-tree ,如果你选择它,将以所有不正确的子树将成为适当的树的方式变换树。 Alternatively, you can use find-path-to to find what sequence of car and cdr will bring you to the element you have searched, and c*r-path will evaluate that sequence to return the element recorded in that way. 或者,您可以使用find-path-to查找carcdr哪个序列将您带到您搜索的元素,并且c*r-path将评估该序列以返回以这种方式记录的元素。

Note that it will be quite challenging to search for repeating occurrences of the same node in this way. 请注意,以这种方式搜索同一节点的重复出现将非常具有挑战性。 You would have to provide some comparator function that counts how many times the item was found. 您必须提供一些比较器函数来计算找到项目的次数。

As Rainer's answer implicitly indicates, the problem you're running into is that the cdr of a cons cell might point to a list or it might point to some other kind of object; 正如Rainer的回答暗示的那样,你遇到的问题是cons小区的cdr可能指向一个列表,或者它可能指向某种其他类型的对象; your search-rec function doesn't guard against the latter possibility. 你的search-rec功能并不能防止后一种可能性。

Here's an Elisp version of what you're looking for (not thoroughly tested; works with your example data): 这是您正在寻找的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)))))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM