; Sometimes programmer needs deeply nested loops over the same
; list of values. For example,
(dolist (i '(0 1))
(dolist (j '(0 1))
(dolist (k '(0 1))
(dolist (l '(0 1))
(println i j k l)))))
; For such, relatively rare, but still realistic situations, it
; might be useful to have "multi" version of the loop, and write
; something like:
;
;
; (dolist-multi ((i j k l) '(0 1))
; (println i j k l))
;
;
; Such a multi loop can be used even if all variables are known only
; during runtime, using letex (or wherex defined in the previous posts.)
;
;
; (letex ((L (random-sublist '(i j k l m n o p q r s t u v))))
; (dolist-multi (L '(0 1))
; (println= ... )))
;
;
; I'll use recursive definition:
;
; 1° Base
; --------
;
; (dolist-multi (() ___) (begin
; expr1 expr1
; ... <===> ... if n # 1
; exprn) exprn)
;
; expr1 in n = 1
;
; 2° Step
; --------
;
; (dolist-multi((v1 ... vn) ...) (dolist (v1 ...)
; expr1; (dolist-multi ((v2 ... vn) ...)
; ...; <===> expr1
; exprn); ...
; exprn))
;
; First one simple, but frequently needed function that transforms
; list of expressions into single expression by inserting "begin"
; in the list - but only if it is needed. If list has only one
; expression, then this expression is returned.
(set 'list-to-single-expression
(lambda(L)
(if (= (length L) 1)
(first L)
(cons 'begin L))))
(set 'dolist-multi
(lambda-macro(L)
(let ((variables (first L)))
(if (empty? variables)
(eval (list-to-single-expression (args)))
(letex ((head1 (cons (first variables) (rest L)))
(head2 (cons (rest variables) (rest L)))
(body (list-to-single-expression (args))))
(dolist head1
(dolist-multi head2
body)))))))
; Tests:
(dolist-multi(() (list 0 1))
(println 5))
(dolist-multi((i) (list 0 1))
(println "i = " i))
(dolist-multi((i j k) (list 0 1))
(println "i =" i ", j = " j ", k = " k))
; Appears to work.
; However, now, when I'm here - many newlisp loops, not only dolist
; have the syntax
;
;
; (<loop name> (<control variable> ...) <body>)
;
;
; For all of these, multi as defined here has a sense. So, it appears
; that defining multi-version of many loops is "low hanging fruit."
; It is also excelent example of the power of the Newlisp metaprogramming.
;
; I'll define the function multiloop that
;
; * accepts the name of the loop as argument,
; * generates new, multiloop macro, and
; * gives the appropriate name to it.
(set 'multiloop
(lambda(loop)
(let ((new-loop (sym (append (string loop) "-multi"))))
(set new-loop
(expand
(lambda-macro(L)
(let ((variables (first L)))
(if (empty? variables)
(eval (list-to-single-expression (args)))
(letex ((head1 (cons (first variables) (rest L)))
(head2 (cons (rest variables) (rest L)))
(body (list-to-single-expression (args))))
(loop head1
(new-loop head2
body))))))
'loop
'new-loop)))))
; Next, I'll apply multiloop on all Newlisp loops of the form
; (<loop name> (<control variable> ... ) <body>)
(map multiloop '(doargs dolist dostring dotimes dotree for))
; TEST
; Simple expression that contains two nested multiloops.
(for-multi ((i j) 0 8 4)
(dotimes-multi ((i j) 5) (print "*"))
(println " i= " i ", j=" j))
; RESULT:
; ************************* i= 0, j=0
; ************************* i= 0, j=4
; ************************* i= 0, j=8
; ************************* i= 4, j=0
; ************************* i= 4, j=4
; ************************* i= 4, j=8
; ************************* i= 8, j=0
; ************************* i= 8, j=4
; ************************* i= 8, j=8
(exit)
Multiple Loops.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment