LISP local/global variable assignment

后端 未结 3 1334
情话喂你
情话喂你 2021-02-08 14:02

If we define a function something like

(defun foo(x)
  (setf x somevalue))

Is x defined as a local variable or global? using setf/

相关标签:
3条回答
  • 2021-02-08 14:20

    To draw a parallel to other languages like C, C++, Java or Python what your code changes is a "local variable" even if this is not a wording that a Lisper would use (the wording in Lisp parlance would be a local "binding").

    You can create local variables by using function parameters like your example does, or using some standard forms like:

    • (let ((x 12)) ...)
    • (do ((x 0 (1+ i))) ...)
    • (dotimes (x 10) ...)
    • (loop for x from 0 to 10 do ...)

    On the other hand it is possible that in your implementation all local variables are created using parameters and other forms simply are macros expanding to that. For example:

    (let ((x 10)) ...)
    

    is equivalent to

    (funcall (lambda (x) ...) 10)
    

    Note also that indeed reading your code fragment it's possible that x is in a sense a "global variable" because it could have been declared special:

    (defvar x 12)
    ;; ==> x
    
    (defun bar ()
      (format t "The value of x is ~a" x))
    ;; ==> bar
    
    (defun foo (x)
      (bar))
    ;; ==> foo
    
    (foo 42)
    The value of x is 42
    ;; ==> NIL
    
    x
    ;; ==> 12
    

    If you declare a variable "special" using (defvar ...) it will be handled differently: it's like every time you use it as a parameter or you use it in a (let ..) form what the code will do is saving the current value, using the new provided value and then restoring the value after you exit the function or let.

    So those variables are both "global" (because outer functions can see them) but also local (because after your function or let terminates the previous value will be restored).

    The standard convention is to name special variables with "earmuffs" i.e. with an asterisk both at the start and at the end of the name like:

    (defvar *x* 12)
    

    This helps who reads your code to understand that the variable is special. Note that however this is not mandated by the language and any name can be used for a special variable.

    There is nothing similar to special variables in C, C++, Java or Python.

    One last note about setq and setf. Things are a bit tricky here because you need to understand lower levels of Lisp to see why setq is needed. If you are using Common Lisp then you should simply forget about setq and always use setf.

    setf is a macro that will expand to setq when needed (however also setq can change into setf when needed (symbol macros) and this is where things may get confusing for a newbie).

    Your last example is a case of a "closure". When you define a function (either named or unnamed with a (lambda ...) form) the function can "capture" the variables that are visible and use them later. A simpler case often shown is the "adder":

    (defun adder (x)
      (lambda (y) (incf x y)))
    

    this function returns a function that will keep adding the passed value to an internal totalizer:

    (let ((f (adder 10)))
      (print (funcall f 3))
      (print (funcall f 9))
      (print (funcall f 11)))
    

    the output will be 13 (10 + 3), 22 (13 + 9) and 33 (22 + 11).

    The anonymous function "captured" the local variable x and can use it even after exiting the adder function. In languages like C, C++ or Java a local variable cannot survive when you exit the scope that defined the variable.

    C++11 has unnamed functions, but still variables cannot be captured and survive the scope (they can be copied to local variables of the unnamed function, but this is not the same thing).

    0 讨论(0)
  • 2021-02-08 14:35

    Actually, x is local to the function, so setf or setqing it only changes the local variable x.

    To answer the second part of your question, there is another way to define a local variable other than let:

    (funcall (lambda (var1 var2...)
               ... use var1 var2 ...)-
       val1 val2 ...)
    

    In fact, defun could be implemented as

    `(setf (symbol-function ,name) (lambda ,args ,@body))
    

    though all the implementations I've checked do more than that.

    Also, the symbol-function place allows you to do this:

    (setf (symbol-function '+) (function -))
    

    though that's generally a bad idea.

    Your second question

    What's happening there is that x is local to the scope that contains the defun. It's not a global variable. What you're doing with that defun is creating a closure that "captures" all the lexically-scoped (not created with defvar/defparameter) variables in the surrounding scopes and holds on to them for the future. If you want to inspect the value of x, add another function

    (defun get-x () x)
    

    inside the let. To put it another way, x is local to the let that the function is in. The situation you've provided is similar to:

    (let ((x 3))
        (print x)
        (let ((y 7))
            (print (list x y))
            (setf x y))
        (print x))
    

    except the inner let is replaced by a defun (which is a lambda).

    0 讨论(0)
  • 2021-02-08 14:41

    In your code x is a parameter to the f function and thus local to the function. The call to setf does not create new variable, but simply sets the value of the existing local variable x. Using setq instead of setf would behave the same.

    0 讨论(0)
提交回复
热议问题