在Emacs Lisp中获取列表元素的指针

7
例如,我有一个列表:

(setq foo '(1 2 3 4 5))

然后我需要获取指向其第3个索引元素的指针(例如示例中包含4):

(setq p (SOME_FUNCTION foo 3))

元素的地址为p,可以移动到另一个列表中,因此我不能仅保存其当前foo的索引。

并且我需要能够在以后说:

(push 0 foo)
=> (0 1 2 3 4 5)
(setf p 444)

并且列表foo在之后必须是(0 1 2 3 444 5)

这在Emacs lisp中是否可行?


这不是很像LISP。获取一个LISP教程并逐步学习。Emacs LISP(以及许多其他LISP)都有nth函数,所以你可以通过(nth 2 foo)来获取第三个元素。 - vonbrand
@vonbrand 我尝试了 (setq p (nth 3 foo)) (setf p 444),但是 foo 仍然包含 (1 2 3 4 5)。 - Alexander Gromnitsky
除了问题之外,对于那些想知道为什么(setf p 444)不能对原始列表产生任何影响的初学者来说,原因是(setq p obj)只是使变量p引用指定的目标对象。(在Python和许多其他动态语言中也是如此)另外,数字是不可变的(在许多其他动态语言中也是如此)。这意味着数字被视为其状态无法更改的对象。尽管如此,人们可以使变量引用与以前不同的数字,甚至是一个不是数字的对象。 - Jisang Yoo
2个回答

8

通常情况下,您无法存储对象的“地址”。但是,您可以引用 cons 单元格(cons 单元格是列表的组成部分)。稍后可以使用 setcarsetcdr 修改 cons 单元格。

例如:

(defvar my-cons-cell nil)

(defun my-save-cons-cell (cons-cell)
  (setq my-cons-cell cons-cell))

(defun my-set-car-in-saved-cons-cell (value)
  (setcar my-cons-cell value))

;; Test

(setq foo '(1 2 3 4 5))

(my-save-cons-cell (cdr (cdr (cdr foo))))

(push 0 foo)

(my-set-car-in-saved-cons-cell 444)

这里,foo的值为(0 1 2 3 444 5)
请注意,这确实不像Lisp,并且打破了函数式编程范例...

5

你可以做

(setq p (nth 3 foo))

它存储在p中,这个值存储在你想要的索引中。你也可以这样做:

(setf (nth 3 foo) 444)

将 444 存储在那个位置。但是如果您尝试执行以下操作

(setq pointer (nth 3 foo))
...
(setf pointer 444)

那样是行不通的。在Emacs的主干版本中,我最近添加了gv-refgv-deref,它们可以在这种情况下很好地工作。它们的工作方式与C语言的&*非常相似:

(setq pointer (gv-ref (nth 3 foo)))
...
(setf (gv-deref pointer) 444)

(setf (nth foo 3) 444) 给我返回了 "错误类型: integerp, (1 2 3 4 5)" - Alexander Gromnitsky
两天前的Emacs trunk:(setq p (gv-ref (nth 3 foo))) 返回 ((lambda nil (car c)) lambda (gv--val) (setcar c gv--val)),而 (setf (gv-deref p) 444) 则会报错 "Symbol's value as variable is void: c"。 - Alexander Gromnitsky
1
(setf (nth foo 3) 444) 产生的错误是因为你交换了 foo3 的位置。而 gv-ref 的问题则在于它只能在 lexical-binding 模式下工作。 - Stefan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接