Scope of functions and variables within a function and / or let statement for lisp

I am having trouble understanding the scope of variables and functions when defined inside a function call. I tried to search this area but couldn't find a suitable answer (or maybe I was looking for the wrong thing), so I decided to write some functions to test the things themselves:

(defun test-scope1 ()
  (setf myvar 1)
  (defun set-var1 ()
    (setf myvar 2))
  (set-var1))

      

With this function, I just wanted to see that something got global. I would expect myvar and set-var to be defined globally because I have no scope here. As expected, before calling the (test-scope1)

command myvar

and (set-var)

giving me errors. After the call, (test-scope1)

I can run myvar

and (set-var)

in the interpreter to get 2.

Thinking to myself, it would be nice to encapsulate my variables, I decided to add let inside my function, so getting the following test:

(defun test-scope2 ()
  (let ((myvar 10))
    (defun set-var2 ()
      (setf myvar 20))
    (set-var2)))

      

I would expect to be myvar

stuck in the scope of the let block, but I couldn't guess set-var2

. It can get stuck in a let block, or it can be globally defined. After running, (test-scope2)

I try to access myvar

and get 2. This means that this new function has its own myvar as it is still 2 from the previous function. I am trying to run (set-var2)

and get 20.

I'm not entirely surprised that the function is defined globally after being run in the let block, but now I'm very confused that myvar is changing its access. Since it didn't change my global copy of myvar, it would seem there is some kind of variable floating around it.

Now I want to see if I can manipulate this floating variable, so I create this third function.

(defun test-scope3 ()
  (let ((myvar (if (ignore-errors myvar)
                   myvar
                   100)))
    (defun set-var3 ()
      (setf myvar (+ myvar 100)))
    (set-var3)))

      

Instead of just setting the variable to a fixed value, I want to increment it based on the previous value. I am checking two things here. First, when test-scope3 is called. I wanted to see if I could raise the "previous value" of myvar, since if it floats somewhere, maybe I can access it again. This probably won't be good practice, but it's not the point. Ignore-error is in case there wasn't really a previous value floating around, in which case I choose the default 100.

The second thing I'm testing is that set-var3 adds 100 to the previous myvar value. I want to see if this will set up this float, or statically somehow. I have no idea what my function should return.

Upon startup, (test-scope3)

I'm completely surprised to see 102. So apparently my test scope3 found that the value of myvar triggers test-scope1. But after checking the value of myvar in the interpreter, it is still 2. Then I run (set-var3)

and get the return value 202. Ok, so it added 100 again to my previous value. Calling it again returns 302 and so on. But the call (test-scope3)

resets that value back to 102 again.

I wrote another function as a double nested let command. I just ran this in two ways, without defining myvar for the let arguments. This function returns 10002. Then I tried to set the local myvar to 50 and it returned 1050.

(defun test-scope4 ()
  (let () ; or ((myvar 50))
    (let ((myvar (if (ignore-errors myvar)
                      myvar
                      2000)))
      (defun set-var4 ()
        (setf myvar (+ myvar 1000)))
    (set-var4))))

      

So with all this together, here are some of my specific questions:

  • When I do a defun inside another defun, even in a let block, why does this function become available globally?
  • When I call set-var #, what variables (or in what scope) are they accessing? Or maybe in a more appropriate way, when I define a function inside another operator, what am I actually binding?
  • When I use a variable myvar

    in a function, where does it come from? From my 4th example, the guess is that it looks for the symbol myvar in the current scope, then checks the level above until it finds a value for it.

Sorry if this was very verbose and my questions were poorly formed. I tried to explore things to the best of my ability. Actually this all leads to my real question, which after I have written everything I understand may be outside (not intended to be pun intended) of this question, as I have set it so far.

The problem of hiding my internal functions as stated above can be handled with a lambda expression; however, I would really like to make a recursive function inside a larger block that uses a block to store values โ€‹โ€‹without loading them directly into the function. As far as I know, this is not possible with a lambda expression. For example, consider the following function that does this.

(defun outer-function (start)
  (let ((x start))
    (defun increment-to-ten ()
      (setf x (+ x 1))
      (if (< x 10)
          (increment-to-ten)))
    (increment-to-ten)
    (print x)))

      

Instead, one could recursively implement the arguments as

(defun increment-to-ten-recursive (x)
  (if (< x 10)
      (increment-to-ten-recursive (+ x 1))
      10))

      

If you have a solution for this, that would be great or if my thinking is completely wrong and there is a better way to do it, which would be great. It just seems like you have block store data and then just call the recursive function with no arguments to work on that data.

+3


source to share


2 answers


Why is it difficult to discuss the effects you see:

You do things that are undefined in Common Lisp: plant undeclared variables in test-scope1

: myvar

. It is unclear at this point how the following code behaves.

Undeclared variables

It's undefined what effect it has when an undeclared variable is set foo

. Implementations allow this. Some will warn. SBCL:

* (setf foo 10)
; in: SETF FOO
;     (SETF FOO 10)
; ==>
;   (SETQ FOO 10)
; 
; caught WARNING:
;   undefined variable: FOO
; 
; compilation unit finished
;   Undefined variable:
;     FOO
;   caught 1 WARNING condition

      

Global variables are defined using DEFPARAMETER

and DEFVAR

. Local variables are defined LET

, LET*

and by function parameters. Because DEFPARAMETER

u DEFVAR

define globally special (using dynamic binding) variables, it is common to write them as *some-variable*

- mark around it *

that are part of the symbol name, not special syntax. This is a convention for special variables.

Nested DEFUN



Nested objects are DEFUN

not used in Common Lisp. DEFUN

is a top-level form that sets a global function. To determine the local functions, use FLET

and LABELS

. You can nest forms DEFUN

, but this is bad style and you won't find it in actual Lisp code. Do not insert forms DEFUN

. Note: this is different from the schema where forms can be nested DEFINE

. Since nesting is DEFUNs

not used, there is no point in discussing effects. Convert examples to use local functions defined by FLET

or LABELS

.

  • When I do a defun inside another defun, even in a let block, why does this function become available globally?

Because it DEFUN

defines global functions. This is his goal.

You need to rewrite your examples so that effects can be discussed:

  • declare all variables

  • don't use nested DEFUN

    s

We could try to understand the code you have in your current examples, but a lot depends on the implementation and sequence of actions. We are either not in the portable Common Lisp realm, or we are doing things (nested DEFUNs) that have no practical use in Common Lisp.

+5


source


As Reiner explained, the answer to your first question is that defun defines global functions. Use labels to define local functions.

Reiner is also right as it depends on your lisp configuration. Many Lisps are aware of two types of scopes, lexical and dynamic. Lexical volume is the one that you can see when you look at the code page and that you are used to in most other programming languages. Dynamic scale is what you get when lisp looks for the value of a variable in the environment where the function was called, not the one where it was defined.

Finally, your code makes it easy to close. When you put a lambda (defun) inside a let, you create a closure, eg. your variables "hover or float around" for future use of the function, even if the function containing them returns to the caller.

So what might be going on (as Reiner said, this is hard to see since you are doing things that would normally not be done, such as nested defuns):

So ... maybe test-scope1 is using the global myvar and set-var is referencing the global myvar since we are dealing with lexical scope. test-scope2 creates a closure that creates myvar with lexical scope only available to set-var2. Since this myvar has lexical scope, calling set-var on test-scope2 will not use this myvar, but still global.

I'm not sure why calling test-scope3 gets you 102, as test-scope3 just calls set-var which should set the global myvar to 2 and not 102. The only code that can explain 102 is var3 and this is not called when you call test-scope3. Here's what might happen if you got 2 instead of 102 when you call test-scope3:

(defun test-scope3 ()
  (let ((myvar (if (ignore-errors myvar)
                   myvar
                   100)))
    (defun set-var3 ()
      (setf myvar (+ myvar 100)))
    (set-var)))

      



With let, you create a local binding for the lexically scoped myvar, but you refer to the global myvar in it - the required form is mywar (if (ignore-errors myvar)). Later, you call set-var, which should reference the global myvar again, since test'scope3 let uses lexical scope to bind myvar. Then, when you first call set-var3, it takes the myvar associated with the above let, which is currently 2, and adds 100 to it. When you call it a second time, you add 100 to the lexically related myvar again that is still hanging around in the closure you created in test-scope3, and so on. But when you call test-scope3 again, you create a new closure (but replace the old one because you use defun to give it the same name ...),which again sets the initial value of the private myvar to the value of the global myvar, which is 2.

I suggest you read Paul Graham's excellent ANSI Common lisp and On Lisp, which explains special variables (dynamic scope), lexical scope, and depth-closure.

You can have several common variables here:

(let ((myvar 0))
  (defun reset () 
    (setf myvar 0)
  (defun inc ()
    (setf myvar (+ myvar 1))))

      

This creates two closures and forces them to share myvar, and since myvar has lexical scope, you cannot access that copy of myvar from elsewhere. So you've basically granted exclusive control over myvar until reset and inc.

This could be one way to create your recursive function that doesn't require a value passed to it with each iteration.

0


source







All Articles