Я слышал, что clojure не имеет cons cells как большинство языков lisp.
означает ли это, что список clojure не заканчивается пустым списком?
может кто-нибудь объяснить, что именно это означает?
Я слышал, что clojure не имеет cons cells как большинство языков lisp.
означает ли это, что список clojure не заканчивается пустым списком?
может кто-нибудь объяснить, что именно это означает?
Lisp обеспечивает структуру данных примитивных минусов и обозначение для нее.
В этой главе представлены:
( a . b )
(a b c)
nil
для завершения списковcar
, cdr
, cons
, eq
и atom
ff
, subst
, equal
, null
, cadr
, caddr
, null
, append
, among
, pair
, assoc
, sublis
, apply
, eval
,...В начале Lisp добавлены функции мутаций cons-ячеек: rplaca
(означает замену автомобиля) и rplacd
(означает замену cdr). См. Руководство по программированию Lisp 1.5 от Джона МакКарти и др. с 1962 года. Эти функции позволяют нам писать деструктивные функции, а также позволяют создавать циклические структуры данных, такие как циклические списки.
Общий Lisp
Обычно диалекты Lisp реализуют большую часть этого. Общий Lisp не является исключением, и для этого эта функция описана в стандарте Common Lisp: Conses. Примеры с использованием упомянутых выше функций:
; pair two lists into a list of cons cells
; the function pair is called pairlis in Common Lisp
CL-USER 17 > (pairlis '(john mary eva) `(34 29 40))
((EVA . 40) (MARY . 29) (JOHN . 34))
; find a cons cell in a list of cons cells, based on the content of the car of those cons cells
CL-USER 18 > (assoc 'eva
(pairlis '(john mary eva)
`(34 29 40)))
(EVA . 40)
; create a tree out of cons cells and atoms
CL-USER 19 > (cons (cons 10 20) (cons 30 40))
((10 . 20) 30 . 40)
; a cons cell is not an atom
CL-USER 20 > (atom (cons 1 2))
NIL
; a cons cell is not nil
CL-USER 21 > (null (cons 1 2))
NIL
; substitute an item with a new one in a tree
CL-USER 22 > (subst 30 ; new
'bar ; old
'((10 . 20) . (bar . 40))) ; tree
((10 . 20) 30 . 40) ; also written as ((10 . 20) . (30 . 40))
; substitute several items in a tree, using an assoc list
; to describe the substitutions
CL-USER 23 > (sublis '((a . 10) (d . 40)) ; substitutions
'((a . b) . (c . d))) ; tree
((10 . B) C . 40)
Списки - это особый случай символических выражений. Они обычно пишутся без точек:
CL-USER 24 > '(a . (b . nil))
(A B)
Общий Lisp также поддерживает мутирующие операции rplaca
и rplacd
of Lisp 1.5:
CL-USER 25 > (let ((c (cons 0 1))) ; create a cons
(print c) ; print it
(print (rplaca c 'foo)) ; replace the car
(print (rplacd c 'bar)) ; replace the cdr
(print (eq c (rplaca c 'baz))) ; identical ?
(values))
(0 . 1) ; the cons cell
(FOO . 1) ; car replaced
(FOO . BAR) ; cdr replaced
T ; still the same object
Emacs Lisp
Emacs Lisp также реализует вышеуказанные функции:
ELISP> (sublis '((a . 10) (d . 40))
'((a . b) . (c . d)))
((10 . b) c . 40)
Clojure
Clojure не поддерживает эти символические выражения, описанные Джоном МакКарти. Он не имеет ячеек cons, нет точечной нотации и не обеспечивает вышеуказанный интерфейс. Например, атом означает что-то совершенно другое в Clojure. cons
не создает ячейку cons. Списки не состоят из cons-ячеек.
В Clojure точка - это еще один символ:
user=> (count '(1 . 2))
3
Существует примитивная функция для построения списков:
user=> (list 1 2 3)
(1 2 3)
В результате должен быть список:
user=> (list? (list 1 2 3))
true
Существует функция, называемая cons
:
user=> (cons 0 (list 1 2 3))
(0 1 2 3)
Как-то это не список:
user=> (list? (cons 0 (list 1 2 3)))
false
В основном Clojure использует разные структуры данных (- > sequences, логические списки) со своим собственным именованием и семантикой. Даже если имена похожи на имена Lisp, не ожидайте, что они сделают то же самое.
Схема
В языке программирования также предусмотрены аналогичные элементы cons. В нем отсутствуют некоторые функции, но их можно легко реализовать. Например, sublis
может быть реализован как в схеме (см. initdr.scm):
(define (sublis alist tree)
(if (pair? tree)
(cons (sublis alist (car tree))
(sublis alist (cdr tree)))
(if (assv tree alist)
(cdr (assv tree alist))
tree)))
clojure.lang.Cons
.cons
rest
/cdr
представляет собой последовательность, а не Object
.cons
что-то в списке, вектор или ленивая последовательность,
вы получите cons
.cons
es. Все они имеют дело с последовательностями в целом.Другое использование: conj
ing на неопределенную последовательность (ни векторный список, ни набор, ни карта...) не дает cons
.
В соответствии с эта страница из clojure.org:
cons, первый и отдых управляют абстракциями последовательности, а не конкретными ячейками cons
Clojure списки не заканчиваются пустым списком, и они не являются традиционными ячейками cons. Это структуры данных, которые реализуют последовательность. Эта страница программирования для абстракций объясняет подход Clojure к структурам "seqable", включая списки:
В общем, программирование до абстракций дает вам силу, позволяя вам использовать библиотеки функций в разных структурах данных независимо от того, как эти структуры данных реализованы.
Итак, Clojure списки похожи на cons-ячейки, поскольку они реализуют cons
, first
и rest
, но это означает, что они имеют общий интерфейс. Их базовые реализации отличаются друг от друга, но они оба "доступны".
В Common Lisp список представляет собой последовательность ячеек cons. Каждая ячейка cons имеет два слота или указателя, называемые "автомобиль" и "cdr". Автомобиль указывает (или держит) что-то - что угодно. Обычно cdr указывает на другую ячейку cons, или nil
. nil
подсчитывается как конец списка. Clojure дает примерно такую же функциональность с его списками, но основное представление отличается. Он имеет тип данных, называемый Cons
, но не все списки или все части данного списка построены из Cons
s. (Теперь вы должны прочитать jmargolisvt ответ, если вы этого еще не сделали.) [EDIT: Другие ответы показывают, что то, что я говорю здесь о связи между списками и Conses в Clojure, неверно. Можно было бы почувствовать, что оно исправлено в неформальном смысле "списка" - или нет.]
Также обратите внимание, что частично из-за идеи абстракции последовательности, списки как таковые гораздо менее распространены в Clojure, чем в Common Lisp или Scheme. Однако другие типы последовательностей очень распространены.
Также стоит знать, что в Clojure вы не можете предположить, что то, что выглядит как список при его распечатке, на самом деле является списком. Это может быть ленивая последовательность, например, которая не считается списком.
Ниже приведены некоторые потенциально информативные примеры Clojure с использованием списков:
user=> (def foo (list 1))
#'user/foo
user=> foo
(1)
user=> (class foo)
clojure.lang.PersistentList
user=> (def bar (cons 2 foo))
#'user/bar
user=> bar
(2 1)
user=> (class bar)
clojure.lang.Cons
(Оба foo
и bar
считаются списками, хотя class
возвращает разные типы данных.)
user=> (next bar)
(1)
user=> (rest bar)
(1)
user=> (class (next bar))
clojure.lang.PersistentList
user=> (class (rest bar))
clojure.lang.PersistentList
user=> (next foo)
nil
user=> (rest foo)
()
user=> (= nil ())
false
user=> (rest ())
()
user=> (rest nil)
()
user=> (next ())
nil
user=> (next nil)
nil
В Common Lisp вы можете переносить объект на другой объект, отличный от списка, или nil
. Результатом является "пунктирный список" (1 . 2)
, который является единственной ячейкой cons, в которой указатель cdr указывает на что-то иное, чем другая ячейка cons или nil
, как это было бы в обычном списке. Попробуйте это в Clojure:
user=> (cons 1 2)
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:528)
Пока я нахожусь в этом, еще одно поразительное отличие от Common Lisp (в котором nil
= ()
= false):
user=> (= nil false)
false
user=> (= () false)
false
Однако, хотя nil
не false
, вы можете использовать его как false
:
user=> (if nil "nil works like true" "nil works like false")
"nil works like false"
Однако вы не можете сделать это с пустым списком:
user=> (if () "() works like true" "() works like false")
"() works like true"
(Несмотря на эти примеры, в целом Clojure намного проще и элегантнее, чем Common Lisp, IMO. Даже люди, которые также любят Common Lisp - как и я, должны признать, что Common Lisp не является ни простым, ни элегантным. У него есть свой вид красоты.)