Lisp / Scheme:评估嵌套的空列表/cons:(())

huangapple go评论75阅读模式
英文:

Lisp / Scheme : Evaluating nested empty list/cons : (())

问题

I'm using this Lisp compiler for testing

There is one things that I don't get it :
If an empty list () evaluate to itself :

(format t " ~:a" ())
;; => ()

why do evaluating several nested empty list () don't evaluate to an empty list ?

(format t " ~:a" (()))
;; => EVAL: undefined function NIL
;; why not () ?

(format t " ~:a" ((())))
;; => EVAL: (NIL) is not a function name; try using a symbol instead
;; why not () ?

From my point of view, () and NIL are the same, so the inner empty list should first evaluate to itself, then the outer list should "call" the newly inner evaluated empty list

I my head, I see it like this should happen when doing the calculation:
(bold + italic = what is currently evaluated)

(()) => (()) => (()) => ()

Seem like Scheme also don't allow nested empty list call

Thank for reading

英文:

I'm using this Lisp compiler for testing

There is one things that I don't get it :
If an empty list () evaluate to itself :

(format t "~:a" ())
;; => ()

why do evaluating several nested empty list () don't evaluate to an empty list ?

(format t "~:a" (()))
;; => EVAL: undefined function NIL
;; why not () ?

(format t "~:a" ((())))
;; => EVAL: (NIL) is not a function name; try using a symbol instead
;; why not () ?

From my point of view, () and NIL are the same, so the inner empty list should first evaluate to itself, then the outer list should "call" the newly inner evaluated empty list

I my head, I see it like this should happen when doing the calculation:
(bold + italic = what is currently evaluated)

(()) => (()) => (()) => ()

Seem like Scheme also don't allow nested empty list call

Thank for reading

答案1

得分: 9

在Common Lisp及其前身中,空列表()由于特殊规定而被视为自我评估:它是唯一具有这种属性的列表。它还是唯一的一个同时也是符号的列表,此处是符号nil(实际上是NIL)。或许你会觉得,在实现的底层,有一些地方只是简单地执行了(defconstant nil ())的等效操作,但实际情况比这更神奇:

> (eq () 'nil)
t

> (symbolp ())
t

> (symbol-name ())
"NIL"

它不仅仅是符号nil的值,它就是符号nil。在CL中,()真的非常神奇。比在Scheme中更神奇,例如:在Scheme中,()仍然是神奇的,只是稍微少一点。在Scheme中,()是唯一一个不是对(pair)的列表,但它既不是符号,也不是自我评估的。

所以,好吧,这解释了为什么()会自我评估:因为它是神奇的。

现在考虑尝试评估这个表达式:

(())

首先,这不是空列表:它是一个包含一个元素的列表,该元素是空列表。从评估的角度来看,它是一个复合形式。嗯,由于空列表(这个列表的第一个元素)也是nil,我们可以将这个复合形式重写为

(nil)

这是相同的事情(equal '(nil) '(()))为真。

好的,现在评估器对复合形式做什么呢?它查看它们的第一个元素并决定要执行什么操作(参见这里)。

  • 如果它是一个符号,那么该符号应该命名以下之一:

    • 一个特殊运算符
    • 或(可能是局部的)函数
    • 或(可能是局部的)宏

    并且将相应地处理它。

  • 如果它不是符号,它可能是一个以(lambda ...)开头的列表,该列表会转换为匿名函数并被调用。

  • 没有更多的情况。

那么(())或等效的(nil)呢?它是一个复合形式,它的第一个元素是符号nil。所以

  • nil是否命名了一个特殊运算符?不,(special-operator-p 'nil)为假;
  • nil是否命名了一个宏?不,(macro-function 'nil)为假;
  • nil是否命名了一个函数?不 - (fboundp 'nil)为假。

所以评估器无法处理这个表达式:它是非法的。


请注意,在Scheme中,相同的事情也是非法的,但原因略有不同:对于Scheme来说,空列表不是自我评估的,因此评估器看到(())并表示(这次忽略宏),这是一个过程调用,其中过程将是评估()的结果,但这是一个错误。如果我说(define nil '()),然后(nil)仍然是一个错误:它将评估nil并得到(),但()不是一个过程。


关于CL的一个令人惊讶的事情是,(())可以被赋予(局部的)含义。这是令人惊奇的,因为这是符合CL的:

(flet ((() () ()))
  (()))

这是符合的,因为允许在CL中为CL包中没有全局函数定义的符号在局部上建立函数定义:

如果COMMON-LISP包的外部符号未被定义为标准化函数、宏或特殊运算符,那么可以在局部上将其绑定为函数(例如,使用flet),声明该绑定的ftype,并(在提供此能力的实现中)跟踪该绑定。--CLHS

然后()是符号nil,它没有全局函数定义。

英文:

In Common Lisp and its ancestors, the empty list, (), is self evaluating by special dispensation: it's the only list which has this property. It's also the only list which is also a symbol, in this case the symbol nil (really NIL). It's tempting to think that, somewhere in the guts of the implementation something just said the equivalent of (defconstant nil ()), but it's much more magic than that:

> (eq () 'nil)
t

> (symbolp ())
t

> (symbol-name ())
"NIL"

It's not just the value of the symbol nil, it is the symbol nil. () is really very magic in CL. More magic than it is in Scheme, say: in Scheme it's still magic just rather less so. In Scheme, () is the only list which is not a pair (a cons), but it is not also a symbol and it is not self-evaluating.

So, OK, that explains why () evaluates to itself: it does so because it is magic.

So now consider trying to evaluate this form:

(())

Well, first of all this is not the empty list: its a list with one element, which is the empty list. From the point of view of evaluation it is a compound form. Well, as the empty list (the first element of this list) is also nil we can rewrite this compound form as

(nil)

This is the same thing: (equal '(nil) '(())) is true.

OK, well now what does the evaluator do with compound forms? It looks at their first element and decides what to do (see here.

  • If it is a symbol, then the symbol should name one of

    • a special operator
    • or a (possibly local) function
    • or a (possibly local) macro

    and it will be processed accordingly.

  • If it is not a symbol it may be a list beginning (lambda ...) which is turned into an anonymous function and called.

  • There are no more cases.

Well, which is (()), or equivalently (nil)? It's a compound form, and the first element of it is the symbol nil. So

  • does nil name a special operator? no -- (special-operator-p 'nil) is false;
  • does nil name a macro? no -- (macro-function 'nil) is false;
  • does nil name a function? No - (fboundp 'nil) is false.

So the evaluator can't process this form: it's not legal.


Note that in Scheme the same thing would also be not legal, but for slightly different reasons: for Scheme, the empty list is not self-evaluating, so the evaluator sees (()) and says (ignoring macros this time) that this is a procedure call, where the procedure is going to be the result of evaluating (), but that's an error. If I said (define nil '()) then (nil) would still be an error: it would evaluate nil and get (), but () is not a procedure.


A surprising thing about CL is that (()) can be given (local) meaning. This is, amazingly enough, conforming CL:

(flet ((() () ()))
  (()))

It's conforming because you're allowed to locally establish function definitions for symbols in CL if they do not have global function definitions:

> If an external symbol of the COMMON-LISP package is not defined as a standardized function, macro, or special operator, it is allowed to lexically bind it as a function (e.g., with flet), to declare the ftype of that binding, and (in implementations which provide the ability to do so) to trace that binding. -- CLHS

And then () is the symbol nil which does not have a global function definition.

huangapple
  • 本文由 发表于 2023年8月10日 16:46:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76874051.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定