clojure:没有利弊细胞

时间:2015-12-18 03:12:16

标签: clojure scheme lisp common-lisp cons

我听说clojure在大多数lisp语言中都没有 cons cells

这是否意味着clojure列表不以空列表结尾?

任何人都可以解释这究竟意味着什么吗?

4 个答案:

答案 0 :(得分:24)

Lisp提供原始的cons数据结构及其符号。

请参阅John McCarthy, Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I, 1960, Chapter 3, Recursive Functions of Symbolic Expressions

该章介绍:

  • 由原子和符号表达式对使用点符号表示的符号表达式:( a . b )
  • 用于缩写某些符号表达式(a b c)
  • 的列表表示法
  • 用于终止列表的原子符号nil
  • 原始函数carcdrconseqatom
  • 其他几项功能:ffsubstequalnullcadrcaddrnull,{ {1}},appendamongpairassocsublisapply,...

在Lisp的早期,添加了改变cons单元格的函数:eval(表示替换汽车)和rplaca(表示替换cdr )。请参阅LISP 1.5 Programmer's Manual by John McCarthy et al. from 1962。这些函数允许我们编写破坏性函数,并允许我们创建循环的基于cons的数据结构,如循环列表。

Common Lisp

通常,Lisp方言实现了大部分内容。 Common Lisp也不例外,Common Lisp标准中描述了这个功能:Conses。使用上述功能的示例:

rplacd

列表是符号表达式的特例。它们通常没有点写:

; 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)

Common Lisp还支持Lisp 1.5的变异操作CL-USER 24 > '(a . (b . nil)) (A B) rplaca

rplacd

Emacs Lisp

Emacs Lisp还实现了上述功能:

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

<强> Clojure的

Clojure 不支持John McCarthy所描述的这些符号表达式。它没有缺陷单元,没有点符号,也没有提供上述接口。例如, atom 意味着Clojure中完全不同的东西。 ELISP> (sublis '((a . 10) (d . 40)) '((a . b) . (c . d))) ((10 . b) c . 40) 不会创建一个cons小区。列表不是由缺陷单元组成的。

在Clojure中,一个点只是另一个符号:

cons

有一个原始函数来构造列表

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)

基本上Clojure使用不同的数据结构( - &gt; sequences逻辑列表)和自己的命名和语义。即使姓名与Lisp名称相似,也不要期望他们也这样做。

<强>方案

编程语言 Scheme 还提供类似于上面的缺点单元。它缺少一些功能,但可以很容易地实现。例如,user=> (list? (cons 0 (list 1 2 3))) false 可能在Scheme中实现(请参阅initdr.scm):

sublis

答案 1 :(得分:7)

  • Clojure确实有一个缺点结构:clojure.lang.Cons
  • 用于cons来电
  • 的结果
  • ......别无其他:既不是列表,也不是矢量,也不是任何类型的懒惰序列。
  • 一般情况下也不能用于成对的对象:尾部/ rest / cdr 是一个序列,而不是Object
  • 如果您cons列表,矢量或懒惰序列, 你得到一个Cons
  • 但是,正如其他答案所说的那样,没有任何功能 处理Cons es。它们都是按顺序处理的。

另一个用途:conj到一个不确定的序列(矢量列表,既不设置也不映射......)产生Cons

答案 2 :(得分:6)

根据this page from clojure.org

  

缺点第一次休息操纵序列抽象,而不是具体的缺点

Clojure列表不以空列表结尾,它们不是传统的cons单元格。它们是实现排序的数据结构。 This page on programming to abstractions解释了Clojure对&#34; seqable&#34;的解决方法。结构,包括清单:

  

通常,对抽象的编程可以让您在不同的数据结构上使用函数库,而不管这些数据结构是如何实现的。

所以Clojure列表就像cons单元格一样,它们实现consfirstrest但这只意味着它们共享一个公共接口。它们的底层实现有所不同,但它们都是“可以选择的”#34;。

答案 3 :(得分:1)

在Common Lisp中,列表是一系列缺点单元格。每个cons单元有两个槽或指针,称为“car”和“cdr”。汽车指向(或持有)某物 - 任何东西。 cdr通常指向另一个cons单元格,或nilnil计为列表的末尾。 Clojure为其提供了与其列表大致相同的功能,但底层表示是不同的。它确实有一个名为Cons的数据类型,但并非所有列表或给定列表的所有部分都是从Cons构建的。 (现在你应该阅读jmargolisvt的答案,如果你还没有。)[编辑:其他答案显示我在这里所说的关于Clojure中列表和Conses之间关系的说法是错误的。人们可能会觉得这是非正式的“列表” - 或者没有。“

另请注意,部分由于序列抽象概念,列表本身在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

foobar都被视为列表,即使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既不简单也不优雅。有自己的美丽。)