TinyScheme Loops: Imperative & Functional with Tail Recursion using "while" vs "do"

I don't see a lot of documentation on TinyScheme in particular and some comments seem less than reliable about it.  So far the best resource I found was the manual.txt file on GitHub but this is a brief overview really.

From what I can tell despite some comments advising not to use it, Tail Recursion is apparently implemented in TinyScheme at least within GIMP 2.8's script-fu plug-in.  The optimisation required is to avoid exceeding the stack depth with repeated recursion, instead tail-recursion can be compiled as a jump.

The "library" utilities (see C:\Program Files\GIMP 2\share\gimp\2.0\scripts\script-fu.init .. 24k text of scripts to extend TinyScheme core to cover more of the standard) defines "do" as a macro.  I pasted a copy at the very end.

A traditional loop can be done explicitly too with "while" and a "mutable" value, and it might be the "best" way for you .. from my short experiment I don't see a lot of difference in performance.

both of these have 10-11 seconds runtime on my Sony Vaio Core i5 laptop.

IMPERATIVE STYLE

(print (time))(let ((x 0) (y 0)) (while (< x 200000) (set! y (+ y x)) (set! x (+ x 1))) (print y)) (print (time))

FUNCTIONAL STYLE
(print (time))(letrec ((myfunc (lambda (x y) (if (< x 200000) (myfunc (+ x 1) (+ y x)) (print y))))) (myfunc 0 0))(print (time))


There are other tools that are more similar to iterators than loops, applying functions to members of a list such as: foldr ,  map and apply.

Anyway writing this kind of functional loop can be done which looks more like the imperative style.
Introducing "do":

Scheme syntax : (do ((var init inc) ...) (endtest result ...) body ...)

Simple loop : (do ((x 0 (+ x 1))) ((> x 9) (print "the end")) (print x))

More complex example with two-dimensions:

(do
    (
        (x 0 (+ x (if (= y x) 1 0)))
        (y 0 (if (< y x) (+ y 1) 0))
    )
    (
        (> x 6)
    )
    (display x)
    (print y)
)

The above "do" macro is transformed into this tail-recursive function implementation for execution ..


> (macro-expand '(do ((x 0 (+ x (if (= y x) 1 0))) (y 0 (if (< y x) (+ y 1) 0))) ((> x 6)) (display x) (print y)))

(letrec
    (
        (myfunc
            (lambda (x y)
                (if (> x 6)
                    (begin)
                    (begin
                        (display x)
                        (print y)
                        (myfunc (+ x (if (= y x) 1 0)) (if (< y x) (+ y 1) 0))
                    )
                )
            )
        )
    )
    (myfunc 0 0)
)


Which outputs this sequence .. y counting up to x and then x incrementing


00
10
11
20
21
22
30
31
32
33
40
41
42
43
44
50
51
52
53
54
55
60
61
62
63
64
65
66
()

MACRO DEFINITION FROM script-fu.init

;;;; (do ((var init inc) ...) (endtest result ...) body ...)
;;
(macro do
  (lambda (do-macro)
    (apply (lambda (do vars endtest . body)
             (let ((do-loop (gensym)))
               `(letrec ((,do-loop
                           (lambda ,(map (lambda (x)
                                           (if (pair? x) (car x) x))
                                      `,vars)
                             (if ,(car endtest)
                               (begin ,@(cdr endtest))
                               (begin
                                 ,@body
                                 (,do-loop
                                   ,@(map (lambda (x)
                                            (cond
                                              ((not (pair? x)) x)
                                              ((< (length x) 3) (car x))
                                              (else (car (cdr (cdr x))))))
                                       `,vars)))))))
                  (,do-loop
                    ,@(map (lambda (x)
                             (if (and (pair? x) (cdr x))
                               (car (cdr x))
                               '()))
                        `,vars)))))
      do-macro)))


Comments

Popular Posts