From: david
Subject: input
Date: 
Message-ID: <d3823361-97bc-47d6-afb1-65215efd6924@r33g2000yqn.googlegroups.com>
hello!
i am thinking about ways to get user input.
i have written this method:

(defmethod get-cmd ((self player))
  (princ "> ")
  (setf (cmd self) (list (read) (read))))

to read in two word commands like go north, take object,
or kill monster. but i would also like to just type n to go
north for example. i think i can do this by
testing if one call to read equals a one word command
then calling read again if needed.
or use read-line and match strings to symbols somehow.
or input commands in parenthesis (n) or (go north) which
is easiest but i don't like it.

so my question: how would a competent lisp programmer
go about this task?
thanks,
david

From: Slobodan Blazeski
Subject: Re: input
Date: 
Message-ID: <68cd516b-0c25-4280-a9bc-6703a695d758@p11g2000yqe.googlegroups.com>
On Apr 21, 5:26 pm, david <······@gmail.com> wrote:
> hello!
> i am thinking about ways to get user input.
> i have written this method:
>
> (defmethod get-cmd ((self player))
>   (princ "> ")
>   (setf (cmd self) (list (read) (read))))
>
> to read in two word commands like go north, take object,
> or kill monster. but i would also like to just type n to go
> north for example. i think i can do this by
> testing if one call to read equals a one word command
> then calling read again if needed.
> or use read-line and match strings to symbols somehow.
> or input commands in parenthesis (n) or (go north) which
> is easiest but i don't like it.
>
> so my question: how would a competent lisp programmer
> go about this task?
> thanks,
> david
1st you're using setf, that already smells bad, in lisp USUALLY  you
return values instead of mutating state:
(defun get-cmd ()
   (princ "> ")
   (list (read) (read))))
(setf (cmd self) (get-cmd))
Or if you  insist
(defmethod get-cmd ((self player))
   (princ "> ")
   (setf (cmd self)
      (let ((res (read)))
        (if (one-word-command-p res)
           res
           (list res (read)))
         res)))

There is an excellent exercise, though not using CLOS in Gentle
introduction to symbolic computation on page 188 about moving Robot
around the room.
http://www.cs.cmu.edu/~dst/LispBook/index.html

cheers
bobi
http://tourdelisp.blogspot.com/
From: Paul Donnelly
Subject: Re: input
Date: 
Message-ID: <87k55et1q8.fsf@plap.localdomain>
david <······@gmail.com> writes:

> hello!
> i am thinking about ways to get user input.
> i have written this method:
>
> (defmethod get-cmd ((self player))
>   (princ "> ")
>   (setf (cmd self) (list (read) (read))))
>
> to read in two word commands like go north, take object,
> or kill monster. but i would also like to just type n to go
> north for example. i think i can do this by
> testing if one call to read equals a one word command
> then calling read again if needed.
> or use read-line and match strings to symbols somehow.
> or input commands in parenthesis (n) or (go north) which
> is easiest but i don't like it.
>
> so my question: how would a competent lisp programmer
> go about this task?

Probably read a line at a time and parse it (which you can do with
READ-FROM-STRING, if you like).
From: namekuseijin
Subject: Re: input
Date: 
Message-ID: <gsl3cc$1rkp$1@adenine.netfront.net>
david wrote:
> to read in two word commands like go north, take object,
> or kill monster.
> 
> so my question: how would a competent lisp programmer
> go about this task?

I'd try:
http://inform-fiction.org/I7/Welcome.html
or:
http://tads.org/

You know, domain specific tasks just lend themselves naturally to domain 
specific tools rather than general-purpose ones.

For instance, here's a short snippet of Inform7 code:

     The Cobble Crawl is a room. A wicker cage is here.

     The Debris Room is west of the Crawl. The black rod is here.

     Above the Debris Room is the Sloping E/W Canyon. West of the Canyon 
is the Orange River Chamber.

Except for full literary descriptions, this already compiles into a full 
game, with rooms than can be navigated, objects to be picked up or 
inspected and a very nice natural language subset parser at your disposal.

If you have doubts, you can join the newsgroups:
rec.arts.int-fiction
rec.games.int-fiction
From: david
Subject: Re: input
Date: 
Message-ID: <63b576d7-2523-433e-bb0f-e3e4d3630078@q9g2000yqc.googlegroups.com>
thanks everyone for your help.
i have done the robot exercise in
touretzky. i am not really interested
in writing interactive fiction, i just
want to do a fun programming exercise.
i probably will want to handle more
complicated input later.
so with read-line and read-from-string
i will need to tokenize the string
(if that is correct term) by testing
for spaces?
From: Thomas A. Russ
Subject: Re: input
Date: 
Message-ID: <ymieivlmr4b.fsf@blackcat.isi.edu>
david <······@gmail.com> writes:

> so with read-line and read-from-string
> i will need to tokenize the string
> (if that is correct term) by testing
> for spaces?

Not necessarily.  As long as you can restrict the input to well-formed
lisp expressions, you can just use READ-FROM-STRING to do the
tokenization for you.

Essentially what you end up doing is using a loop to transform an input
that would otherwise need to be written in lisp fashion like
  (whistle)
or
  (go north)
into
  whistle
  go north
and then effectively adding the parentheses around them.

Note that there are things that this simple parser will fail on, so you
also get to learn a bit about handling errors (HANDLER-CASE):

  go (north

since this will end up being an incomplete list because of the un-closed
parenthesis.


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: david
Subject: Re: input
Date: 
Message-ID: <c864f683-8c21-41b3-a5b4-cb23e7826536@x3g2000yqa.googlegroups.com>
On Apr 21, 8:09 pm, ····@sevak.isi.edu (Thomas A. Russ) wrote:
>
> Not necessarily.  As long as you can restrict the input to well-formed
> lisp expressions, you can just use READ-FROM-STRING to do the
> tokenization for you.
>
> Essentially what you end up doing is using a loop to transform an input
> that would otherwise need to be written in lisp fashion like
>   (whistle)
> or
>   (go north)
> into
>   whistle
>   go north
> and then effectively adding the parentheses around them.

sorry, i'm not following you at all. if i use read-line
i get a string like "go north" and read-from-string gives me
go. i don't know how to iterate over words in a sentence
except to check for spaces. i was trying to avoid having
to type (go north). what am i missing here?
thanks, david
From: ··················@gmail.com
Subject: Re: input
Date: 
Message-ID: <c53a11a8-da87-4217-9aae-39199c931ded@x3g2000yqa.googlegroups.com>
On Apr 22, 1:01 am, david <······@gmail.com> wrote:
> On Apr 21, 8:09 pm, ····@sevak.isi.edu (Thomas A. Russ) wrote:
>
>
>
> > Not necessarily.  As long as you can restrict the input to well-formed
> > lisp expressions, you can just use READ-FROM-STRING to do the
> > tokenization for you.
>
> > Essentially what you end up doing is using a loop to transform an input
> > that would otherwise need to be written in lisp fashion like
> >   (whistle)
> > or
> >   (go north)
> > into
> >   whistle
> >   go north
> > and then effectively adding the parentheses around them.
>
> sorry, i'm not following you at all. if i use read-line
> i get a string like "go north" and read-from-string gives me
> go. i don't know how to iterate over words in a sentence
> except to check for spaces. i was trying to avoid having
> to type (go north). what am i missing here?
> thanks, david

He is talking about doing something like:
(read-from-string (format nil "(~a)" "go north"))

produces:
(go north)
Which in turn gets evaluated.
You convert the string into a lisp expression, then read it.

Certainly a possibility if you want something simple and are just
playing around with lisp.
From: david
Subject: Re: input
Date: 
Message-ID: <a2916ff4-4d2a-4947-8fe8-cafa29baa7f9@t21g2000yqi.googlegroups.com>
On Apr 22, 1:21 am, ··················@gmail.com wrote:

> He is talking about doing something like:
> (read-from-string (format nil "(~a)" "go north"))
>
> produces:
> (go north)
> Which in turn gets evaluated.
> You convert the string into a lisp expression, then read it.
>
> Certainly a possibility if you want something simple and are just
> playing around with lisp.

thanks. it never occurred to me to use format like that.
i was going to write a function to do that.
-david
From: Rob Warnock
Subject: Re: input
Date: 
Message-ID: <W4OdnVOHf9-rVnPUnZ2dnUVZ_rOdnZ2d@speakeasy.net>
david  <······@gmail.com> wrote:
+---------------
| ··················@gmail.com wrote:
| > He is talking about doing something like:
| >   (read-from-string (format nil "(~a)" "go north"))
| > produces:
| >   (go north)
| > Which in turn gets evaluated.
| > You convert the string into a lisp expression, then read it.
...
| thanks. it never occurred to me to use format like that.
| i was going to write a function to do that.
+---------------

I tend to prefer this sort of style to that FORMAT hack:

    > (with-input-from-string (s "go north")
	(loop with eof = (list :eof)
	      for form = (read s nil eof)
	      until (eq form eof)
	  collect form))

    (GO NORTH)
    > 

It gives you an easier handle on the intermediate forms, so you
can do some special processing, if you like, before (or instead of)
calling EVAL, e.g., rewriting CL:GO [which is what the above "GO"
really is!!] into MY-APP::MY-GO or something.

By the way, you might find OPFR[1] useful for this sort of thing:

    $ opfr
    opfr> get-universal-time     ; Functions look like "commands"

    3449374524
    opfr> expt 2 100

    1267650600228229401496703205376
    opfr> list 1 2 :foo 4

    (1 2 :FOO 4)
    opfr> 37     ; Rewrites to avoid FUNCALLing single non-function values.

    37
    opfr> most-positive-fixnum   ; Ditto)

    536870911
    opfr> 


-Rob

[1] http://rpw3.org/hacks/lisp/opfr.lisp      The library.
    http://rpw3.org/hacks/lisp/opfr.cmucl     Wrapper script for CMUCL.
    http://rpw3.org/hacks/lisp/opfr.clisp     Wrapper script for CLISP.

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: david
Subject: Re: input
Date: 
Message-ID: <9aea36da-1279-4092-bb7e-720a97b3eea5@w40g2000yqd.googlegroups.com>
On Apr 22, 2:45 am, ····@rpw3.org (Rob Warnock) wrote:

> I tend to prefer this sort of style to that FORMAT hack:
>
>     > (with-input-from-string (s "go north")
>         (loop with eof = (list :eof)
>               for form = (read s nil eof)
>               until (eq form eof)
>           collect form))

thanks for your help. why do you make eof a keyword?
in (read s nil eof) i think nil is eof-error-p. what
is the purpose to set it to nil?
i think read knows eof is end-of-file by position so
(read s nil foo) would work also?
From: Thomas A. Russ
Subject: Re: input
Date: 
Message-ID: <ymiskk0ldre.fsf@blackcat.isi.edu>
david <······@gmail.com> writes:

> On Apr 22, 2:45 am, ····@rpw3.org (Rob Warnock) wrote:
> 
> > I tend to prefer this sort of style to that FORMAT hack:
> >
> >     > (with-input-from-string (s "go north")
> >         (loop with eof = (list :eof)
> >               for form = (read s nil eof)
> >               until (eq form eof)
> >           collect form))
> 
> thanks for your help. why do you make eof a keyword?
> in (read s nil eof) i think nil is eof-error-p. what
> is the purpose to set it to nil?

Correct.  The reason is so that READ doesn't signal an error for
attempting to read when there is no more data.  In order to provide your
own return value for end of file when using READ and friends, you have
to tell Lisp not to signal an error, but rather to use your own return
value.

See what happens if you use (read s t eof) instead. 

> i think read knows eof is end-of-file by position so
> (read s nil foo) would work also?

Well, it would -- assuming FOO had some definition.  The keyword is only
used inside the LIST which is bound to the EOF variable.  This is, of
course, a safer choice than using :EOF directly (as I did in my
solutions), although for more applications that won't matter.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Rob Warnock
Subject: Re: input
Date: 
Message-ID: <TOednVmJ-OwDWnLUnZ2dnUVZ_uednZ2d@speakeasy.net>
david  <······@gmail.com> wrote:
+---------------
| ····@rpw3.org (Rob Warnock) wrote:
| > I tend to prefer this sort of style to that FORMAT hack:
| >     > (with-input-from-string (s "go north")
| >         (loop with eof = (list :eof)
| >               for form = (read s nil eof)
| >               until (eq form eof)
| >           collect form))
| 
| thanks for your help. why do you make eof a keyword?
+---------------

EOF *isn't* "a keyword"; it's a freshly-consed list *containing*
a keyword. The "freshly-consed list" is the important part, since
that object *cannot* be EQ to any possible object the READ function
might return from reading your string. Writing "EOF = (LIST)" or
"EOF = (CONS NIL NIL)" [but the former is shorter!] would have
worked just as well, and then EOF would have contained "(NIL)".

But I find that burying some visible marker [such as the keyword :EOF]
in said freshly-consed list sometimes aids debugging. That is, down
inside some debugger breakpoint, when you run into "(:EOF)" you
immediately know where it came from, which isn't equally true of
"(NIL)". It costs almost nothing extra, since I almost always use
the keyword :EOF *somewhere* in a program, and thus it's already
been INTERNed.

+---------------
| in (read s nil eof) i think nil is eof-error-p. what
| is the purpose to set it to nil?
| i think read knows eof is end-of-file by position so
| (read s nil foo) would work also?
+---------------

Thomas Russ already addressed these two parts in his parallel reply,
but the purpose of setting EOF-ERROR-P is to allow you the programmer
to explicitly get a return value from reading to end-of-file instead
of causing the implementation to signal a condition.


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Mark Wooding
Subject: Re: input
Date: 
Message-ID: <878wlrtv9s.fsf.mdw@metalzone.distorted.org.uk>
····@rpw3.org (Rob Warnock) writes:

> Writing "EOF = (LIST)" or "EOF = (CONS NIL NIL)" [but the former is
> shorter!] would have worked just as well, and then EOF would have
> contained "(NIL)".

Umm.  (LIST) evaluates to NIL, which isn't particularly useful.
(LIST NIL) is still shorter than (CONS NIL NIL), though.

-- [mdw]
From: Pascal J. Bourguignon
Subject: Re: input
Date: 
Message-ID: <7c3abze5iq.fsf@pbourguignon.anevia.com>
Mark Wooding <···@distorted.org.uk> writes:

> ····@rpw3.org (Rob Warnock) writes:
>
>> Writing "EOF = (LIST)" or "EOF = (CONS NIL NIL)" [but the former is
>> shorter!] would have worked just as well, and then EOF would have
>> contained "(NIL)".
>
> Umm.  (LIST) evaluates to NIL, which isn't particularly useful.
> (LIST NIL) is still shorter than (CONS NIL NIL), though.

Well if shorter is what you want, notice that the reader won't be able
(unless you extend yourself extraordinarily, but the same could be
done with the value bound to eof) to read the stream itself, so you
could as well write:

(loop with eof = stream
      for item = (read stream nil eof)
      until (eq eof item)
      collect item)

-- 
__Pascal Bourguignon__
From: Rob Warnock
Subject: Re: input
Date: 
Message-ID: <LuOdncXcO5-nlGzUnZ2dnUVZ_vWdnZ2d@speakeasy.net>
Pascal J. Bourguignon <···@informatimago.com> wrote:
+---------------
| Mark Wooding <···@distorted.org.uk> writes:
| > ····@rpw3.org (Rob Warnock) writes:
| >> Writing "EOF = (LIST)" or "EOF = (CONS NIL NIL)" [but the former is
| >> shorter!] would have worked just as well, and then EOF would have
| >> contained "(NIL)".
| >
| > Umm.  (LIST) evaluates to NIL, which isn't particularly useful.
| > (LIST NIL) is still shorter than (CONS NIL NIL), though.
+---------------

*D'Oh!*  Of course Mark is correct here! I must have been having a
momentary brain fart when I said "(LIST) ==> (NIL)". Sorry 'bout that.
(LIST NIL) or (LIST :EOF) it is, then.

[One could always use the shorter (LIST 0) or (VECTOR), but that would
just be silly, not to mention very confusing to anyone reading your code.]

+---------------
| Well if shorter is what you want, notice that the reader won't be able
| (unless you extend yourself extraordinarily, but the same could be
| done with the value bound to eof) to read the stream itself, so you
| could as well write:
| 
| (loop with eof = stream
|       for item = (read stream nil eof)
|       until (eq eof item)
|       collect item)
+---------------

Nice try! And I almost fell for it, too!  ;-}  ;-}  Unfortunately,
STREAM here is only required to be an "input stream designator"
and not an actual stream, so if STREAM is being passed in from
some outer context it might well have the value T [denoting the
value of *TERMINAL-IO*] or NIL [denoting the value of *STANDARD-INPUT*],
and the chance of a T or NIL being in random sexp input is far
too high for this to be considered safe.

Probably the best thing for production code is:

    (defconstant *eof* (load-time-value (list :eof)))

but an inlined (LIST :EOF) will suffice for one-off & examples.


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Pascal J. Bourguignon
Subject: Re: input
Date: 
Message-ID: <87prf2d1em.fsf@galatea.local>
····@rpw3.org (Rob Warnock) writes:

> Pascal J. Bourguignon <···@informatimago.com> wrote:
> +---------------
> | Well if shorter is what you want, notice that the reader won't be able
> | (unless you extend yourself extraordinarily, but the same could be
> | done with the value bound to eof) to read the stream itself, so you
> | could as well write:
> | 
> | (loop with eof = stream
> |       for item = (read stream nil eof)
> |       until (eq eof item)
> |       collect item)
> +---------------
>
> Nice try! And I almost fell for it, too!  ;-}  ;-}  Unfortunately,
> STREAM here is only required to be an "input stream designator"
> and not an actual stream, so if STREAM is being passed in from
> some outer context it might well have the value T [denoting the
> value of *TERMINAL-IO*] or NIL [denoting the value of *STANDARD-INPUT*],
> and the chance of a T or NIL being in random sexp input is far
> too high for this to be considered safe.
>
> Probably the best thing for production code is:
>
>     (defconstant *eof* (load-time-value (list :eof)))
>
> but an inlined (LIST :EOF) will suffice for one-off & examples.

Oops, you are correct.

Usually that code snippet is embedded in a WITH-OPEN-FILE form so the
stream (AFAIK) is really a stream.  But in the general case, you're right.

-- 
__Pascal Bourguignon__
From: Thomas A. Russ
Subject: Re: input
Date: 
Message-ID: <ymiskjydmw7.fsf@blackcat.isi.edu>
···@informatimago.com (Pascal J. Bourguignon) writes:

> (loop with eof = stream
>       for item = (read stream nil eof)
>       until (eq eof item)
>       collect item)

And another variant that I've seen, that makes use of uninterned symbols
for uniqueness, with a name that aids debuggability:

 (loop with eof = #:EOF
       for item = (read stream nil eof)
       until (eq eof item)
       collect item)

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Rob Warnock
Subject: Re: input
Date: 
Message-ID: <LpmdnQLAt6wh-m_UnZ2dnUVZ_rednZ2d@speakeasy.net>
Thomas A. Russ <···@sevak.isi.edu> wrote:
+---------------
| And another variant that I've seen, that makes use of uninterned symbols
| for uniqueness, with a name that aids debuggability:
| 
|  (loop with eof = #:EOF
|        for item = (read stream nil eof)
|        until (eq eof item)
|        collect item)
+---------------

Don't forget to quote the #:EOF or you'll blow an UNBOUND-VARIABLE
exception...


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: ·····@sherbrookeconsulting.com
Subject: Re: input
Date: 
Message-ID: <eca5c476-e159-4cd7-82ee-441e5b8b323b@a7g2000yqk.googlegroups.com>
On Apr 22, 3:45 am, ····@rpw3.org (Rob Warnock) wrote:

> david  <······@gmail.com> wrote:
[...]
> | thanks. it never occurred to me to use format like that.
> | i was going to write a function to do that.
> +---------------

> I tend to prefer this sort of style to that FORMAT hack:

>     > (with-input-from-string (s "go north")
>         (loop with eof = (list :eof)
>               for form = (read s nil eof)
>               until (eq form eof)
>           collect form))

>     (GO NORTH)
>     >

> It gives you an easier handle on the intermediate forms, so you
> can do some special processing, if you like, before (or instead of)
> calling EVAL, e.g., rewriting CL:GO [which is what the above "GO"
> really is!!] into MY-APP::MY-GO or something.

This is true, but why do that when you can just pile on another
hack? :)

(defvar *if-commands* (find-package '#:if-commands))

(let ((*package* *if-commands*))
  (read-from-string (format nil "(~A)" "go north")))

I'm not really serious about holding on to the READ-FROM-STRING trick,
but I'm at least somewhat convinced that using EVAL and very minimual
rewriting is the way to go. I'm more than somewhat convinced that
whatever READing is actually done ought be done with *PACKAGE* bound
to the package just for interactive fiction commands regardless of
what other intermediate processing david decides to go with.

Cheers,
Pillsy
From: ··················@gmail.com
Subject: Re: input
Date: 
Message-ID: <d34d75e7-1da0-4a77-a583-30e092eefc19@j18g2000yql.googlegroups.com>
On Apr 22, 3:48 pm, ······@sherbrookeconsulting.com"
<·········@gmail.com> wrote:
> On Apr 22, 3:45 am, ····@rpw3.org (Rob Warnock) wrote:
>
>
>
>
>
> > david  <······@gmail.com> wrote:
> [...]
> > | thanks. it never occurred to me to use format like that.
> > | i was going to write a function to do that.
> > +---------------
> > I tend to prefer this sort of style to that FORMAT hack:
> >     > (with-input-from-string (s "go north")
> >         (loop with eof = (list :eof)
> >               for form = (read s nil eof)
> >               until (eq form eof)
> >           collect form))
> >     (GO NORTH)
> >     >
> > It gives you an easier handle on the intermediate forms, so you
> > can do some special processing, if you like, before (or instead of)
> > calling EVAL, e.g., rewriting CL:GO [which is what the above "GO"
> > really is!!] into MY-APP::MY-GO or something.
>
> This is true, but why do that when you can just pile on another
> hack? :)
>
> (defvar *if-commands* (find-package '#:if-commands))
>
> (let ((*package* *if-commands*))
>   (read-from-string (format nil "(~A)" "go north")))
>
> I'm not really serious about holding on to the READ-FROM-STRING trick,
> but I'm at least somewhat convinced that using EVAL and very minimual
> rewriting is the way to go. I'm more than somewhat convinced that
> whatever READing is actually done ought be done with *PACKAGE* bound
> to the package just for interactive fiction commands regardless of
> what other intermediate processing david decides to go with.
>
> Cheers,
> Pillsy

I'm fairly convinced than any sort of use of the 'read' or 'eval'
functionalities is going to amount to a hack. Its a neat hack, but a
hack nonetheless. When you are dealing with user inputs, it just isn't
very robust. Really using a package interactively amounts to using a
hash-table, you you might as well just use a hash-table from the
start.

What is going on ends up being more obviously defined, and you don't
have the baggage of circumventing the package system.

clever but not a good idea.
From: ·····@sherbrookeconsulting.com
Subject: Re: input
Date: 
Message-ID: <0501e7c6-8baf-48cc-8d62-5ad4f96d595e@y7g2000yqa.googlegroups.com>
On Apr 22, 9:37 pm, ··················@gmail.com wrote:
[...]
> I'm fairly convinced than any sort of use of the 'read' or 'eval'
> functionalities is going to amount to a hack. Its a neat hack, but a
> hack nonetheless.

david wants to write a command interpreter. I don't really think using
READ and EVAL to make a REPL is all that hacky, almost by definition.

> When you are dealing with user inputs, it just isn't very robust.

The lack of robustness doesn't seem to be a critical issue for this
application. It's an interpreter to let people play a game; depending
on the intended audience, letting people cheat by typing

    > cl::incf daves-internal-if-package::*player-score* (cl::expt 10
10)

may be more of a feature than a bug. Or it may not, and he may want to
revisit the decision when the program is closer to being done. Either
way, having that  capability during development is fantastically
useful.

> Really using a package interactively amounts to using a
> hash-table, you you might as well just use a hash-table from the
> start.

Except that the idea is that by using a package, david can have
functions, macros and variables that implement the commands he wants
to expose to the user, and he also can freely name them things like
"room" and "go" and "inspect". Eventually, if he doesn't like that
approach or it doesn't scale, he can still use the package as a hash
table, and why not?

Cheers,
Pillsy
From: Thomas A. Russ
Subject: Re: input
Date: 
Message-ID: <ymiws9cldzg.fsf@blackcat.isi.edu>
··················@gmail.com writes:

> On Apr 22, 1:01��am, david <······@gmail.com> wrote:
> > On Apr 21, 8:09��pm, ····@sevak.isi.edu (Thomas A. Russ) wrote:
> >
> >
> >
> > > Not necessarily. ��As long as you can restrict the input to well-formed
> > > lisp expressions, you can just use READ-FROM-STRING to do the
> > > tokenization for you.
> >
> > > Essentially what you end up doing is using a loop to transform an input
> > > that would otherwise need to be written in lisp fashion like
> > > �� (whistle)
> > > or
> > > �� (go north)
> > > into
> > > �� whistle
> > > �� go north
> > > and then effectively adding the parentheses around them.
> >
> > sorry, i'm not following you at all. if i use read-line
> > i get a string like "go north" and read-from-string gives me
> > go. i don't know how to iterate over words in a sentence
> > except to check for spaces. i was trying to avoid having
> > to type (go north). what am i missing here?
> > thanks, david
> 
> He is talking about doing something like:
> (read-from-string (format nil "(~a)" "go north"))

That works.

But I was thinking more about using a loop and just reading from the
string until it was exhausted.  I didn't think too carefully about
exactly how that would happen, and in fact, it would be perhaps better
to do that using WITH-INPUT-FROM-STRING instead.  But read-from-string
would work as well, just with a bit more machinery.

So:

(defun read-input-1 ()
  (with-input-from-string (s (read-line))
    (loop for item = (read s nil :eof)
          until (eq item :eof)
          collect item)))

(defun read-input-2 ()
  (loop with line = (read-line) 
        and item = nil
        and start = 0
        do (multiple-value-setq (item start) 
              (read-from-string line nil :eof :start start))
        when (eq item :eof) do (loop-finish)
        else collect item))

the second one is a bit more work, because we have to explicitly track
position inside the string, but it may be more efficient than setting up
the stream mechanism and object for each string.  Anyway, for the given
application of processing user input, I would go with the first
solution, since it is clearer and processing speed won't be an issue for
user-typed input.






-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: ··················@gmail.com
Subject: Re: input
Date: 
Message-ID: <68eddea2-12bb-4df1-8327-78445ea2c8f3@z5g2000yqn.googlegroups.com>
On Apr 21, 8:12 pm, david <······@gmail.com> wrote:
> thanks everyone for your help.
> i have done the robot exercise in
> touretzky. i am not really interested
> in writing interactive fiction, i just
> want to do a fun programming exercise.
> i probably will want to handle more
> complicated input later.
> so with read-line and read-from-string
> i will need to tokenize the string
> (if that is correct term) by testing
> for spaces?

I would use read-line, 'tokenize' and dispatch based on a 'recognized'
string.

You could do recognition based on the first thing in the string to
return a key in an equalp hash-table. (Or when tokenizing convert each
string keyword (and use eq(i believe?)), doesn't matter much for our
purposes as performance isn't important...)

Then you have that hash table return another function that gets takes
the rest of the tokenized string, sorts it out, (so to speak) and does
what you want with it.

Then to add an alias to a new function you'd just add it to the hash
table with its function.

For example for go north, you'd probably write a function 'go' that
takes one direction input. Go gets looked up in the hash and returns
the function, applies it to the second token.

For north (or n)  you'd take that go function, do an anonymous lambda
like this (lambda () (go :north)) or (lambda () (go "north")), and
store it in the hash table with the "N" or :|N| or :N key. (depending
on the definition of 'tokenize' i guess).

(Maybe when the function applies successfully return T)

Then anything that is unrecognized as a command (gethash returns 2
values, key,value; so anything that doesn't return a key), replies "I
don't understand". (you'd have to do an if statement to make sure you
don't apply a nil function, or just default the function to something
that prints "I don't understand" when applied to whatever).

Anything that returns a key but when applied returns nil replies "You
can't do that".

I don't particularly like using read as a wrong input can cause the
program to crash.
(Generally I will dispatch based on a split string or write my own
'reader' using 'read-char' and dispatching based on character...(the
second application being for streams/slurping files, not for this
exercise)).

Read/read-from-string is dangerous for anything but reading lisp code.
(Sometimes numbers).

have fun!
From: Pascal J. Bourguignon
Subject: Re: input
Date: 
Message-ID: <87ab69eve0.fsf@galatea.local>
david <······@gmail.com> writes:

> hello!
> i am thinking about ways to get user input.
> i have written this method:
>
> (defmethod get-cmd ((self player))
>   (princ "> ")
>   (setf (cmd self) (list (read) (read))))

User interaction should go thru the *QUERY-IO* stream, 
not the *STANDARD-INPUT* / *STANDARD-OUTPUT*.

(defmethod get-cmd ((self player))
   (princ "> " *query-io*)
   (setf (cmd self) (list (read *query-io*) (read *query-io*))))


> to read in two word commands like go north, take object,
> or kill monster. but i would also like to just type n to go
> north for example. i think i can do this by
> testing if one call to read equals a one word command
> then calling read again if needed.
> or use read-line and match strings to symbols somehow.
> or input commands in parenthesis (n) or (go north) which
> is easiest but i don't like it.
>
> so my question: how would a competent lisp programmer
> go about this task?

Since you may want to parse a more sophisticated language, reading
text and parsing it could be a good idea.

Otherwise, for a Q&D prototype, you can indeed just read symbols:

(defmethod get-cmd ((self player))
   (princ "> " *query-io*)
   (setf (cmd self) (let ((verb (read *query-io*)))
                      (cons verb (loop repeat (argument-count verb) collect (read *query-io*))))))


-- 
__Pascal Bourguignon__