Variable in function

I see the following code ... The first call (next-num) returns 1 , and the second returns 2 .

 (define next-num (let ((num 0)) (lambda () (set! num (+ num 1)) num))) (next-num) ; 1 (next-num) ; 2 

What I can’t understand ... num is created by let inside next-num , it is a kind of local variable ... As the circuit knows that next-num is called every time, the num value is not erased let ((num 0)) ; How does the circuit know that we always always change num when we call next-num ?

It seems that num is both local and static ... How to define a local variable, but not static?

+6
source share
1 answer

This is a “lexical closure”, and you are right that num , a “private variable” is similar to a static variable in C, for example: it is visible only in the code in the form let (its “lexical scope”), but it is saved throughout the program, and not reinitialized with every function call.

I think the part you are confusing is this: " num is created by pasting inside next-num , it's kind of a local variable." This is not true because the let block is not part of the next-num function: it is actually an expression that creates and returns a function that is then bound to next-num . (This is very different, for example, from C, where functions can only be created at compile time and defined at the top level. In Scheme, functions are values, such as integers or lists, that any expression can return).

Here's another way to write (almost) the same thing, which makes it clearer that define simply associates next-num with the value of the expression returning the function:

 (define next-num #f) ; dummy value (let ((num 0)) (set! next-num (lambda () (set! num (+ num 1)) num))) 

It is important to note the difference between

 (define (some-var args ...) expression expression ...) 

which makes some-var function that executes all expressions when called, and

 (define some-var expression) 

which binds some-var to the expression value evaluated then and there. Strictly speaking, the previous version is not needed because it is equivalent

 (define some-var (lambda (args ...) expression expression ...)) 

Your code is almost the same as this, with the addition of a variable with the lexical scope num , around the lambda form.

Finally, here is the key difference between private variables and static variables, which makes locks more powerful. If you wrote the following:

 (define make-next-num (lambda (num) (lambda () (set! num (+ num 1)) num))) 

then each call to make-next-num will create an anonymous function with a new variable num , which is private to this function:

 (define f (make-next-num 7)) (define g (make-next-num 2)) (f) ; => 8 (g) ; => 3 (f) ; => 9 

This is a really cool and powerful trick that explains most of the languages ​​with lexical closures.

Edited to add: you ask how Scheme “knows” which num to change when next-num called. In general terms, if not in implementation, it is actually quite simple. Each expression in the Schema is evaluated in the context of the environment (lookup table) of variable bindings, which are name associations in places where values ​​can be stored. Each evaluation of a let form or function call creates a new environment, expanding the current environment with new bindings. To have lambda forms that behave like closures, an implementation presents them as a structure consisting of the function itself and the environment in which it was defined. Then the calls to this function are calculated by expanding the binding environment in which the function was defined, and not the environment in which it was called.

Older Lisps (including Emacs Lisp until recently) had a lambda , but not a lexical scope, so although you could create anonymous functions, calls to them would be evaluated in the calling environment and not in the definition environment, and therefore there were no closures. I believe that Scheme was the first language that got this right. Sussman and Steele original Lambda papers on the implementation of the Scheme make a great mind reading for anyone who wants to understand the scope, among many other things.

+9
source

Source: https://habr.com/ru/post/909741/


All Articles