Why does this function return a different value ev

2019-01-03 00:11发布

Can someone explain the following behavior? Specifically, why does the function return a different list every time? Why isn't some-list initialized to '(0 0 0) every time the function is called?

(defun foo ()
  (let ((some-list '(0 0 0)))
    (incf (car some-list))
    some-list))

Output:

> (foo)
(1 0 0)
> (foo)
(2 0 0)
> (foo)
(3 0 0)
> (foo)
(4 0 0)

Thanks!

EDIT:

Also, what is the recommended way of implementing this function, assuming I want the function to output '(1 0 0) every time?

4条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-01-03 00:44

Wanted to write one myself, but I found a good one online:

CommonLisp has first class functions, i.e. functions are objects which can be created at runtime, and passed as arguments to other functions. --AlainPicard These first-class functions also have their own state, so they are functors. All Lisp functions are functors; there is no separation between functions that are "just code" and "function objects". The state takes the form of captured lexical variable bindings. You don't need to use LAMBDA to capture bindings; a top-level DEFUN can do it too: (let ((private-variable 42)) (defun foo () ...))

The code in the place of ... sees private-variable in its lexical scope. There is one instance of this variable associated with the one and only function object that is globally tied to the symbol FOO; the variable is captured at the time the DEFUN expression is evaluated. This variable then acts something like a static variable in C. Or, alternately, you can think of FOO as a "singleton" object with an "instance variable". --KazKylheku

Ref http://c2.com/cgi/wiki?CommonLisp

查看更多
在下西门庆
3楼-- · 2019-01-03 00:50

'(0 0 0) is a literal object, which is assumed to be a constant (albeit not protected from modification). So you're effectively modifying the same object every time. To create different objects at each function call use (list 0 0 0).

So unless you know, what you're doing, you should always use literal lists (like '(0 0 0)) only as constants.

查看更多
你好瞎i
4楼-- · 2019-01-03 00:53

'(0 0 0) in code is literal data. Modifying this data has undefined behavior. Common Lisp implementations may not detect it at runtime (unless data is for example placed in some read-only memory space). But it can have undesirable effects.

  • you see that this data may be (and often is) shared across various invocations of the same function

  • one of the more subtle possible errors is this: Common Lisp has been defined with various optimizations which can be done by a compiler in mind. For example a compiler is allowed to reuse data:

Example:

(let ((a '(1 2 3))
      (b '(1 2 3)))
  (list a b))

In above code snippet the compiler may detect that the literal data of a and b is EQUAL. It may then have both variables point to the same literal data. Modifying it may work, but the change is visible from a and b.

Summary: Modification of literal data is a source of several subtle bugs. Avoid it if possible. Then you need to cons new data objects. Consing in general means the allocation of fresh, new data structures at runtime.

查看更多
小情绪 Triste *
5楼-- · 2019-01-03 01:02

On a side note, defining this function in the sbcl REPL you get the following warning:

  caught WARNING:
    Destructive function SB-KERNEL:%RPLACA called on constant data. 
    See also: 
      The ANSI Standard, Special Operator QUOTE 
      The ANSI Standard, Section 3.2.2.3

Which gives a good hint towards the problem at hand.

查看更多
登录 后发表回答