clojure: нет минусов клетки

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

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

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

4 ответа

Решение

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...

В самом начале в Лиспе были добавлены функции для изменения 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, но не все списки или все части данного списка построены из Conss. (Теперь вам следует прочитать ответ 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 не простой и не элегантный. вид красоты.)

Другие вопросы по тегам