Reputation: 95
This does what I want but I wonder if I can eliminate the outer function. It only serves to reject empty input and return nil. In the recursive inner function I cdr through the input list and when empty, return the string "NIL" to be concatenated into the output string. So, the (null lst) does matter.
For each input, the code below does return the expected output:
'() or nil => NIL
'(1) => "(1 . NIL)"
'(1 2) => "(1. (2. NIL))"
(defun showdots (lst)
(if (not (null lst))
(do-showdots lst)
()))
(defun do-showdots (lst)
(if (not (null lst))
(concatenate 'string (format nil "(~A . " (car lst)) ; first member of pair
(do-showdots (cdr lst))
(format nil ")")) ; the rest
(format nil "NIL"))) ; there is nothing left so concatenate the end of cons
You may recognize this as an exercise from Paul Graham's "ANSI Common Lisp". But, I am not taking a class; I am working through the book on my own--so it's ok to help me out.
Upvotes: 1
Views: 123
Reputation: 38799
I wanted to show an example that involves the pretty-printer, because that's something that may come handy. This integrates a bit better with the usual printing functions, respecting the various global variables that influence formatting. In particular, the code automatically checks if there are circular references.
I checked the book and the exercise gives very few details, in particular it does not say what to do when the items in the list are themselves lists. Instead of calling write
on the elements, I also recurse with showdots
and there is a base case that just writes the object when it's an atom (that also covers the NIL case).
(defun showdots (object)
(typecase object
(cons
(pprint-logical-block (nil object :prefix "(" :suffix ")")
(showdots (car object))
(write-string " . ")
(pprint-newline :linear)
(showdots (cdr object))))
(t (write object))))
For example:
(let ((*print-right-margin* 50)
(*print-circle* t))
(fresh-line)
(showdots '(a b . #1=(c d (e 1 (2 a b c) (3 a b . #1#)) f g h i j k l))))
Prints:
(A .
(B .
#1=(C .
(D .
((E .
(1 .
((2 . (A . (B . (C . NIL)))) .
((3 . (A . (B . #1#))) . NIL)))) .
(F .
(G .
(H . (I . (J . (K . (L . NIL))))))))))))
Upvotes: 2
Reputation: 10533
Respecting your approach:
(defun showdots (lst)
(if lst
(if (null (cdr lst))
(format nil "(~A . NIL)" (car lst))
(concatenate 'string (format nil "(~A . " (car lst))
(showdots (cdr lst))
(format nil ")")))))
Upvotes: 0
Reputation: 139251
Without intermediate strings:
(defun showdots (list)
(labels ((%showdots (s list)
(cond (list
(format s "(~A . " (car list))
(%showdots s (cdr list))
(format s ")"))
(t (format s "NIL")))))
(with-output-to-string (s)
(%showdots s list))))
CL-USER 30 > (showdots '(1 2 4 5))
"(1 . (2 . (4 . (5 . NIL))))"
Upvotes: 2
Reputation: 7568
You can remove it- and you can simplify your function even more. You don't need concatenate
or multiple format
s, one is enough. And if
that returns nil
in one branch is when
(or unless
in the opposite case):
(defun showdots (list)
(when list
(format nil "(~A . ~A)"
(car list)
(showdots (cdr list)))))
Tests:
> (showdots nil)
NIL
> (showdots '())
NIL
> (showdots '(1))
"(1 . NIL)"
> (showdots '(1 2))
"(1 . (2 . NIL))"
Upvotes: 4