Как работает "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
- это список.