Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getting standard input and storing it as a string in lisp

I realize this is probably a really stupid question but i have no idea why this isnt working and i pretty much gave up. basically i tried:

(setq answer (string (read)))

and

(setq answer 0)
(format answer "~s" (read))

and

(setq answer (read))

when i try to evaluate

(if (stringp answer) 
    (princ "works")
    (princ "failed"))

on any of the above tries it always comes out failed.

what am i doing wrong?

like image 980
MBU Avatar asked Mar 29 '11 05:03

MBU


2 Answers

Or you could just do:

(setq answer (read-line))

That gives you a string right there.

[1]> (setq answer (read))
3
3
[2]> (type-of answer)
(INTEGER 0 16777215)
[3]> (setq answer (read-line))
3
"3"
[4]> (type-of answer)
(SIMPLE-BASE-STRING 1)
[5]>
like image 197
Bill Evans at Mariposa Avatar answered Sep 28 '22 20:09

Bill Evans at Mariposa


Start a fresh REPL, then try checking the return value of each of your steps:

T1> (read)
foo
FOO
T1> (read)
1
1
T1> (type-of (read))
foo
SYMBOL
T1> (type-of (read))
1
BIT

Now note, that STRING won't work on all input types:

T1> (string 'foo)
"FOO"
T1> (string 1)

Also note that, unlike setq, (format foo ...) won't set foo, but write to it, if it's a stream or a string with a fill-pointer. Take a look at its Documentation, and you'll see:

format destination control-string &rest args => result

[...]

destination---nil, t, a stream, or a string with a fill pointer.

[...]

format is useful for producing nicely formatted text, producing good-looking messages, and so on. format can generate and return a string or output to destination.

If destination is a string, a stream, or t, then the result is nil. Otherwise, the result is a string containing the `output.'

Try it like this:

T1> (setq *answer*
          (with-output-to-string (s)
            (format s "~s" (read))))
1
"1"
T1> *answer*
"1"

Or like this:

T1> (setq *answer* (make-array 20 :element-type 'character :fill-pointer 0))
""
T1> (format *answer* "~s" (read))
1
NIL
T1> *answer*
"1" 

Those are the only relevant errors I could find in your code. This definitely returns "works" in every conforming CL (you could also use prin1-to-string):

T1> (defvar *answer*)
*ANSWER*
T1> (setq *answer* (format nil "~s" (read)))
1
"1"
T1> (if (stringp *answer*)
        (princ "works")
        (princ "failed"))
works
"works"

Unless you are in a messed-up package (try (in-package cl-user) before evaluating your code), or redefined basic functionality, this will work. Give some more exact error descriptions, if it still won't.

ED:

Having read Bill's answer, who correctly pointed at read-line as a shorter solution, I should maybe mention that I didn't try to show the best, most succinct, or most idiomatic approaches in my answer, and that those will vary depending on what you are really trying to do. (The shortest possible solution would have been "works" :) They are just examples that should help explaining why your code failed.

Another thing I forgot to say is that you should keep in mind, that toplevel setqs on variables not defined with defvar or defparameter are generally to be avoided in anything but dabbling at the REPL, since the consequences are undefined. Also, those variables are, by convention, wrapped in asterisks (also called earmuffs) in order to prevent bugs caused by confusing specials with lexically scoped variables.

like image 29
danlei Avatar answered Sep 28 '22 19:09

danlei