鉴于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并将它们转换为/来自常规名单。
答案 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)+4
。 dash
有一种称为-reduce
的(-reduce '+ '(1 2 3 4)) ; => 10
:dash
但是这种折叠是不灵活的:整数列表上的折叠只能返回一个整数,而不是一些任意值。我们需要一个更普遍的折叠,更好的控制。这种通用折叠使用另一个参数,称为累加器,在二元函数内部使用。在每个迭代器上,您可以对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}}