clojure: нет минусов клетки
Я слышал, что у clojure нет консов, как у большинства языков lisp.
Означает ли это, что список clojure не заканчивается пустым списком?
Кто-нибудь может объяснить, что это значит?
4 ответа
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
...
В самом начале в Лиспе были добавлены функции для изменения cons-ячеек: rplaca
(значит заменить автомобиль) и rplacd
(означает заменить CDR). См. Руководство программиста LISP 1.5 от John McCarthy et al. с 1962 г. Эти функции позволяют нам писать деструктивные функции, а также позволяют создавать циклические структуры данных на основе конусов, такие как циклические списки.
Common Lisp
Обычно диалекты Лисп реализуют большую часть этого. Common 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)
Common Lisp также поддерживает мутирующие операции rplaca
а также rplacd
Лисп 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 не поддерживает эти символические выражения, описанные Джоном Маккарти. Он не имеет ни минусов, ни точечных обозначений и не предоставляет вышеуказанный интерфейс. Например, атом означает нечто совершенно иное в Clojure. 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 использует различные структуры данных (-> последовательности, логические списки) со своими именами и семантикой. Даже если имена похожи на имена 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.org:
cons, first и rest манипулируют последовательными абстракциями, а не конкретными клетками cons
Списки Clojure не заканчиваются пустым списком и не являются традиционными консолями. Это структуры данных, которые реализуют последовательность. Эта страница по программированию абстракций объясняет подход Clojure к "секвенируемым" структурам, включая списки:
В целом, программирование на абстракции дает вам силу, позволяя вам использовать библиотеки функций с различной структурой данных независимо от того, как эти структуры реализованы.
Так что списки Clojure похожи на минусы в том, что они реализуют cons
, first
, а также rest
но это только означает, что они имеют общий интерфейс. Их базовые реализации различаются, но оба они "последовательны".
- Clojure имеет структуру минусов:
clojure.lang.Cons
, - Используется для результатов
cons
звонки - ... и ничего больше: ни списки, ни векторы, ни ленивые последовательности любого рода.
- Также его нельзя использовать для пар объектов в целом: хвост /
rest
/cdr
это последовательность, а неObject
, - если ты
cons
что-то на список, вектор или ленивую последовательность, вы получаетеCons
, - Но, как показывают другие ответы, нет никаких функций, которые имеют дело с
Cons
эс. Все они имеют дело с последовательностями в целом.
Еще одно использование: conj
Вхождение в неопределенную последовательность (ни вектор, ни набор, ни отображение...) не дает Cons
,
В Common Lisp список - это последовательность cons-ячеек. Каждая консольная ячейка имеет два слота или указателя, которые называются "car" и "cdr". Автомобиль указывает (или держит) что-то - что угодно. Cdr обычно указывает либо на другую конс-ячейку, либо nil
, nil
считается концом списка. Clojure предоставляет вам примерно те же функции со своими списками, но базовое представление отличается. У него есть тип данных под названием Cons
, но не все списки или все части данного списка построены из Cons
s. (Теперь вам следует прочитать ответ jmargolisvt, если вы этого еще не сделали.) [РЕДАКТИРОВАТЬ: Другие ответы показывают, что то, что я говорю здесь о связи между списками и 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
знак равно ()
= ложь):
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 не простой и не элегантный. вид красоты.)