Skip to content

LISP - String Concatenation

Common Lisp provides several powerful and flexible methods for string concatenation and formatting. Choosing the right one depends on the complexity of the task and performance considerations. This guide covers the most common techniques, from simple joining to complex, programmatic string construction.

  • concatenate: Best for simple, direct joining of a few strings.
  • format: The most versatile tool for creating complex formatted strings with embedded data.
  • with-output-to-string: Ideal for efficiently building a string incrementally, for example, within a loop.

The concatenate function is a generic sequence function. To use it for strings, you must specify 'string as the type of the resulting sequence. It then takes any number of strings as arguments and joins them together.

Here’s how to join two simple strings. Note that the result is a new string.

;; The first argument specifies the output type.
(concatenate 'string "Hello, " "Modern " "Lisp!")
"Hello, Modern Lisp!"

format is the Swiss Army knife of Lisp’s text formatting capabilities. It can construct a string from a template, seamlessly integrating various data types. The first argument to format is the destination. A destination of nil tells format to return the result as a new string.

Common format directives include:

  • ~a: Aesthetic directive, prints the argument in a human-readable way.
  • ~s: Syntactic directive, prints the argument as Lisp would read it (e.g., with quotes for strings).
  • ~d: Prints an integer in decimal.
  • ~%: Prints a newline character.
  • ~~: Prints a literal tilde ~ character.

This example creates a formatted string including a number and a string argument.

;; Using `format` with a `nil` destination to create a string.
(let ((user-name "Alex")
(items-count 42))
(format nil "User ~a has ~d items in the cart." user-name items-count))
"User Alex has 42 items in the cart."

The with-output-to-string macro is the most efficient way to build a string programmatically from many smaller pieces. It creates a string stream and captures all output directed to it. This avoids creating many intermediate string objects, which can be inefficient in a loop.

Here, we build a string by iterating over a list of items.

;; Define a list of parts to assemble.
(defvar *parts* '("chassis" "engine" "wheels"))
;; Build the final string using the macro.
(with-output-to-string (s) ; `s` is the stream variable
(write-string "Assembly report:~%" s)
(dolist (part *parts*)
(format s "- Installing ~a...~%" part))
(write-string "Assembly complete." s))
"Assembly report:
- Installing chassis...
- Installing engine...
- Installing wheels...
Assembly complete."

Best Practices and Choosing the Right Method

Section titled “Best Practices and Choosing the Right Method”
  • Use concatenate for simple, one-off joining of a few known strings. It’s clear and direct.
  • Use format whenever you need to embed data into a template string. It’s powerful, readable, and the idiomatic choice for formatted text.
  • Use with-output-to-string when building a string in pieces, especially inside loops or complex conditional logic. It offers the best performance for incremental string construction.