Basic tips
nil
-> ()
and
-> if
(if it has 2 arguments)
(if(not a)b)
-> (or a b)
(if the return is not needed)
(when(not a))
-> (unless a)
(+ a 1)
-> (1+ a)
and (- a 1)
-> (1- a)
char-code
-> char-int
and code-char
-> int-char
(concatenate'string a b)
-> (format()"~a~a"a b)
- Large numbers can be compacted by writing them in base 36 with the prefix
#36r
.
Setting variables
There are multiple ways to set a variable:
(set'x 3)
to set just one variable.
(setq x 3 y 5)
to set more than one variable in succession.
setf
to set places (e.g. a location in an array or hash table)
- Normal variables and places can also be combined in
setf
statements.
psetf
is like setf
, but assignments are done in parallel.
incf
/decf
to add/subtract from a variable.
push
/pop
to push/pop from the front of a list.
All of the above methods also return the new value of the variable (except psetf
); multiple assignments return the last assigned value.
Printing
The format
function is an extremely terse and powerful tool for golfing, able to perform lengthy logic and iteration over its arguments. The Wikipedia page for format
contains (nearly) every available directive, with many annotated examples and a handy table; a complete syntax guide is available in the Hyper-Spec Section on Formatted Output.
It is difficult to teach all of what format
has to offer (golfing or otherwise), but here are some highlights:
- Use
~{~}
to print a list of values.
- Use
~[~]
as a conditional for booleans and small nonnegative integers.
- Use
~t
and ~&/~%
to print multiple spaces and newlines, respectively.
- Use
~*
to skip/goto specific arguments.
~<~>
can left align, right align, or center its contents.
~^
can cut off formatting early following a variety of conditions.
~f
and ~g
can round floats to a given precision.
~r
can print many non-decimal representations of integers.
- All directive parameters can be passed to
format
instead of hardcoded using a v
.
- Furthermore, a directive parameter can be set to the number of remaining arguments via
#
.
print
and princ
Quite disappointingly, Common Lisp does not have a function to print a value with a trailing newline, hence the necessity of format t"~a~%"
in most holes. However, if you need no additional formatting, you may be able to combine print
and princ
to achieve the same effect: print
prints its argument with a leading newline, while princ
adds no whitespace or newlines at all.
Thus, a distinguished first value of e.g. a sequence hole could invite print
and princ
to the forefront. Furthermore, print
and princ
return their arguments, allowing them to appear in the middle of a calculation or other form. Be wary with non-integers, though: lists will be printed as literals, and strings are wrapped in double quotes by print
.
Looping
loop
One of Lisp's most powerful (and arguably least Lisp-y) features is the loop
macro. The Hyper-Spec Section describes its capabilities in full, while the Common Lisp Cookbook has a more user-oriented review and collection of examples. Here's a selection of the most common use cases.
; Infinite loop
(loop)
; Loop over a range
(loop as i from a to b do (...)) ; includes b
(loop as i from b downto a do (...)) ; includes a
(loop as i from a below b do (...)) ; excludes b
(loop as i from b above a do (...)) ; excludes a
; Collect a range into a list
; This is often (unfortunately) the shortest way to do this
(loop as i from a to b collect i)
; Reduce values in a loop
(loop as i from a to b sum (...))
(loop as i from a to b thereis (...))
(loop as i from a to b always (...))
(loop as i from a to b never (...))
; Loop over a sequence
(loop as e in my-list do (...))
(loop as c across my-string do (...))
; Return early
(loop as i from a to b if (cond) return (val))
With all this and more, loop
is by far the most powerful and flexible looping facility Lisp has to offer. In general, though, loop
is only shortest when you have a "complicated" looping procedure which returns something.
do
The do
macro is the more Lisp-y cousin to loop
. It is capable of the same complex looping and collection/reduction operations, but varies from its relative in how terse each operation is. All do
loops have the following form:
(do (
; Update forms
(var-1 init-1 (update-1)) ; var-1 is set to init-1, then updated each iteration by evaluating update-1
(var-2 init-2 (update-2)) ; the (optional) update form is a rebinding of the var, rather than an in-place update
... ; updates occur in parallel; use do* if you want sequential updates
)(
; End forms
(end-condition) ; end-condition is checked after the update forms are run, including after initialization
(return-form-1) ; all return forms are run within the scope of the do loop
(return-form-2)
... ; only the last form's value is actually returned
)
; Body forms
(body-form-1)
(body-form-2)
...
)
Overall, consider do
anywhere you might consider loop
, particularly if you don't need to collect any values; do
is also likely the best option for loop variables which depend on each other.
doseq
doseq
is the shortest way to loop over any sequence without collecting or returning some value (e.g. running some procedure on each arg in *args*
). If you do need to return something, though, doseq
may still be the way to go: (doseq(x y z))
iterates as x
over y
and returns the evaluation of z
. You can also return early using return
.
dotimes
If you find yourself looping from 0
to n-1
, use dotimes
. It is far and away shorter than loop
or do
for the same task, and can return a value using the third parameter like doseq
. If you need to start from 1
or another small value, dotimes
may yet save the day, either by incorporating an offset into your code, or starting from 0
anyway and using a short conditional.
Note that dotimes
simply calls incf
on its loop variable on each iteration, rather than setting the variable to the "next" value. That is, the following is an infinite loop:
; 0 0 0 0 0 0 ...
(dotimes(x 10)(format t "~a " x)(decf x))
Macros
Use numbered sharpsign macros to compress lengthy, repeated forms. These macros expand to their values at compile time, and can also aid in reducing whitespace in some situations.
(let(
(a (parse-integer x))
(b (parse-integer y))
))
(let(
(a (#0=parse-integer x))
(b (#0# y))
))
Special variables & syntaxes
'
& `
Common Lisp has certain syntactic characters which expand to a form at compile time. The most common is '
: 'foo
is shorthand for (quote foo)
, where the quote
function is what actually produces symbols. Since Common Lisp is a Lisp-2, having distinct scopes for functions and user variables, quote
can be used as a variable.
This would be useless in golf if not for dotted pair notation, where .'
is replaced with quote
. This notation is parsed like a list literal, so it must be followed by exactly one literal, identifier, or form.
; Example:
(dotimes(i 10)(format t"~d ~a~%"i"potato")) ; this
(dotimes(quote 10)(format t"~d ~a~%"quote"potato")) ; is equivalent to this,
(dotimes'10(format t"~d ~a~%".'"potato")) ; is equivalent to this,
(dotimes'10(format t"~d ~a~%".'"potato"())) ; but this will fail to compile
The above also holds for backquote
, using .`
. Backquote's intended use is also quite powerful for golfing, used for constructing and splatting/unrolling lists:
(list(+ x 10)1 2 3'(foo bar))
`(,(+ x 10)1 2 3(foo bar))
(append a b)
`(,@a,@b)
+
, -
, *
, & /
The variables +
, ++
, and +++
(and their counterparts for /
and *
) store certain prior values of forms during the Lisp read-eval-print loop. Since submissions on code.golf are not run via interactive REPL, these values are never updated, but are set to specific values at the start of execution. Of particular use are *
and /
, which are both nil
.
Reference