Lewis Levin
Lewis Levin

Reputation: 95

Combine these 2 lisp functions into one?

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

Answers (4)

coredump
coredump

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

Jérôme Radix
Jérôme Radix

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

Rainer Joswig
Rainer Joswig

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

Martin Půda
Martin Půda

Reputation: 7568

You can remove it- and you can simplify your function even more. You don't need concatenate or multiple formats, 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

Related Questions