Как работает "Cons" в Lisp?

Я изучал Lisp, и я не знаком с программированием Lisp. В части моих исследований я столкнулся с приведенными ниже примерами:

> (cons ‘a ‘(a b))  ----> (A A B)
> (cons ‘(a b) ‘a)  ----> ((A B).A)

Мне было интересно, почему, когда у нас есть (cons 'a' (ab)), ответ (AAB) и почему, когда мы его немного меняем и помещаем 'a после (ab), ответ представляет собой точечный список, например ((AB).A)? В чем разница между первой строкой кода и второй? Что происходит за этими кодами?

Ответ 1

Это довольно легко понять, если вы думаете о них как cons-cells.

Короче говоря, ячейка cons состоит из двух значений. Обычная нотация для этого заключается в использовании точки, например:

(cons 'a 'b) ==> (A . B)

Но поскольку списки используются так часто в LISP, лучше обозначить точку. Списки создаются тем, что второй элемент представляет собой новую ячейку cons, с последним завершением терминатора (обычно nil или '() в Common Lisp). Итак, эти два равны:

(cons 'a (cons 'b '())) ==> (A B)
(list 'a 'b) ==> (A B)

Итак, (cons 'a 'b) создает ячейку [a,b], а (list 'a 'b) создаст [a, [b, nil]]. Обратите внимание на соглашение о кодировании списков в cons-ячейках: они заканчиваются с внутренней nil.

Теперь, если вы минуете 'a в последний список, вы создаете новую ячейку cons, содержащую [[a, [b, nil]], a]. Поскольку это не "правильный" список, т.е. Он не заканчивается с помощью nil, способ его записи состоит в использовании точки: (cons '(a b) 'a) ==> ((a b) . a).

Если точка не была напечатана, это должен был быть список со структурой [[a, [b, nil]], [a, nil]].

Ваш пример

Когда вы выполните (cons 'a '(a b)), он примет символ 'a и список '(a b) и поместит их в новую ячейку cons. Таким образом, это будет состоять из [a, [a, [b, nil]]]. Поскольку это, естественно, заканчивается внутренним nil, оно написано без точек.

Что касается (cons '(a b) 'a), теперь вы получите [[a, [b, nil]], a]. Это не заканчивается с внутренним nil, и поэтому будет использоваться точечная нотация.

Можем ли мы использовать минусы, чтобы конец последнего примера был внутренним? Да, если мы делаем

(cons '(a b) (cons 'a '())) ==> ((A B) A)

И, наконец,

(list '(a b) 'a))

эквивалентно

(cons (cons (cons 'a (cons 'b '())) (cons 'a '())))

Ответ 2

Смотрите эту визуализацию:

CL-USER 7 > (sdraw:sdraw '(A A B))

[*|*]--->[*|*]--->[*|*]--->NIL
 |        |        |
 v        v        v
 A        A        B

CL-USER 8 > (sdraw:sdraw '((A B) . A))

[*|*]--->A
 |
 v
[*|*]--->[*|*]--->NIL
 |        |
 v        v
 A        B

также:

CL-USER 9 > (sdraw:sdraw '(A B))

[*|*]--->[*|*]--->NIL
 |        |
 v        v
 A        B

CL-USER 10 > (sdraw:sdraw (cons 'A '(A B)))

[*|*]--->[*|*]--->[*|*]--->NIL
 |        |        |
 v        v        v
 A        A        B

CL-USER 11 > (sdraw:sdraw (cons '(A B) 'A))

[*|*]--->A
 |
 v
[*|*]--->[*|*]--->NIL
 |        |
 v        v
 A        B

Ответ 3

Список (a b c) представлен (хранится внутри) как три cons-ячейки: (cons 'a (cons 'b (cons 'c '()). Обратите внимание, что последняя пара имеет '() в своем cdr.

Серия cons-cells, последний cdr которого '() печатается в виде списка принтером. Таким образом, пример печатается как (a b c).

Посмотрим на: (cons 'a '(a b)).

Список '(a) представлен как (cons' a (cons 'b'()). Это означает, что (cons 'a '(a b)) производит: (cons 'a (cons 'a (cons 'b '())).

Посмотрим на: (cons '(a b) 'a).

Список '(a) представлен как (cons' a (cons 'b'()). Это означает, что (cons (cons '(a b) 'a)) производит (cons (cons 'a (cons 'b '()) 'a).

Обратите внимание, что этот ряд не заканчивается на '(). Чтобы показать, что принтер использует точечную нотацию. ( ... . 'a) означает, что значение состоит из серии cons-cells и что последний cdr содержит 'a. Таким образом, значение (cons (cons 'a (cons 'b '()) 'a) печатается как '((a b) . a).

Ответ 4

A cons - структура данных, которая может содержать два значения. Например, (cons 1 2) ; ==> (1 . 2). Первая часть car, вторая - cdr. A cons является list, если он cdr равен либо nil, либо list. таким образом   (1 . (2 . (3 . ()))) - это список.

При печати cons точка опущена, если cdr является cons или nil. Внешние круглые скобки cdr также опущены. Таким образом, (3 . ()) печатается (3) и (1 . (2 . (3 . ()))) печатается (1 2 3). Это та же структура, но с другой визуализацией. A cons в car не имеет этого правила.

Функция чтения читает cons с точкой и странным исключительным форматом печати, когда cdr является списком. Он будет во время чтения вести себя так, как будто это было cons.

Со специальным правилом как для read, так и для print иллюзия списка завершена, даже если она цепочки cons.

(cons ‘a ‘(a b))  ----> (A . (A B)) 
(cons ‘(a b) ‘a)  ----> ((A B) . A)

При печати первый представляет собой один список из 3 элементов, так как cdr - это список.