Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ;; Deriving something like generators, but I didn't really feel like doing that exactly.
- ;; This applys a function across a range from 0 to x.
- (define (apply-to-range f x)
- (let loop ((i 0))
- (if (< i x)
- (begin
- (f i)
- (loop (+ 1 i))))))
- ;; (apply-to-range display 10) => #unspecified, but prints 0123456789
- ;; At each step, it essentially does this.
- ;; Pass it a new value for i and apply the function `f'
- (define (apply-to-range f x)
- (lambda (i)
- (if (< i x)
- (f i))))
- ;; Essentially, here, we've separated the core logic from the
- ;; iteration. Big woop.
- ;; Let's keep out the iteration but add the state back.
- (define (apply-to-range f x)
- (lambda (i)
- (lambda ()
- (if (< i x)
- (begin
- (f i)
- (set! i (+ 1 i)))))))
- ;; Now, we get to say:
- ;; (define counter ((apply-to-range display 10) 1) ; 1 represents the starting value.
- ;; (counter) => #unspecified, but it outputs 1.
- ;; (counter) => #unspecified, but it outputs 2.
- ;; ...
- ;; We can create a factory for this.
- (define (apply-to-range-factory f i x)
- ((apply-to-range x) 1))
- ;; but, now we've got an extra function,
- ;; and we've completely lost the spirit of the original intention,
- ;; which the first one showed. (i.e. the structure of the code is gone)
- ;; What if we could automatically replace the running function with a new
- ;; one right before we return at each iteration?
- ;; how do we break out of the loop early?
- (define (apply-to-range f x)
- (call-with-current-continuation
- (lambda (bail)
- (let loop ((i 0))
- (if (< i x)
- (begin
- (f i)
- (bail)
- (loop (+ 1 i)))))))
- ;; calling `bail' puts us here
- )
- ;; So, now, we didn't end up looping and instead bailed out after
- ;; calling the `f' once.
- ;; What if we were able to save our spot where we exited and come
- ;; back to it later?
- ;; The `bail' function represented the exit point of the function
- ;; `apply-to-range', can we represent the point in the program
- ;; where `bail' was called?
- ;; Sure! But, let's go one step further. Let's REPLACE the
- ;; `main-logic' function with that new frame of reference.
- (define (apply-to-range f x)
- (define (main-logic exit-function)
- (let loop ((i 0))
- (if (< i x)
- (begin
- (f i)
- (call-with-current-continuation
- (lambda (new-bail)
- (set! main-logic new-bail)
- (exit-function)))
- (display "continuing...")
- (newline)
- (loop (+ 1 i))))))
- (lambda ()
- (call-with-current-continuation
- (lambda (exit-function)
- (main-logic exit-function)))))
- ;; (define counter (apply-to-range display 10))
- ;; (counter) => #unspecified, but it outputs 0
- ;; (counter) => #unspecified, but it outputs "continuing...\n1"
- ;; (counter) => #unspecified, but it outputs "continuing...\n2"
- ;; (counter) => #unspecified, but it outputs "continuing...\n3"
- ;; (counter) => #unspecified, but it outputs "continuing...\n4"
- ;; So, the state is there, we can temporarily exit, and when we
- ;; continue go right back to where we left off... but it's ugly!
- ;; Let's give the save point a nice name, call it 'suspend'
- (define (apply-to-range f x)
- (define (main-logic exit-function)
- (letrec ((suspend (lambda ()
- (call-with-current-continuation
- (lambda (new-bail)
- (set! main-logic new-bail)
- (exit-function))))))
- (let loop ((i 0))
- (if (< i x)
- (begin
- (f i)
- (suspend)
- (display "continuing...")
- (newline)
- (loop (+ 1 i)))))))
- (lambda ()
- (call-with-current-continuation
- (lambda (exit-function)
- (main-logic exit-function)))))
- ;; Looks a bit nicer, still works the same way.
- ;; (define counter (apply-to-range display 10))
- ;; (counter) => #unspecified, but it outputs 0
- ;; (counter) => #unspecified, but it outputs "continuing...\n1"
- ;; (counter) => #unspecified, but it outputs "continuing...\n2"
- ;; (counter) => #unspecified, but it outputs "continuing...\n3"
- ;; (counter) => #unspecified, but it outputs "continuing...\n4"
- ;; But damn, that's a lot of code to get the desired effect...
- ;; We've created a sort of framework, which we can "template"
- ;; in the form of a macro
- (define-syntax def-generator
- (syntax-rules ()
- ((_ (name arg1 ...) suspend-name body1 ...)
- (define (name arg1 ...)
- (define (main-logic exit-function)
- (letrec ((suspend-name (lambda ()
- (call-with-current-continuation
- (lambda (new-bail)
- (set! main-logic new-bail)
- (exit-function))))))
- (let name ((arg1 arg1) ...)
- body1 ...)))
- (lambda ()
- (call-with-current-continuation
- (lambda (exit-function)
- (main-logic exit-function))))))))
- (def-generator (apply-to-range f i x) suspend
- (if (< i x)
- (begin
- (f i)
- (suspend)
- (display "continuing...")
- (newline)
- (apply-to-range f (+ i 1) x))))
- ;; Looks a bit nicer, still works the same way.
- ;; (define counter (apply-to-range display 1 10))
- ;; (counter) => #unspecified, but it outputs 0
- ;; (counter) => #unspecified, but it outputs "continuing...\n1"
- ;; (counter) => #unspecified, but it outputs "continuing...\n2"
- ;; (counter) => #unspecified, but it outputs "continuing...\n3"
- ;; (counter) => #unspecified, but it outputs "continuing...\n4"
Add Comment
Please, Sign In to add comment