Clojure: нет cons-ячеек

Я слышал, что clojure не имеет cons cells как большинство языков lisp.

означает ли это, что список clojure не заканчивается пустым списком?

может кто-нибудь объяснить, что именно это означает?

Ответ 1

Lisp обеспечивает структуру данных примитивных минусов и обозначение для нее.

См. Джон Маккарти, Рекурсивные функции символических выражений и их вычисление машиной, часть I, 1960, глава 3, Рекурсивные функции символических выражений.

В этой главе представлены:

  • Символьные выражения, сделанные из атомов и пар символических выражений, написанных с использованием точечной нотации: ( 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)))

Ответ 2

  • Clojure имеет структуру cons: clojure.lang.Cons.
  • Он используется для результатов вызовов cons
  • ... и ничто другое: ни списки, ни векторы, ни ленивые последовательности любого рода.
  • Он также не может использоваться для пар объектов вообще: хвост /rest/cdr представляет собой последовательность, а не Object.
  • Если вы cons что-то в списке, вектор или ленивая последовательность, вы получите cons.
  • Но, как показывают другие ответы, нет функций, которые заключить сделку в cons es. Все они имеют дело с последовательностями в целом.

Другое использование: conj ing на неопределенную последовательность (ни векторный список, ни набор, ни карта...) не дает cons.

Ответ 3

В соответствии с эта страница из clojure.org:

cons, первый и отдых управляют абстракциями последовательности, а не конкретными ячейками cons

Clojure списки не заканчиваются пустым списком, и они не являются традиционными ячейками cons. Это структуры данных, которые реализуют последовательность. Эта страница программирования для абстракций объясняет подход Clojure к структурам "seqable", включая списки:

В общем, программирование до абстракций дает вам силу, позволяя вам использовать библиотеки функций в разных структурах данных независимо от того, как эти структуры данных реализованы.

Итак, Clojure списки похожи на cons-ячейки, поскольку они реализуют cons, first и rest, но это означает, что они имеют общий интерфейс. Их базовые реализации отличаются друг от друга, но они оба "доступны".

Ответ 4

В 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 не является ни простым, ни элегантным. У него есть свой вид красоты.)