On Thu, Apr 20, 2017 at 10:04 PM, Lawrence Bottorff <borg...@gmail.com>
wrote:

> Looking at HTDP-1 (http://htdp.org/2003-09-26/Bo
> ok/curriculum-Z-H-25.html#node_chap_19), I see this:
>
> (define (filter predicate alon)
>   (cond
>    [(empty? alon) empty]
>    [else (cond
>           [(predicate (first alon))
>            (cons (first alon)
>                  (filter predicate (rest alon)))]
>           [else
>            (filter predicate (rest alon))])]))
>
> Then to find if a member of a list l is below a number i
>
> (define (below i l)
>   (local ((define (below-i x)
>             (> i x)))
>     (filter below-i l)))
>
> As the proverbial beginner -- who doesn't totally understand closures --
> this looks like a use of the closure idea. Right? The i is "magically"
> known in `filter`, which seems pretty closure-ish to me. Or am I totally
> wrong?
>
> LB
>

Yep, you nailed it.

To expand a bit, and please forgive me if this is stuff you already know:

Lexical Scope:
Different variables are accessible at different parts of your program; the
term for this is 'scope'.  There are a couple of kinds of scope but the
easiest is lexical scope, which is based on the written structure of the
program -- things at a deeper level of nesting are at a deeper scope.

Closure:
When a function is created it 'closes over' the currently visible scope.
(Hence the term, 'closure'.)  That means that it has access to the
variables that were defined at the time the closure was created, even if
those variable go out of scope and are no longer directly usable by the
rest of the program.

NB:
Note that *variables* are not the same as *values*.  A variable, e.g. 'x',
is a container for a value, e.g. 7.  There can be multiple variables named
x at any given time, all of them holding different values.  That means that
the closure may see a different 'x' than the program immediately around the
closure.  Those two variables might happen to have the same value but
probably do not.

Racket tends to say 'identifier' instead of 'variable' and calls it
'binding' a value instead of 'assigning' a value, but the concepts are
close enough for government work and if you're new to Racket then you are
likely to find the other terms more familiar.

Here's some example code that will hopefully make it all clear.  Try to
predict what the output will be before you run it.

#lang racket

;    'println' et al don't play nicely with printing multiple
;    arguments at once, so create a convenience function that does.
(define (say . args)
  (println (apply ~a args)))


;;    The actual interesting part of the program starts here.

(define x 'x)
(say "first print: " x)

; At this point remember that we are defining a function, not running it
(define (baz)
  (define w 'w)
  (say "top of baz, w: " w) ; "2: w"

  (define (foo)
    (define y 'y)
    (say "top of foo, w: " w)
    (say "top of foo, x: " x)
    (say "top of foo, y: " y)

    ;    Return a closure.  This closure will have access to the
    ;    scope of foo (including the variable y) even when the
    ;    point of execution is no longer inside foo and y is
    ;    therefore not available.
    (lambda () (say "thunk inside of foo, y: " y))
    )
  (say "after foo")
  ;    The variable y that was defined in foo is not visible outside
  ;    of foo.  If you try to print it you will be unable to execute
  ;    this file because the Racket compiler won't know what to do
  ;    with the unbound variable y.
  ; (say "This won't work: " y) ; BOOM!

  (define (bar)
    (define z 'z)
    (say "top of bar, w: " w)
    (say "top of bar, x: " x)
    (say "top of bar, z: " z)

    ;    Return a closure, as above.
    (lambda () (say "thunk returned from bar, z: " z))
    )
  (say "after bar")
  (say "inside baz, after declaration of foo and bar, x: " x) ; prints 'x
  (say "inside baz, after declaration of foo and bar, w: " w) ; prints 'w
  (set! x 'new-x)
  (say "inside baz, after set! x, x: " x) ; prints 'new-x because we
changed the value of x with set!
  (say "inside baz, after set! x, w: " w) ; still prints 'w

  ;    Execute the foo function.  The 'say' inside of foo will print
  ;    'new-x because when foo was defined it closed over the
  ;    original variable x and we have now set!'d the value of the
  ;    original x.  foo returns a thunk, which is what the say
  ;    itself will print.
  (say "inside baz, after set! x, (foo) returns a thunk: " (foo))

  ; Execute the thunk that is returned by foo.
  (say "inside baz, after set! x, result of thunk returned by (foo): "
((foo)))


  ; Create a new variable named w with the same value as the
  ; existing w.  This is called "shadowing" a variable -- it's
  ; basically a way to take a temporary copy that exists only within
  ; the scope of the 'let'.   The copy is the 'local version'.
  (let ((w w))
    (say "inside 'let', local w: " w) ; prints 'w, the variable of the
local version of w which has the same value as the original w
    (set! w 'new-w) ; This affects the local copy
    (say "inside 'let', after set! w, local w: " w) ; prints 'new-w, the
variable of the local version of w
    (let ((w w)) ; how deep does the rabbit hole go?
      ; Prints 'new-w, the variable of the local version of w which
      ; has the same value as the w visible in the enclosing scope
      ; (which is the outer 'let')
      (say "inside inner 'let', local w: " w)
      (set! w 'new-new-w) ; This affects the local copy here in the inner
let
      (say "inside inner 'let', after set! w, local w: " w) ; prints
'new-new-w, the variable of the local version of w
      ); end the inner let, restoring the scope of the outer let
    (say "at the bottom of the outer 'let', w: " w) ; prints 'new-w
    ) ; end the 'let', restoring the original scope
  (say "after outer let, w: " w) ; prints 'w, the unchanged value of the
original w

  (say "(bar) returns a thunk: " (bar)) ; The 'say' inside 'bar' will print
'new-x. Returns a thunk, which gets printed

  ; bar will say 'new-x, then return a thunk.  We will execute the
  ; thunk which will say 'z, the value of the variable named z which
  ; was defined inside of bar and is therefore visible to the thunk
  ; that was also defined inside of bar.
  (say "result of thunk returned by (bar): "  ((bar)))

  ;     Try to say z directly and you won't be able to compile this file
  ;     because 'z' is not known at this level
  ; (say z)  ; BOOM!

  ;;    An interesting edge case that isn't directly related to what
  ;;    we're talking about: The compiler will notice when you
  ;;    'define' a variable and will assume that all of the uses of
  ;;    that variable at that scope and deeper static scopes should
  ;;    refer to the one you are defining, even if there is another
  ;;    one at a higher scope that would be perfectly usable.
  ;;    Therefore, if you uncomment the following line this program
  ;;    will not compile because the compiler will think that the x in
  ;;    (say "top of foo, x: " x) above should be referencing this x
  ;;    which, of course, isn't defined because we haven't gotten to
  ;;    it yet but we're going to real soon now honest.  If that
  ;;    didn't make sense, just ignore it.
  ;;
  ; (define x 'a-new-x)

  ) ; closes baz
(baz)  ; run the function.  All the things described in the comments happen.
(say "last print: " x) ; Inside of baz we set!'d x, meaning we changed the
value

;;    END EXECUTION.  Everything from here down is comments.
;;================

;;  The output of the program is below, but try to predict it yourself
;;  before reading.
;;
;;
;;
;; "first print: x"
;; "top of baz, w: w"
;; "after foo"
;; "after bar"
;; "inside baz, after declaration of foo and bar, x: x"
;; "inside baz, after declaration of foo and bar, w: w"
;; "inside baz, after set! x, x: new-x"
;; "inside baz, after set! x, w: w"
;; "top of foo, w: w"
;; "top of foo, x: new-x"
;; "top of foo, y: y"
;; "inside baz, after set! x, (foo) returns a thunk:
#<procedure:...cer/app/test.rkt:26:4>"
;; "top of foo, w: w"
;; "top of foo, x: new-x"
;; "top of foo, y: y"
;; "thunk inside of foo, y: y"
;; "inside baz, after set! x, result of thunk returned by (foo): #<void>"
;; "inside 'let', local w: w"
;; "inside 'let', after set! w, local w: new-w"
;; "inside inner 'let', local w: new-w"
;; "inside inner 'let', after set! w, local w: new-new-w"
;; "at the bottom of the outer 'let', w: new-w"
;; "after outer let, w: w"
;; "top of bar, w: w"
;; "top of bar, x: new-x"
;; "top of bar, z: z"
;; "(bar) returns a thunk: #<procedure:...cer/app/test.rkt:42:4>"
;; "top of bar, w: w"
;; "top of bar, x: new-x"
;; "top of bar, z: z"
;; "thunk returned from bar, z: z"
;; "result of thunk returned by (bar): #<void>"
;; "last print: new-x"

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to