Input and output under program control

In the programs we've written so far this semester, we've relied on the Chez Scheme interactive interface to collect the input. We've assumed that the users of our programs know enough about Scheme to formulate a call to at least one of the procedures that we've defined.

Unfortunately, this simplifying assumption doesn't always hold. In many cases, we'd like our program to take over the job of interacting with users, reading in values and writing out computed results without the assistance of the interactive interface.

To support programs of this kind, Scheme provides several procedures that perform interactive input or output. We have already encountered two of them: display and newline. The others are:

Here's a small illustration of the use of the read procedure. The square-root-computer procedure asks the user to supply a number, computes the square root of the number that the user supplies, and prints out the result, appropriately labelled, without using the Chez Scheme interactive interface.

(define square-root-computer
  (lambda ()
    (display "Give me a number, and I'll compute its square root.")
    (newline)
    (let ((proposed-number (begin
                             (display "Number: ")
                             (read))))
      (if (not (number? proposed-number))
          (error 'square-root-computer
                 "To compute a square root, I need a number"))
      (display "The square root of ")
      (display proposed-number)
      (display " is ")
      (display (sqrt proposed-number))
      (display ".")
      (newline))))

Exercise 1

Create a file square-root-computer.ss containing the preceding Scheme procedure, followed by a call to that procedure. Save the file. Start Scheme in a dtterm window by typing in the following command line:

scheme square-root-computer.ss

This runs the program. Use it to find the square root of 303/25.


Exercise 2

Write a Scheme procedure that collects two numbers from the user and prints out their sum.


If one wants the procedure to compute many square roots instead of just one, prompting the user each time for a new number, one can set up a recursion in which the completion of each exchange initiates another:

(define square-root-computer
  (lambda ()
    (display "Give me as many numbers as you like.")
    (newline)
    (display "I'll compute its square root of each one.")
    (newline)
    (let loop ((proposed-number (begin
                                  (display "Number: ")
                                  (read))))
      (if (not (number? proposed-number))
          (error 'square-root-computer
                 "To compute a square root, I need a number"))
      (display "The square root of ")
      (display proposed-number)
      (display " is ")
      (display (sqrt proposed-number))
      (display ".")
      (newline)
      (loop (begin
              (display "Number: ")
              (read))))))

In this form, the procedure doesn't provide any way for the user to break out of the loop (except by supplying a non-numeric value, so that the error procedure is invoked). What is needed is some kind of a sentinel -- a conventional value indicating the end of the input. The user could then type in the sentinel value when she wanted to leave the program.

Here's how the procedure looks if the symbol stop is used as the sentinel:

(define square-root-computer
  (lambda ()
    (display "Give me as many numbers as you like.")
    (newline)
    (display "I'll compute its square root of each one.")
    (newline)
    (display "Type STOP at the prompt to get me to stop.")
    (newline)
    (let loop ((proposed-number (begin
                                  (display "Number: ")
                                  (read))))
      (if (not (eq? proposed-number 'stop))
        (begin
          (if (not (number? proposed-number))
              (error 'square-root-computer
                     "To compute a square root, I need a number"))
          (display "The square root of ")
          (display proposed-number)
          (display " is ")
          (display (sqrt proposed-number))
          (display ".")
          (newline)
          (loop (begin
                  (display "Number: ")
                  (read))))))))

Exercise 3

Save this version of the procedure (again followed by a call to the procedure) in square-root-computer.ss and restart Chez Scheme with the same command line as in exercise 1. Use the program to compute the square roots of 153, 729, and 1111; then exit from the loop.


Exercise 4

Define a Scheme procedure sum-of-inputs that takes no arguments and returns the sum of as many numbers as the user chooses to type in. Prompt the user for each addend, and have the user signal the end of the addends by typing in the symbol end. An interaction with this procedure might look like this:

> (sum-of-inputs)
Addend: 53
Addend: 42
Addend: 39
Addend: 41
Addend: end
175

To illustrate the use of read-char, here is a procedure that reads in a line of input from the user and returns all the characters typed as a string:

(define read-line
  (lambda ()
    (let loop ((next-char (read-char))
               (so-far '()))
      (if (char=? next-char #\newline)
          (list->string (reverse so-far))
          (loop (read-char) (cons next-char so-far))))))

Here is a loop with which you can test the read-line procedure:

(define test-read-line
  (lambda ()
    (let loop ((line (begin
                       (display "Type in a line: ")
                       (read-line))))
      (if (not (string=? line "stop"))
          (begin
            (write line)
            (newline)
            (loop (begin
                    (display "Type in a line: ")
                    (read-line))))))))

Exercise 5

How can the user exit from the loop initiated by a call to test-read-line?


Exercise 6

Write a Scheme procedure named yes-or-no-prompt that prompts the user to type in a yes-or-no answer, reads in the response using read-line, and returns #t if the response is a non-null string beginning with y or Y and #f if the response is the null string or a string beginning with some other character.


This document is available on the World Wide Web as

http://www.math.grin.edu/~stone/courses/scheme/input-and-output.html

created October 10, 1997
last revised May 31, 1998

John David Stone (stone@math.grin.edu)