From: Kevin Ryde <user42@zip.com.au>
Subject: Re: while break and continue
Date: Thu, 14 Aug 2003 07:49:30 +1000 [thread overview]
Message-ID: <87znid1b4l.fsf@zip.com.au> (raw)
In-Reply-To: 8765lmkwpb.fsf@zip.com.au
[-- Attachment #1: Type: text/plain, Size: 1594 bytes --]
I applied this
* boot-9.scm (while): Rewrite, continue as a proper escape, break
doesn't take a return value, break and continue procs new for each
while form allowing use in nested whiles, don't depend on bindings in
expansion environment.
* tests/syntax.test (while): New tests.
* scheme-control.texi (while do): Update `while' for code rewrite, in
particular describe break and continue.
- syntax: while cond body ...
Run a loop executing the BODY forms while COND is true. COND is
tested at the start of each iteration, so if it's `#f' the first
time then BODY is not executed at all. The return value is
unspecified.
Within `while', two extra bindings are provided, they can be used
from both COND and BODY.
- Scheme Procedure: break
Break out of the `while' form.
- Scheme Procedure: continue
Abandon the current iteration, go back to the start and test
COND again, etc.
Each `while' form gets its own `break' and `continue' procedures,
operating on that `while'. This means when loops are nested the
outer `break' can be used to escape all the way out. For example,
(while (test1)
(let ((outer-break break))
(while (test2)
(if (something)
(outer-break #f))
...)))
Note that each `break' and `continue' procedure can only be used
within the dynamic extent of its `while'. Outside the `while'
their behaviour is unspecified.
[-- Attachment #2: boot-9.scm.while.diff --]
[-- Type: text/plain, Size: 1252 bytes --]
--- boot-9.scm.~1.316.~ 2003-05-30 08:40:07.000000000 +1000
+++ boot-9.scm 2003-08-14 07:38:41.000000000 +1000
@@ -2491,18 +2491,6 @@
(loop (1- count) (cons count result)))))
\f
-;;; {While}
-;;;
-;;; with `continue' and `break'.
-;;;
-
-(defmacro while (cond . body)
- `(letrec ((continue (lambda () (or (not ,cond) (begin (begin ,@ body) (continue)))))
- (break (lambda val (apply throw 'break val))))
- (catch 'break
- (lambda () (continue))
- (lambda v (cadr v)))))
-
;;; {collect}
;;;
;;; Similar to `begin' but returns a list of the results of all constituent
@@ -2560,6 +2548,26 @@
(else
(error "define-syntax-macro can only be used at the top level")))))
+;;; {While}
+;;;
+;;; with `continue' and `break'.
+;;;
+
+;; The inner `do' loop avoids re-establishing a catch every iteration,
+;; that's only necessary if continue is actually used.
+;;
+(define-macro (while cond . body)
+ (let ((key (make-symbol "while-key")))
+ `(,do ((break ,(lambda () (throw key #t)))
+ (continue ,(lambda () (throw key #f))))
+ ((,catch (,quote ,key)
+ (,lambda ()
+ (,do ()
+ ((,not ,cond))
+ ,@body)
+ #t)
+ ,(lambda (key arg) arg))))))
+
\f
;;; {Module System Macros}
;;;
[-- Attachment #3: syntax.test.while.diff --]
[-- Type: text/plain, Size: 3788 bytes --]
--- syntax.test.~1.10.~ 2003-04-28 07:51:30.000000000 +1000
+++ syntax.test 2003-08-12 22:30:55.000000000 +1000
@@ -550,3 +550,171 @@
exception:missing/extra-expr
(eval '(quote a b)
(interaction-environment)))))
+
+(with-test-prefix "while"
+
+ (define (unreachable)
+ (error "unreachable code has been reached!"))
+
+ ;; an environment with no bindings at all
+ (define empty-environment
+ (make-module 1))
+
+ ;; Return a new procedure COND which when called (COND) will return #t the
+ ;; first N times, then #f, then any further call is an error. N=0 is
+ ;; allowed, in which case #f is returned by the first call.
+ (define (make-iterations-cond n)
+ (lambda ()
+ (cond ((not n)
+ (error "oops, condition re-tested after giving false"))
+ ((= 0 n)
+ (set! n #f)
+ #f)
+ (else
+ (set! n (1- n))
+ #t))))
+
+
+ (pass-if-exception "too few args" exception:wrong-num-args
+ (while))
+
+ (with-test-prefix "empty body"
+ (do ((n 0 (1+ n)))
+ ((> n 5))
+ (pass-if n
+ (let ((cond (make-iterations-cond n)))
+ (while (cond)))
+ #t)))
+
+ (pass-if "initially false"
+ (while #f
+ (unreachable))
+ #t)
+
+ (with-test-prefix "in empty environment"
+
+ (pass-if "empty body"
+ (eval `(,while #f)
+ empty-environment)
+ #t)
+
+ (pass-if "initially false"
+ (eval `(,while #f
+ #f)
+ empty-environment)
+ #t)
+
+ (pass-if "iterating"
+ (let ((cond (make-iterations-cond 3)))
+ (eval `(,while (,cond)
+ 123 456)
+ empty-environment))
+ #t))
+
+ (with-test-prefix "iterations"
+ (do ((n 0 (1+ n)))
+ ((> n 5))
+ (pass-if n
+ (let ((cond (make-iterations-cond n))
+ (i 0))
+ (while (cond)
+ (set! i (1+ i)))
+ (= i n)))))
+
+ (with-test-prefix "break"
+
+ (pass-if-exception "too many args" exception:wrong-num-args
+ (while #t
+ (break 1)))
+
+ (with-test-prefix "from cond"
+ (pass-if "first"
+ (while (begin
+ (break)
+ (unreachable))
+ (unreachable))
+ #t)
+
+ (do ((n 0 (1+ n)))
+ ((> n 5))
+ (pass-if n
+ (let ((cond (make-iterations-cond n))
+ (i 0))
+ (while (if (cond)
+ #t
+ (begin
+ (break)
+ (unreachable)))
+ (set! i (1+ i)))
+ (= i n)))))
+
+ (with-test-prefix "from body"
+ (pass-if "first"
+ (while #t
+ (break)
+ (unreachable))
+ #t)
+
+ (do ((n 0 (1+ n)))
+ ((> n 5))
+ (pass-if n
+ (let ((cond (make-iterations-cond n))
+ (i 0))
+ (while #t
+ (if (not (cond))
+ (begin
+ (break)
+ (unreachable)))
+ (set! i (1+ i)))
+ (= i n)))))
+
+ (pass-if "from nested"
+ (while #t
+ (let ((outer-break break))
+ (while #t
+ (outer-break)
+ (unreachable)))
+ (unreachable))
+ #t))
+
+ (with-test-prefix "continue"
+
+ (pass-if-exception "too many args" exception:wrong-num-args
+ (while #t
+ (continue 1)))
+
+ (with-test-prefix "from cond"
+ (do ((n 0 (1+ n)))
+ ((> n 5))
+ (pass-if n
+ (let ((cond (make-iterations-cond n))
+ (i 0))
+ (while (if (cond)
+ (begin
+ (set! i (1+ i))
+ (continue)
+ (unreachable))
+ #f)
+ (unreachable))
+ (= i n)))))
+
+ (with-test-prefix "from body"
+ (do ((n 0 (1+ n)))
+ ((> n 5))
+ (pass-if n
+ (let ((cond (make-iterations-cond n))
+ (i 0))
+ (while (cond)
+ (set! i (1+ i))
+ (continue)
+ (unreachable))
+ (= i n)))))
+
+ (pass-if "from nested"
+ (let ((cond (make-iterations-cond 3)))
+ (while (cond)
+ (let ((outer-continue continue))
+ (while #t
+ (outer-continue)
+ (unreachable)))))
+ #t)))
[-- Attachment #4: Type: text/plain, Size: 142 bytes --]
_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-devel
next prev parent reply other threads:[~2003-08-13 21:49 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2003-05-30 0:00 while break and continue Kevin Ryde
2003-06-01 23:58 ` Marius Vollmer
2003-06-05 1:42 ` Kevin Ryde
2003-06-18 22:56 ` Marius Vollmer
2003-06-21 23:28 ` Kevin Ryde
2003-07-27 14:48 ` Marius Vollmer
2003-07-29 0:23 ` Kevin Ryde
2003-08-13 21:49 ` Kevin Ryde [this message]
2003-08-13 9:27 ` Matthias Koeppe
2003-08-14 23:35 ` Kevin Ryde
2003-08-15 1:43 ` Kevin Ryde
2003-06-06 22:31 ` Kevin Ryde
2003-06-18 22:57 ` Marius Vollmer
2003-06-20 23:56 ` Kevin Ryde
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/guile/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87znid1b4l.fsf@zip.com.au \
--to=user42@zip.com.au \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).