Lisp:如何在列表列表中设置元素?

Lisp: How to set element in list of lists?

我熟悉如何在二维数组中设置元素,可以使用以下语句完成。

(setf (aref array2D 0 0) 3)

但是,我不熟悉如何在列表的列表中设置元素,比如下面的输入:'((1) (2) (2) (1))。我不能使用 aref,因为它只适用于数组。

如前所述,而 aref works on arrays, elt works on sequences 可以是:

  1. 有序的元素集合
  2. 向量或列表。
* (setf test-list '((1) (2) (2) (1)))
((1) (2) (2) (1))
* (setf (elt test-list 2) 'hi)
HI
* test-list
((1) (2) HI (1))

您确实可以使用变量代替固定偏移量:

* (setf test-list '((1) (2) (2) (1)))
((1) (2) (2) (1))
* (setf offset 2)
2
* (setf (elt test-list offset) 'hi)
HI
* test-list
((1) (2) HI (1))

访问列表的第n个元素,有(至少)两个函数:nthelt。参数的顺序不同,nth 仅适用于列表,而 elt 适用于任何序列( 列表、向量、字符串...):

(nth 1 '(foo bar baz)) => BAR
(nth 1 #(foo bar baz)) => ERROR
(elt '(foo bar baz) 1) => BAR
(elt #(foo bar baz) 1) => BAR

现在,一般来说,设置一个值(而不是简单地访问它)的方法非常简单,至少对于built-in函数来说,这是几乎总是这样:每当你有某种形式 FORM 从所谓的 place 中检索一些值时,形式 (setf FORM <value>) 会将此元素设置为给定的 <value>。这适用于 car, cdr, gethash, aref, slot-value, symbol-function 和许多其他函数,以及这些函数的任意组合。

在您的示例中,您有一个列表列表。因此,例如,要修改第三个列表中的“内部整数”:

* (setf test-list '((0) (1) (2) (3))) ; changed the values to have something clearer
((0) (1) (2) (3))
* (car (nth 2 test-list)) ; this accesses the integer in the second list
2
* (setf (car (nth 2 test-list)) 12) ; this modifies it. Notice the syntax
12
* test-list
((0) (1) (12) (3))

附带说明一下,您应该避免修改文字列表(使用引号 ' 创建)。如果要修改列表,请在运行时使用 list 函数创建它们。

编辑: 发生的事情是,通过“查看”您提供的表格,setf 知道如何真正找到您想要修改的地方,可能在此过程中使用函数。

如果您查看其他语言,例如 Python,您在用于获取和设置值的语法中也有某种双重性。事实上,如果你有一个列表 L 或一个字典 d,那么 L[index]d[thing] 将获得相应的元素,而 L[index] = 12d[thing] = "hello"会修改的。

然而,在Python中,这些访问器使用了一种特殊的语法,即方括号[]。其他类型的对象使用另一种语法,例如,点符号来访问对象的 slots/attributes,如 my-object.attr。结果是以下代码在 Python 中无效:

>>> L = [1, 2, 3, 2, 1]

>>> max(L)

3
>>> max(L) = 12

Traceback (most recent call last):
  File "<string>", line 9, in __PYTHON_EL_eval
  File "/usr/lib/python3.8/ast.py", line 47, in parse
    return compile(source, filename, mode, flags,
  File "<string>", line 1
SyntaxError: cannot assign to function call

您必须编写一个 other 函数,例如 setMax(L, val),以更改列表的最大值。这意味着你现在必须函数,不再对称。

在 Common Lisp 中,一切(至少在语法上)都是函数调用。这意味着您可以为任何功能定义 访问和修改事物的新方法!作为您可以做什么的(坏)示例:

* (defun my-max (list)
    (reduce #'max list))
MY-MAX
* (my-max '(1 2 3 8 4 5))
8
* (defun (setf my-max) (val list)
    (do ((cur list (cdr cur))
         (cur-max list (if (< (car cur-max) (car cur))
                               cur
                               cur-max)))
        ((endp (cdr cur)) (setf (car cur-max) val))))

(SETF MY-MAX)
* (setf test-list (list 0 4 5 2 3 8 6 3))
(0 4 5 2 3 8 6 3)
* (setf (my-max test-list) 42)
42
* test-list
(0 4 5 2 3 42 6 3)

这样,用于设置和获取列表最大值的语法是相同的(FORM 获取,(setf FORM val) 设置),并自动与其他所有 "setter”。没有明确的 pointers/references 涉及,它只是函数。