How to use format's iteration ability on a list of conses

Viewed 45

I know that I can use format's ~:{ ~} operator to process a list of lists directly, e.g.

CL-USER> (format t "~:{<~A,~A> ~}~%" '((1 2) (3 4)))
<1,2> <3,4>

But now I have a list of conses, e.g. ((1 . 2) (3 . 4)) and the expression

(format t "~:{<~A,~A> ~}~%" '((1 . 2) (3 . 4)))

leads to SBCL complaining

The value
  2
is not of type
  LIST 

Is there any format magic doing the trick without having to use an extra iteration with do or loop?

1 Answers

I see basically 4 options

Do not use format for the whole list

The straightforward solution is avoid the problem:

(loop 
  for (k . v) in '((1 . 2) (3 . 4))
  do (format t "<~a,~a> " k v))

Custom format function

Alternatively, use Tilde Slash to call a function that prints cons-cells:

(defun cl-user::pp-cons (stream cons colonp atsignp)
  (declare (ignore colonp atsignp))
  (format stream "~a, ~a" (car cons) (cdr cons)))

(format nil "~/pp-cons/" (cons 1 2))
=> "1, 2"

Note that the function must be in the CL-USER package if you don't specify the package. If you want to customize how cells are printed, you need to pass the format through a special variable:

(defvar *fmt* "(~s . ~s)")

(defun cl-user::pp-cons (stream cons colonp atsignp)
  (declare (ignore colonp atsignp))
  (format stream *fmt* (car cons) (cdr cons)))

And then:

(let ((*fmt* "< ~a | ~a >"))
  (format t "~/pp-cons/" (cons 1 2)))

=> < 1 | 2 >

Convert on printing

Build a fresh list, where improper lists are replaced by proper lists:

(format t
        "~:{<~a,~a> ~}~%"
        (series:collect 'list
          (series:mapping (((k v) (series:scan-alist '((1 . 2) (3 . 4)))))
            (list k v))))

The drawback is that the conversion needs to allocate memory, just for printing.

Change your data format

If proper lists are good for printing, maybe they are good for other operations too. A lot of standard functions expect proper lists. Note that the list ((1 2) (3 4)) is still an alist, the value is just wrapped in a cons-cell. If you decide to use this format from the start, you won't have to convert your lists.

Related