Chapter 6. Interacting with the World: Reading and Printing in Lisp

So far, we haven’t written any code that directly interacts with the outside world. Instead, all the results generated by commands are just returned as values, which we can see by calling functions from our Lisp REPL.

However, code can’t just spend its whole life sitting in a black box. At some point, it’s going to need to interact with the world, so it will need a user interface. Luckily, Lisp has much to offer to help you create user interfaces. There are many graphical user interface libraries for different flavors of Common Lisp, as well as libraries for building web interfaces. In fact, we’ll be building our own toy web interface in Chapter 13.

In this chapter, we’ll focus on the most basic of all user interfaces, the command-line interface.

image with no caption

Printing and Reading Text

For a command-line interface, we need commands that can directly print text from the screen and read in text entered by the user. The two commands that do this are, appropriately enough, print and read. As you might expect by now, there is a lot of symmetry between these two commands.

Printing to the Screen

The print function simply lets you print stuff to the console:

> (print "foo")

 "foo"
 "foo"

Don’t get confused by the fact that calling the print function caused "foo" to be printed twice. The first "foo" is what the print function actually printed. The second "foo" is there because, as you know, the REPL always prints the value of any expression that is entered. It so happens that the value of (print "foo") is "foo", causing the word to be shown twice. In the examples that follow in this chapter, I’ll typically omit this extra final value printed by the REPL, just to avoid confusion.

The print function is an easy way to print a Lisp value to the screen. However, advanced Lispers often favor a related function called prin1. To understand the difference, let’s try both of these functions out in the REPL:

> (progn (print "this")
         (print "is")
         (print "a")
         (print "test"))

"this"
"is"
"a"
"test"

The print function causes each item to be printed on a separate line. Now, let’s try prin1:

> (progn (prin1 "this")
         (prin1 "is")
         (prin1 "a")
         (prin1 "test"))
"this""is""a""test"

As you can see, prin1 does not put the printed items on separate lines. To be precise, the print and prin1 commands are the same in every way, except that print will start a new line before printing a value. Additionally, print places a space character at the end of the printed value.

Because prin1 does less, it is really a simpler, more fundamental function. It is more flexible and, therefore, is commonly used in more serious Lisp code. We’ll use the print function more frequently in this book, but you should be aware of the prin1 command, as well.

Saying Hello to the User

The following example is a simple function, say-hello, that you can call from your Lisp prompt. It asks users for their name and responds with a greeting. When you run the program, be sure to type quotes around your name, even if this may seem odd.

> (defun say-hello ()
      (print "Please type your name:")
      (let ((name (read)))
          (print "Nice to meet you, ")
          (print name)))
  SAY-HELLO.
  > (say-hello)
  "Please type your name:" "bob"
  "Nice to meet you,"
  "bob"

In the first line of the say-hello function, we print a message asking users for their name . Next, we define a local variable called name, which is set to the value returned by the read function . The read function will cause Lisp to wait for the user to type in something at the REPL. Only after the user has typed something in at the prompt and pressed enter will the variable name be set to the result. Once we know the user’s name, a personalized message is printed, greeting the user .

As you can see from this simple function, the print and read functions do (almost) exactly what you would expect. The print function prints something on the screen. The read function lets the user enter something into the program. However, there is one glaring idiosyncrasy in these functions: Every value displayed and entered is surrounded by quotation marks.

Starting with print and read

When you need to print something on the screen, you should first think of the print command. If you need to read something in, you should first think of the read command. Other printing commands let you create the previous example without having superfluous quotes, but whenever you have an input or output task in Lisp, you should ask yourself, “Can print or read do the job?” You will save yourself a lot of trouble if you always use these two functions as your starting point.

Warning

The read command can be dangerous if used in the wrong way. See The Dangers of read and eval in The Dangers of read and eval for details.

The print and read functions think about values with the mind of a computer, not the mind of a human. A computer loves having strings of text surrounded by quotes. It doesn’t have a human brain, and consequently, it can’t understand what we mean when we feed it raw textual information. However, if a text fragment is surrounded by quotes, even a dumb old computer can figure out that the value we’re handing it is probably a string of text.

The print and read commands actually take this philosophy to the extreme. Almost any conceivable type of data in Lisp (with the exception of actual functions and some advanced data structures) can be printed and read using these commands, without the slightest bit of loss along the way. You can probably already imagine some scenarios where this feature would be immensely valuable, such as writing some hairy and huge piece of data to a file, and loading it in again at a later date.

As a simple example, the following code has exactly the same design as the previous function, but amazingly, it can read and print a number instead of a string. Notice how the program prints and reads numbers without the use of quotes, since Lisp knows when something is a number just by seeing the number in its raw form.

> (defun add-five ()
      (print "please enter a number:")
      (let ((num (read)))
          (print "When I add five I get")
          (print (+ num 5))))
ADD-FIVE
> (add-five)
"please enter a number:" 4
"When I add five I get"
9

Let’s look at some more examples of what happens when we use print to write out values.

(print '3)     => 3      An integer
(print '3.4)   => 3.4    A float
(print 'foo)   => FOO
    A symbol. It may be printed in all caps, since Common
                          Lisp symbols are blind to letter case.
(print '"foo") => "foo"  A string
(print '#a)   => #a    A character

These examples are all really boring, since print pretty much just prints out exactly what we put in. Note that we put an explicit quote on the front of each value. It could be omitted and would be implicit in all cases but the symbol name, since a symbol can also refer to functions.

The last example shows how literal characters are entered in Lisp. To create a Lisp character, just place the # symbols in front of the actual character. Lisp also has special literals defined for nonvisible characters. The most important for everyday use are # ewline, # ab, and #space.

A table of output from the read function would look just as boring as this table for print, in the same symmetrical way.

Note

In the examples above I stated that Common Lisp symbols are blind to letter case. While this is true for most strings, it is in fact possible to create case-sensitive symbols by surrounding the symbol with the vertical pipe |. So the symbol |CaseSensitiveSymbol| will retain its case. Symbols surrounded by vertical pipes can even contain punctuation. Hence |even this is a legal Lisp symbol!|

Reading and Printing Stuff the Way Humans Like It

Of course, our initial little say-hello function does a pretty awful job of greeting people, even if it has some interesting properties. It would be much better if we had more functions that could make it friendlier for humans. Actually, we can create a (very symmetrical) little table that summarizes what we would like:

image with no caption

As you can see, Lisp has a command that can print pieces of data in a way that is appealing to humans. The princ function can take any piece of Lisp data, and it tries to print that data in a way humans would prefer. It will do the basic things you might expect: leave off the quotes on a string, print characters in their raw form, and so on. Here are some examples:

(princ '3)     => 3
(princ '3.4)   => 3.4
(princ 'foo)   => FOO
(princ '"foo") => foo
(princ '#a)   => a

Here’s an example of princing a character that has a special meaning:

> (progn (princ "This sentence will be interrupted")
         (princ #
ewline)
         (princ "by an annoying newline character."))
This sentence will be interrupted
by an annoying newline character.

By its nature, princ could be used to print any arbitrary output of characters you want. This is fundamentally different from print. As we’ve discussed, the cool thing about the print command is that it prints objects in such a way that they can always be “read” back into their internal representation. However, this means print can’t be used to generate any arbitrary bit of text. On the other hand, princ can be used to print anything you want.

Therefore, although princ can print stuff in a way that humans prefer, it’s a one-way street. Once we’ve printed things with princ, only a humanlike intelligence could decipher how to change things back into a meaningful, appropriate Lisp data structure. Since computers are too stupid to do this right now, it means our beloved symmetry has been broken.

Of course, we could always cheat and come up with some arbitrary rules for how the computer should interpret what the human enters. An obvious way to do this would be to say to the computer, “Just let the users type in whatever they want until they hit the enter key, then treat the whole thing as a string.” The function that does this in Common Lisp is called read-line. However, it has none of the sophistication of the read, print, and princ functions, since it knows about nothing beyond characters and strings.

With this new knowledge, we can finally go full circle and create a proper function for greeting someone, without ugly quotes or other oddities:

> (defun say-hello ()
      (princ "Please type your name:")
      (let ((name (read-line)))
          (princ "Nice to meet you, ")
          (princ name)))
  SAY-HELLO
  > (say-hello)
  Please type your name: Bob O'Malley
  Nice to meet you, Bob O'Malley

This version of the say-hello function is similar to our first version. However, when the computer asks users for their name , it now does so without printing quotes around the text string. The same holds true to when we print the greeting . Also, users can now enter in any name (including a name with spaces and quotes), since the read-line command captures and returns all the text entered until the enter key is pressed, without any fuss.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.252.204