Let's review some parts of your code.
(if (not (oddp n)) (progn (print ...) (return-from ...)))
When you have an if
with only one branch, think about using when
or unless
, especially
if you put a progn
in the subform. For example, you can get rid of the progn
by switching
to a when
:
(when (not (oddp n))
(print ...)
(return-from ...))
The (when (not ...))
expression is the same as (unless ...)
:
(unless (oddp n)
...)
Here, you print an error message, and return from the function.
Common Lisp has exceptions, which are made for those use cases.
You would typically call (error "Not an odd integer: ~d" n)
,
but here you can rely on the default behaviour of assert
, and
replace the whole check by:
(assert (oddp n))
If you try it with 2, you will obtain an error with a message similar to this:
The assertion (ODDP N) failed with N = 2.
Which is enough for tests.
Then, you have the following expression:
(cons (car (list n)) (triangle (- n 2)))
When you write (list e1 e2 .. en)
, it is as-if you wrote:
(cons e1 (cons e2 (... (cons en nil))))
In your case, that means that (list n)
is the same as:
(cons n nil)
But since you then do the following:
(car (cons n nil))
You are just in fact allocating a cell and discarding it just to access n
.
The whole expression can be replaced by n
.
Third, you also uses setf
on lst
, where lst
is an undefined variable.
On most implementations (but really this behaviour is unspecified), that would set the global binding
for lst
, and it is a bad practice to set global variables from within functions.
You could use a let
instead:
(let ((lst (cons n (triangle (- n 2)))))
(print lst))
But the variable is used only once, you may as well inline it:
(print (cons n (triangle (- n 2))))
Finally, you have:
(defun triangle (n)
(assert (oddp n))
(if (< n 1)
()
(print (cons n (triangle (- n 2))))))
This is a mattery of style, but remember that the if
that returns nil
in one of its branch can usually be replaced
by when
or unless
(here unless
). Some people don't like that and prefer to not rely on the nil
return value of when
and unless
.
Anyway, let's test it:
(triangle 7)
That gives:
(1)
(3 1)
(5 3 1)
(7 5 3 1)
Notice how the lists are backwards.
One way you could try to solve the problem is by replacing (print ...)
by (print (reverse ...))
, but that won't work. Can you see why?
Instead, let's build the list backwards, which requires to count up from 1, until we reach n
:
(defun triangle (n &optional (c 1))
(assert (oddp n))
(when (<= c n)
(print
(cons c (triangle n (+ c 2))))))
Since the second parameter is optional, we can call it as before:
(triangle 7)
But now, the output is:
(7)
(5 7)
(3 5 7)
(1 3 5 7)