将alist转换为Elisp的常规列表?

时间:2013-11-04 18:32:24

标签: emacs elisp

鉴于Lisp中代码和数据的“所有列表”同源方法,我很难理解如何在Emacs Lisp中操作alist。特别是,如何将alist转换为普通的双元素列表列表,反之亦然?似乎alist是他们自己的特殊类型,不能像常规列表那样被操纵。

具体示例:我正在尝试绑定.emacs文件中的密钥以使用编程生成的名称创建特定大小的框架,如下所示:

(make-frame '((name . "Blarg") (width . 80) (height . 24)))

只要“Blarg”是一个常量字符串,这样就可以正常工作,但是由于alist开头的引用,我不能将任何求值的代码放在一个字符串中代替“Blarg”。我想要做的是通过cons为宽度和高度的符号和整数建立一个列表,然后add-list将名称放在前面,并将整个事物传递给{{1 }}。但是,如何将结果数据结构转换为alist?

关于如何让make-frame做我想做的事的具体答案当然会受到赞赏,但正如我的标题所示,我希望有一个更一般的解释如何操纵alist并将它们转换为/来自常规名单。

4 个答案:

答案 0 :(得分:5)

关于构建列表的两分钱:

(pairlis '(name width height)
         '("Blarg" 80 24))
;; => ((name . "Blarg") (width . 80) (height . 24))

答案 1 :(得分:5)

@wvxvw回答了你的一般问题。但请注意,所描述的内容是 转换列表,反之亦然。

一般来说,你需要做任何这样的“转换”。你可以直接使用alist - 毕竟它们是列表。

这让我得到了make-frame问题的答案,就是这个问题,其中foobar包含您想要的名称:

(make-frame `((name . ,foobar) (width . 80) (height . 24)))

或者如果您愿意:

(make-frame (cons (cons 'name foobar) '((width . 80) (height . 24))))

答案 2 :(得分:3)

(require 'list-utils)
(list-utils-flatten '((name . "Blarg") (width . 80) (height . 24)))
;; (name "Blarg" width 80 height 24)

list-utils可以从MELPA安装。

或使用loop

(loop for (head . tail) in '((name . "Blarg") (width . 80) (height . 24))
      nconc (list head tail))
;; (name "Blarg" width 80 height 24)

(loop for (head . tail) on '(name "Blarg" width 80 height 24) by 'cddr
      collect (cons head (car tail)))
;; ((name . "Blarg") (width . 80) (height . 24))

我就是这样做的(需要cl库)

答案 3 :(得分:1)

使用dash列表和树操作库,您可以通过将点对(a . b)转换为2元素列表(a b),然后展平列表,将alist转换为平面列表,这可能与-mapcat(下面的2个表达式相同):

(-mapcat (lambda (x) (list (car x) (cdr x))) my-alist)
(-flatten (-map (lambda (x) (list (car x) (cdr x))) my-alist))
(apply '-concat (-map (lambda (x) (list (car x) (cdr x))) my-alist))

在Haskell中,由于懒惰的评估,类似的表达式(连续应用各种函数来转换列表)会更有效和自然。

所以我想用这个答案作为一个探索编程概念的机会。什么是当你遍历一个列表并根据其内容构建一些结果时(无论结果如何)?这是一个fold!最原始的折叠形式是一个采用二元函数并将其应用于元素1和2,然后结果为3,然后结果为4,依此类推。因此折叠接受+和列表[1,2,3,4]变为((1+2)+3)+4dash有一种称为-reduce(-reduce '+ '(1 2 3 4)) ; => 10dash

但是这种折叠是不灵活的:整数列表上的折叠只能返回一个整数,而不是一些任意值。我们需要一个更普遍的折叠,更好的控制。这种通用折叠使用另一个参数,称为累加器,在二元函数内部使用。在每个迭代器上,您可以对list的元素和累加器执行任何操作。函数应用程序的结果将成为下一次迭代的累加器。在(-reduce-from (lambda (acc x) (append acc (list (car x) (cdr x)))) '() my-alist) 这样的折叠被称为-reduce-from。我们将空列表作为累加器,将原始列表中的每个点对一个接一个,然后将其转换为2个新元素,我们将其附加到二进制函数内的累加器中。什么可以更容易?

-reduce-from

但是以这种方式附加列表是低效和惯用的,因为列表在Lisp中实现为单链表,因此在结尾添加元素或连接列表是O(n),整个函数在O(n²)中工作。 Lispers通常位于列表的开头,但(-reduce-r-from (lambda (x acc) (cons (car x) (cons (cdr x) acc))) '() my-alist) 从左到右遍历列表,这就是结果将被颠倒的原因。如果我们只能从右到左遍历列表,那么我们就可以将元素转换成累积器。好吧,有一个函数-reduce-r-from

-reduce-r-from

-reduce-r-from是最有效的版本,因为它只遍历alist一次。每次你需要使用折叠构建一些列表时,你可能需要dash。最后,lambda库的优点在于它为接受函数作为参数的函数提供了anaphoric macro版本以消除(--mapcat (list (car it) (cdr it)) my-alist) (-flatten (--map (list (car it) (cdr it)) my-alist)) (apply '-concat (--map (list (car it) (cdr it)) my-alist)) (--reduce-r-from (cons (car it) (cons (cdr it) acc))) '() my-alist) 语法:

{{1}}