String Streams: The Oddball Type

Streams are usually used for communicating with the outside world from within a Lisp program. One exception to this is the string stream, which simply makes a string look like a stream. In the same way you can read or write to external resources with other types of streams, a string stream will let you read or write to a string.

You can create string streams with the make-string-output-stream and make-string-input-stream commands. Following is an example that uses make-string-output-stream:

> (defparameter foo (make-string-output-stream))
> (princ "This will go into foo. " foo)
> (princ "This will also go into foo. " foo)
> (get-output-stream-string foo)
"This will go into foo. This will also go into foo. "

You may be wondering why anyone would want to do this, since we can already directly manipulate strings in Lisp, without using streams. Actually, there are several good reasons for using string streams in this way. They are useful for debugging, as well as for creating complex strings efficiently.

Sending Streams to Functions

Using string streams allows us to use functions that require streams as parameters. This is great for debugging code that works with files or sockets, using only strings for the input and output of data.

For example, suppose we have a function write-to-log that writes log information to a stream. Usually, we would want to send the log information to a file stream, so it can be written to a file for safekeeping. However, if we want to debug the function, we may want to send it a string stream instead, so we can take a look at the data it writes and make sure it’s correct. If we had hard-coded the write-to-log function to only write to a file, we wouldn’t have this flexibility. This is why it makes sense to write functions to use the abstract concept of a stream whenever possible, instead of using other methods to access external resources.

Working with Long Strings

String streams can lead to better-performing code when dealing with very long strings. For instance, concatenating two strings together can be a costly operation—first, it requires a new block of memory to be allocated to hold both strings, and then the strings need to be copied into this new location. Because of this bottleneck, many programming languages use devices called string builders to avoid this overhead. In Lisp, we can get similar performance benefits by using string streams.

Reading and Debugging

Another reason for using string streams is that they can make our code easier to read and debug, especially when we use the with-output-to-string macro.

image with no caption

Here’s an example of this command being used:

 >  (with-output-to-string (*standard-output*)
      (princ "the sum of ")
       (princ 5)
       (princ " and ")
       (princ 2)
       (princ " is ")
       (princ (+ 2 5)))
 "the sum of 5 and 2 is 7"

The with-output-to-string macro will intercept any text that would otherwise be output to the console, REPL, or other output stream, and capture it as a string. In the preceding example, the output created by the princ functions within the body of the with-output-to-string call is redirected automatically into a string stream. Once the body of the with-output-to-string command has completed, the entire printed output that was put into the stream is returned as a result .

You can also use the with-output-to-string macro to easily construct complex strings by “printing” each part, and then capturing the result as a string. This tends to be much more elegant and efficient than using the concatenate command.

Note

Using with-output-to-string runs counter to the tenets of functional programming (discussed in Chapter 14). Some Lispers consider this function (and similar functions that intercept input or output intended for other destinations) to be an ugly hack. You’ll see some disagreement in the Lisp community about whether the use of with-output-to-string is elegant or ugly.

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

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