3

Here's a code snippet that I wrote, but doesn't work:

(let ((separator "|")
      (text '("Just" "do" "it" "!")))
  (format t "~{~A~A~%~}" text separator))

The output should look like:

Just|
do|
it|
!|

However, it won't work like that - when I'm inside ~{~}, I don't know how I can add elements from outside the list. I could do this:

(format t "~{~A|~%~}" text)

but this does not allow me to use separator from variable, and that's what I'm after.

Now I know that I could merge text with separator, by adding separator in every even place, but that looks ugly to me. Is there other way to do it in format?

MatthewRock
  • 1,071
  • 1
  • 14
  • 30
  • This is almost answered by the accepted answer to http://stackoverflow.com/q/8830888/1281433, except that you want the separator not coded into the string. The [non-accepted answer](http://stackoverflow.com/a/9432718/1281433) is more general and could be used here (adjusting to let the delimiter be a string, and not just a character). – Joshua Taylor Dec 16 '15 at 15:15
  • 1
    This is absolutely _not_ answered! I want to have separator defined elsewhere, and the whole question is about that - the answer in the question you showed shows how to make separator separate elements instead of getting printed after each element. – MatthewRock Dec 16 '15 at 15:19
  • I said it's **almost** answered. If you didn't need to have the separator defined outside, this would simply be: **(format t "~{~A|~%~}" items)**. – Joshua Taylor Dec 16 '15 at 15:30
  • @JoshuaTaylor I didn't need it to separate, but to be at the end of each element, and apart from that I already knew how to do what you showed - the exact thing I was missing wasn't there, so the answer wasn't of much use for me, but thanks for the link - somebody might stumble across it and find it useful. – MatthewRock Dec 16 '15 at 15:40
  • It's still a separator; you just don't omit it after the final element. In your code, you even called it `separator`. – Joshua Taylor Dec 16 '15 at 15:43
  • Yeah, I guess I'm not really that great at providing clear questions. Luckily, I provided code snippet too, which is a bit more unambiguous. – MatthewRock Dec 16 '15 at 15:45
  • I wonder if something can be done with ~* (go-to). It *almost* works, in that could pass in the list (separator items...) and then do an iteration like ~{~A~0@*~} which says "process an element, then go-to element 0, etc. The problem is that ~@ doesn't just use the element at the given position, but resets the iteration position... – Joshua Taylor Dec 16 '15 at 15:58

3 Answers3

4

This is a hack:

CL-USER 49 > (let ((separator "|")
                   (text '("Just" "do" "it" "!")))
                (format t
                        (concatenate 'string "~{~A" separator "~%~}")
                        text))
Just|
do|
it|
!|

Escaping tilde in the separator string:

CL-USER 56 > (flet ((fix-tilde (string)
                      (with-output-to-string (s)
                        (loop for c across string
                              when (char= c #\~)
                              do (write-string "~~" s)
                              else do (write-char c s)))))
               (let ((separator (fix-tilde "~a~a"))
                     (text '("Just" "do" "it" "!")))
                 (format t
                         (concatenate 'string "~{~A" separator "~%~}")
                         text)))
Just~a~a
do~a~a
it~a~a
!~a~a
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
2

You could use a Tilde Slash format directive, that allows a user-defined function as format specifier. This is just to give you a possible example, many variations on this are possible:

CL-USER> (defvar *separator* " ")
*SEPARATOR*
CL-USER> (defun q(stream arg &rest args)
           (declare (ignore args))
           (format stream "~a~a" arg *separator*))
Q
CL-USER> (let ((*separator* "|")
               (text '("Just" "do" "it" "!")))
           (format t "~{~/q/~%~}" text))
Just|
do|
it|
!|
NIL
CL-USER> (let ((*separator* "+++")
               (text '("Just" "do" "it" "!")))
           (format t "~{~/q/~%~}" text))
Just+++
do+++
it+++
!+++
NIL
CL-USER> 
Renzo
  • 26,848
  • 5
  • 49
  • 61
1

Changing the answer from renzo a bit: if we use SPECIAL declarations, we don't need the global variable:

(defun %d1 (stream arg &rest args)
  (declare (ignore args)
           (special delim))
  (princ arg stream)
  (princ delim stream))

(defun my-print (list delim)
  (declare (special delim))
  (format nil "~{~/%d1/~%~}" list))
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346