From: Steven E. Harris
Subject: case and quoted keys - a misunderstanding
Date: 
Message-ID: <q673ch6fk8t.fsf@raytheon.com>
I was trying to write a simple stream scanning function like this,
intended to indicate whether it found the given character or EOF
first:

(defun read-to-char (c stream)
  (loop
    (case (read-char stream nil)
      (c (return t)))
      (nil (return))))

This doesn't work, because the case keys get quoted, and the nil entry
disappears entirely. Trying to track down the problem, I experimented
with this:

(let ((c #\A)
      (ch #\A))
  (case ch
    (c (format t "Found it!~%"))))

or, just to be sure, this:

(let* ((c #\A)
       (ch c))
  (case ch
    (c (format t "Found it!~%"))))


I had assumed that since 'c' is a bound symbol here, case would
actually evaluate 'c' to compare it against the evaluated
'ch'. Macroexpanding these examples shows that 'c' is getting quoted,
so the attempted character comparison fails.

Apparently I misunderstood how case works. Why does the key get quoted
rather than evaluated?


What to use instead, then? I was trying to avoid using an explicit
variable to catch the return value from read-char, since all I need to
do is compare it but not necessarily return it. case looked like the
only form that would allow multiple comparisons without naming the
variable under consideration.

For now I'm considering using one of these, not bothering to return t
instead of the non-nil matching character:

(defun read-to-char (c stream)
  (loop for ch = (read-char stream nil)
        when (or (null ch)
                 (char= ch c)) return ch))

(defun read-to-char (c stream)
  (loop for ch = (read-char stream nil)
        until (or (null ch)
                  (char= ch c))
        finally (return ch)))

or, forgoing loop:

(defun read-to-char (c stream)
  (do ((ch (read-char stream nil) (read-char stream nil)))
      ((or (null ch)
           (char= ch c)) ch)))

That last one forces me to duplicate the step form, and is hence less
desirable. Is there a better way, or do these look acceptable and
idiomatic?

-- 
Steven E. Harris        :: ········@raytheon.com
Raytheon                :: http://www.raytheon.com

From: Thomas A. Russ
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <ymiznjeruqi.fsf@sevak.isi.edu>
Steven E. Harris <········@raytheon.com> writes:

> I was trying to write a simple stream scanning function like this,
> intended to indicate whether it found the given character or EOF
> first:
> 
> (defun read-to-char (c stream)
>   (loop
>     (case (read-char stream nil)
>       (c (return t)))
>       (nil (return))))
> 
> This doesn't work, because the case keys get quoted, and the nil entry
> disappears entirely. Trying to track down the problem, I experimented
> with this:

Well, not exactly.  It isn't that case keys get quoted, but rather that
case keys can be either a key or a list of keys.  NIL is therefore
interpreted as a list of no keys.  To match against NIL (or T, since
that is also treated specially by case) you need to enclose it in its
own parentheses.  Try the following instead:

(defun read-to-char (c stream)
   (loop
     (case (read-char stream nil)
       (c (return t)))
       ((nil) (return))))




For more fun, you can try the following test function:

(defun test-case (item)
  (case item
    (a  'a)
    ((b) 'b)
    ((0 1 2 3 4 5 6 7 8 9) 'digit)
    (nil 'nil-entry)   ; ** Will never be chosen
    ((nil) 'other-nil-entry)
    ((t)  't-entry)
    (t 'default-entry)))

USER> (test-case t)
T-ENTRY
USER> (test-case 'foo)
DEFAULT-ENTRY
USER> (test-case nil)
OTHER-NIL-ENTRY
USER> (test-case 'c)
DEFAULT-ENTRY
USER> (test-case 3)
DIGIT



-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Nils Goesche
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <lyr84pjm2c.fsf@cartan.de>
···@sevak.isi.edu (Thomas A. Russ) writes:

> It isn't that case keys get quoted,

The point is that they are not evaluated but literal objects.

> but rather that case keys can be either a key or a list of keys.
> NIL is therefore interpreted as a list of no keys.  To match against
> NIL (or T, since that is also treated specially by case)

or OTHERWISE

> you need to enclose it in its own parentheses.  Try the following
> instead:
> 
> (defun read-to-char (c stream)
>    (loop
>      (case (read-char stream nil)
>        (c (return t)))
>        ((nil) (return))))

This (hopefully) gives you an ``unused variable�� warning :-)

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x0655CFA0
From: Steven E. Harris
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <q674r1ldrnk.fsf@raytheon.com>
Nils Goesche <······@cartan.de> writes:

>> Try the following instead:
>> 
>> (defun read-to-char (c stream)
>>    (loop
>>      (case (read-char stream nil)
>>        (c (return t)))
>>        ((nil) (return))))
>
> This (hopefully) gives you an ``unused variable�� warning :-)

Right, because the 'c' in the first case key does not correspond to
the function argument 'c', but rather matches only the symbol 'c'. At
least that's what I found. Now that I know, I'll stop trying to do
that.

-- 
Steven E. Harris        :: ········@raytheon.com
Raytheon                :: http://www.raytheon.com
From: Thomas A. Russ
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <ymiy8yxrt13.fsf@sevak.isi.edu>
Nils Goesche <······@cartan.de> writes:

> > (defun read-to-char (c stream)
> >    (loop
> >      (case (read-char stream nil)
> >        (c (return t)))
> >        ((nil) (return))))
> 
> This (hopefully) gives you an ``unused variable�� warning :-)

Oops.

That's the trouble with the one-fault assumption in diagnosis :)

At least I tested the test function I posted.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Justin Dubs
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <2e262238.0307161624.2f99ec0e@posting.google.com>
I prefer recursion to looping if I have the choice, so how about:

(defun read-to-char (c stream)
  (if (char= c (read-char stream nil))
    t
    (read-to-char c stream)))

It's tail recursive, so any decent compiler will turn it into a loop.

It's even prettier if you don't care about the return value:

(defun read-to-char (c stream)
  (unless (char= c (read-char stream nil))
    (read-to-char c stream)))

Good luck with your project.

Justin Dubs
From: Steven E. Harris
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <q67y8yxcct7.fsf@raytheon.com>
······@eos.ncsu.edu (Justin Dubs) writes:

> It's tail recursive, so any decent compiler will turn it into a
> loop.

That would be nice. How can I tell if my compiler (CLISP) would do so?

I have seen these kind of justifications in C++ programming ("Oh, of
course the compiler just inlines that," or, "That variable will
obviously be eliminated.")  that turn out to be false. We often assume
our compilers are more capable and aggressive than they really are.

-- 
Steven E. Harris        :: ········@raytheon.com
Raytheon                :: http://www.raytheon.com
From: Christophe Rhodes
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <sqlluxdqzj.fsf@lambda.jcn.srcf.net>
Steven E. Harris <········@raytheon.com> writes:

> ······@eos.ncsu.edu (Justin Dubs) writes:
>
>> It's tail recursive, so any decent compiler will turn it into a
>> loop.
>
> That would be nice. How can I tell if my compiler (CLISP) would do so?

The DISASSEMBLE function usually sheds some light.

> I have seen these kind of justifications in C++ programming ("Oh, of
> course the compiler just inlines that," or, "That variable will
> obviously be eliminated.")  that turn out to be false. We often assume
> our compilers are more capable and aggressive than they really are.

The Sufficiently Smart Compiler is just at the end of that rainbow!

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: Steven E. Harris
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <q67lluxcbp6.fsf@raytheon.com>
Christophe Rhodes <·····@cam.ac.uk> writes:

> The DISASSEMBLE function usually sheds some light.

Let's take a look. First, my final-as-of-yesterday version:

(defun read-to-char (c stream)
  (loop for x = (read-char stream nil)
        when (or (null x)
                 (char= x c)) return x))

,----[ loop version - CLISP 2.30 ]
| > (disassemble #'read-to-char)
| 
| Disassembly of function READ-TO-CHAR
| 2 required arguments
| 0 optional arguments
| No rest parameter
| No keyword parameters
| 13 byte-code instructions:
| 0     (NIL&PUSH)
| 1     L1
| 1     (LOAD&PUSH 2)
| 2     (NIL&PUSH)
| 3     (PUSH-UNBOUND 2)
| 5     (CALLS1&STORE 119 0)                ; READ-CHAR
| 8     (JMPIFNOT L16)
| 10    (LOAD&PUSH 0)
| 11    (LOAD&PUSH 4)
| 12    (CALLSR&JMPIFNOT 1 7 L1)            ; CHAR=
| 16    L16
| 16    (POP)
| 17    (SKIP&RET 3)
| #<COMPILED-CLOSURE READ-TO-CHAR>
`----


Now, the recursive one:

(defun read-to-char (c stream)
  (let ((x (read-char stream nil)))
    (cond ((null x)         nil)
          ((char= x c)      t)
          (t                (read-to-char c stream)))))

,----[ recursive version - CLISP 2.30 ]
| > (disassemble #'read-to-char)
| 
| Disassembly of function READ-TO-CHAR
| 2 required arguments
| 0 optional arguments
| No rest parameter
| No keyword parameters
| 18 byte-code instructions:
| 0     L0
| 0     (LOAD&PUSH 1)
| 1     (NIL&PUSH)
| 2     (PUSH-UNBOUND 2)
| 4     (CALLS1&PUSH 119)                   ; READ-CHAR
| 6     (LOAD&JMPIFNOT 0 L21)
| 9     (LOAD&PUSH 0)
| 10    (LOAD&PUSH 4)
| 11    (CALLSR&JMPIF 1 7 L24)              ; CHAR=
| 15    (LOAD&PUSH 3)
| 16    (LOAD&PUSH 3)
| 17    (JMPTAIL 2 6 L0)
| 21    L21
| 21    (NIL)
| 22    (SKIP&RET 4)
| 24    L24
| 24    (T)
| 25    (SKIP&RET 4)
| #<COMPILED-CLOSURE READ-TO-CHAR>
`----


There are more instructions produced, partly for the separate jump
targets for the explicit return nil v. t, and perhaps as preparation
for the JMPTAIL call. I have no idea whether these differences would
amount to any difference in execution speed.

-- 
Steven E. Harris        :: ········@raytheon.com
Raytheon                :: http://www.raytheon.com
From: Christophe Rhodes
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <sqhe5ldpfq.fsf@lambda.jcn.srcf.net>
Steven E. Harris <········@raytheon.com> writes:

> Christophe Rhodes <·····@cam.ac.uk> writes:
>
>> The DISASSEMBLE function usually sheds some light.
>
> Let's take a look. [...]
> 
> There are more instructions produced, partly for the separate jump
> targets for the explicit return nil v. t, and perhaps as preparation
> for the JMPTAIL call. I have no idea whether these differences would
> amount to any difference in execution speed.

Nor do I; but note that tail recursion elimination isn't generally a
speed optimization, but a space optimization.  (Speaking loosely;
there are those here who understand this in far greater depth than I).

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: Frode Vatvedt Fjeld
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <2h1xwpv6n8.fsf@vserver.cs.uit.no>
······@eos.ncsu.edu (Justin Dubs) writes:

> I prefer recursion to looping if I have the choice, so how about:
>
> (defun read-to-char (c stream)
>   (if (char= c (read-char stream nil))
>     t
>     (read-to-char c stream)))

I guess it's a statement about the unpleasentness of using recursion
for iteration that this example doesn't work at all when EOF is
reached..

-- 
Frode Vatvedt Fjeld
From: Frode Vatvedt Fjeld
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <2hwuehtrg5.fsf@vserver.cs.uit.no>
······@eos.ncsu.edu (Justin Dubs) writes:

> It's even prettier if you don't care about the return value:
>
> (defun read-to-char (c stream)
>   (unless (char= c (read-char stream nil))
>     (read-to-char c stream)))

Actually, in this case I think you can eat the cake too (that is,
return t when c is found):

  (defun read-to-char-or-spin-forever (c stream)
    (or (char= c (read-char stream nil))
        (read-to-char-or-spin-forever c stream)))

If you really don't care about the return value, you can get it kind
of right like this:

  (defun read-to-char-or-eof (c stream)
    (unless (char= c (read-char stream nil c))
      (read-to-char-or-eof c stream)))

-- 
Frode Vatvedt Fjeld
From: Justin Dubs
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <2e262238.0307170810.29fa199d@posting.google.com>
Frode Vatvedt Fjeld <······@cs.uit.no> wrote in message news:<··············@vserver.cs.uit.no>...
> ······@eos.ncsu.edu (Justin Dubs) writes:
> 
> > It's even prettier if you don't care about the return value:
> >
> > (defun read-to-char (c stream)
> >   (unless (char= c (read-char stream nil))
> >     (read-to-char c stream)))
> 
> Actually, in this case I think you can eat the cake too (that is,
> return t when c is found):
> 
>   (defun read-to-char-or-spin-forever (c stream)
>     (or (char= c (read-char stream nil))
>         (read-to-char-or-spin-forever c stream)))
> 
> If you really don't care about the return value, you can get it kind
> of right like this:
> 
>   (defun read-to-char-or-eof (c stream)
>     (unless (char= c (read-char stream nil c))
>       (read-to-char-or-eof c stream)))

Haha.  Thanks for catching that.  I just wrote that so quick that I
didn't stop and consider hitting EOF first.  How embarrasing.  :-)

Here's the obvious recursive interpretation which respects both
hitting EOF first and returning the right value.

(defun read-to-char (c stream)
  (let ((x (read-char stream nil)))
    (cond ((null x)         nil)
          ((char= x c)      t)
          (t                (read-to-char c stream)))))

This isn't much worse looking then the obvious iterative solutions. 
Oh, and nice catch with the s/unless/or/ in my recursive example.

Justin Dubs
From: Frode Vatvedt Fjeld
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <2hsmp5t7th.fsf@vserver.cs.uit.no>
······@eos.ncsu.edu (Justin Dubs) writes:

> (defun read-to-char (c stream)
>   (let ((x (read-char stream nil)))
>     (cond ((null x)         nil)
>           ((char= x c)      t)
>           (t                (read-to-char c stream)))))
>
> This isn't much worse looking then the obvious iterative solutions.

..only it has Scheme written all over it ;-) In Common Lisp this is
spelled as

  (defun read-to-char (c stream)
    (loop for x = (read-char stream nil)
       while x do (when (char= x c) (return t))))

So there, I think we have now listed almost every possible
implementation of this function. Take your pick.

-- 
Frode Vatvedt Fjeld
From: Steven E. Harris
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <q67u19lccgx.fsf@raytheon.com>
Frode Vatvedt Fjeld <······@cs.uit.no> writes:

> (defun read-to-char (c stream)
>   (loop for x = (read-char stream nil)
>      while x do (when (char= x c) (return t))))

This one returns nil when the 'while' test fails? Is it returning nil
because of a lack of an explicit return call, or is it returning x
which happens to be nil? I consulted the HyperSpec� and I read it to
confirm the former.


Footnotes: 
� http://www.lispworks.com/reference/HyperSpec/Body/06_ad.htm

-- 
Steven E. Harris        :: ········@raytheon.com
Raytheon                :: http://www.raytheon.com
From: Wade Humeniuk
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <%LARa.17917$xn5.2285063@news0.telusplanet.net>
"Frode Vatvedt Fjeld" <······@cs.uit.no> wrote in message ···················@vserver.cs.uit.no...
> 
>   (defun read-to-char (c stream)
>     (loop for x = (read-char stream nil)
>        while x do (when (char= x c) (return t))))
> 
> So there, I think we have now listed almost every possible
> implementation of this function. Take your pick.


(defun read-to-char (c stream)
  (when (peek-char c stream nil nil)
    (read-char stream)))

CL-USER 8 > (with-input-from-string (string "hello")
              (read-to-char #\y string))
NIL

CL-USER 9 > (with-input-from-string (string "hello")
              (read-to-char #\e string))
#\e

CL-USER 10 > 

Wade
From: Rob Warnock
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <9sucnYYoE-29LoqiXTWc-g@speakeasy.net>
Wade Humeniuk <····@nospam.nowhere> wrote:
+---------------
| "Frode Vatvedt Fjeld" <······@cs.uit.no>:
| >   (defun read-to-char (c stream)
| >     (loop for x = (read-char stream nil)
| >        while x do (when (char= x c) (return t))))
| > 
| > So there, I think we have now listed almost every possible
| > implementation of this function. Take your pick.
| 
| (defun read-to-char (c stream)
|   (when (peek-char c stream nil nil)
|     (read-char stream)))
+---------------

Yup! That's what I finally settled on as well for a "#!"-ignoring
reader macro ("#!" starts Unix shell scripts):

	(set-dispatch-macro-character #\# #\!
	  (lambda (s c p)
	    (declare (ignore c p))
	    (peek-char #\newline s t nil t) ; doesn't cons like read-line
	    (read-char s t nil t)
	    (values)))

[Hmmm... Didn't we just have a thread about occasionally reimplementing
advanced features because you don't remember them all?  ;-}  ;-}  ]


-Rob

-----
Rob Warnock, PP-ASEL-IA		<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Steven M. Haflich
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <3F16DAF8.8020903@alum.mit.edu>
Frode Vatvedt Fjeld wrote:

> ..only it has Scheme written all over it ;-) In Common Lisp this is
> spelled as
> 
>   (defun read-to-char (c stream)
>     (loop for x = (read-char stream nil)
>        while x do (when (char= x c) (return t))))

How verbose!!  The function can be expressed much more concisely as:

     (defun read-to-char (c stream)
       (loop for x = (read-char stream nil)
          while x when (char= x c) return t))

or if one doesn't mind the possibility of an extra return value, as:

     (defun read-to-char (c stream)
       (ignore-errors (loop when (eql (read-char stream) c) return it))

or finally, the following elegantly terse version that needs _neither_
explicit recursion nor iteration:

     (defun read-to-char (c stream)
       (ignore-errors (peek-char c stream) (read-char stream) t))

:-)
From: Edi Weitz
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <873ch5qbko.fsf@bird.agharta.de>
"Steven M. Haflich" <·················@alum.mit.edu> writes:

>      (defun read-to-char (c stream)
>        (ignore-errors (loop when (eql (read-char stream) c) return it))

Hmm, I seem to remember having seen this 'RETURN IT' idiom together
with LOOP somewhere but I must have forgotten. I scanned the CLHS and
found two places where it is mentioned - in the dictionary where the
syntax of LOOP is described and in the notes (6.1.9) where there's a
warning not to use the variable IT in connection with LOOP. Is there
any place where the behaviour of 'RETURN IT' is defined?

Thanks,
Edi.
From: Peter Seibel
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <m3r84p6mx8.fsf@javamonkey.com>
Edi Weitz <···@agharta.de> writes:

> "Steven M. Haflich" <·················@alum.mit.edu> writes:
> 
> >      (defun read-to-char (c stream)
> >        (ignore-errors (loop when (eql (read-char stream) c) return it))
> 
> Hmm, I seem to remember having seen this 'RETURN IT' idiom together
> with LOOP somewhere but I must have forgotten. I scanned the CLHS and
> found two places where it is mentioned - in the dictionary where the
> syntax of LOOP is described and in the notes (6.1.9) where there's a
> warning not to use the variable IT in connection with LOOP. Is there
> any place where the behaviour of 'RETURN IT' is defined?

From _6.1.6 Conditional Execution Clauses_:

  The loop keyword it can be used to refer to the result of the test
  expression in a clause. Use the loop keyword it in place of the form
  in a return clause or an accumulation clause that is inside a
  conditional execution clause. If multiple clauses are connected with
  and, the it construct must be in the first clause in the block.

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Edi Weitz
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <87u19loue1.fsf@bird.agharta.de>
Peter Seibel <·····@javamonkey.com> writes:

> From _6.1.6 Conditional Execution Clauses_:
> 
>   The loop keyword it can be used to refer to the result of the test
>   expression in a clause. Use the loop keyword it in place of the
>   form in a return clause or an accumulation clause that is inside a
>   conditional execution clause. If multiple clauses are connected
>   with and, the it construct must be in the first clause in the
>   block.

I _swear_ I looked there half an hour ago and it wasn't there. Really!

:)

Thanks,
Edi.
From: Joe Marshall
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <n0fchoju.fsf@ccs.neu.edu>
Edi Weitz <···@agharta.de> writes:

> Peter Seibel <·····@javamonkey.com> writes:
> 
> > From _6.1.6 Conditional Execution Clauses_:
> > 
> >   The loop keyword it can be used to refer to the result of the test
> >   expression in a clause. Use the loop keyword it in place of the
> >   form in a return clause or an accumulation clause that is inside a
> >   conditional execution clause. If multiple clauses are connected
> >   with and, the it construct must be in the first clause in the
> >   block.
> 
> I _swear_ I looked there half an hour ago and it wasn't there. Really!

It probably wasn't.  LOOP clauses seem to appear spontaneously despite
the efforts to cull them every few years.
From: Barry Margolin
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <zngRa.220$0z4.172@news.level3.com>
In article <···············@raytheon.com>,
Steven E. Harris  <········@raytheon.com> wrote:
>Apparently I misunderstood how case works. Why does the key get quoted
>rather than evaluated?

This makes it less verbose to provide a list of keys, e.g.

(case ch
  ((a b c) ...)
  ((d e f) ...)
  ...)

If the keys were evaluated, you'd have to write:

(case ch
  (('a 'b 'c) ...)
  (('d 'e 'f) ...)
  ...)

This would also make a singleton key be treated differently from a list of
keys.  You couldn't write:

(case ch
  ('a ...))

because this expands to:

(case ch
  ((quote a) ...))

which would try to evaluate QUOTE and A.

Many other languages that have similar constructs also treat the labels as
literals.  For instance, see C's switch statement.

>What to use instead, then? I was trying to avoid using an explicit
>variable to catch the return value from read-char, since all I need to
>do is compare it but not necessarily return it. case looked like the
>only form that would allow multiple comparisons without naming the
>variable under consideration.

You could easily write your own case-like macro that evaluates the keys.

>For now I'm considering using one of these, not bothering to return t
>instead of the non-nil matching character:

All of these look like pretty common idioms.

>(defun read-to-char (c stream)
>  (loop for ch = (read-char stream nil)
>        when (or (null ch)
>                 (char= ch c)) return ch))
>
>(defun read-to-char (c stream)
>  (loop for ch = (read-char stream nil)
>        until (or (null ch)
>                  (char= ch c))
>        finally (return ch)))
>
>or, forgoing loop:
>
>(defun read-to-char (c stream)
>  (do ((ch (read-char stream nil) (read-char stream nil)))
>      ((or (null ch)
>           (char= ch c)) ch)))
>
>That last one forces me to duplicate the step form, and is hence less
>desirable. Is there a better way, or do these look acceptable and
>idiomatic?
>
>-- 
>Steven E. Harris        :: ········@raytheon.com
>Raytheon                :: http://www.raytheon.com


-- 
Barry Margolin, ··············@level3.com
Level(3), Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Frode Vatvedt Fjeld
Subject: Re: case and quoted keys - a misunderstanding
Date: 
Message-ID: <2hadbeuz6h.fsf@vserver.cs.uit.no>
Steven E. Harris <········@raytheon.com> writes:

> (defun read-to-char (c stream)
>   (loop
>     (case (read-char stream nil)
>       (c (return t)))
>       (nil (return))))

Note that this case is (modulo the paren mismatch; do learn to use a
paren-enabled editor) the same as

  (case (read-char stream nil)
    (c (return t))
    (() (return)))

That is, it's not nil, it's "the empty set of values". To test against
nil in a case statement, do it like this:

  (case (read-char stream nil)
    (c (return t)) ; thest against the symbol named c
    ((nil) (return)))

> Apparently I misunderstood how case works. Why does the key get
> quoted rather than evaluated?

Because that's what case does. It's all about matching one (evaluated)
value against several known-at-compile-time values.

> What to use instead, then? I was trying to avoid using an explicit
> variable to catch the return value from read-char, since all I need
> to do is compare it but not necessarily return it.

So what? Just use a variable.

-- 
Frode Vatvedt Fjeld