Formatting a list as a comma-delimited string is easy;
(format nil "~{~a~^,~}" '(1 2 3)) => "1,2,3"
How do i do the same (SBCL) but with tab characters in the output?
It must be simple but I've tried a ton of different things.
Cheers
Mike.
> (format nil "~{~a~^,~}" '(1 2 3)) => "1,2,3"
> How do i do the same (SBCL) but with tab characters in the output?
The simplest is to have a literal #\Tab in the format string:
(format nil "~{~a~^ ~}" '(1 2 3))
^ this is a #\Tab (``C-q TAB'' with emacs)
The next two simplest are (i) to construct the format string along
the lines of
(format nil (format nil "~~{~~a~~^~C~~}" #\Tab) '(1 2 3))
and (ii) to call a user-defined format function along the lines of
(format nil "~{~a~^~:*~/cl-user::tab/~}" '(1 2 3))
where
(defun cl-user::tab (stream &rest ignored)
(declare (ignore ignored))
(write-char #\Tab stream))
(the possibly redundant package marker above is there to illustrate
how the function name could be in any package).
Apart from the semi-portability of #\Tab, all of the above are
portable.
---Vassil.
--
"Even when the muse is posting on Usenet, Alexander Sergeevich?"
Just embed a TAB character which has char-code 9 in the format string
instead of the comma char-code 44. If your editor is set to convert all
tabs to spaces, you lose. Then you can use the `22.3.5.4 Tilde Slash:
Call Function' facility of FORMAT like this:
(defun cl-user::printtab (stream &rest args)
(declare (ignore args))
(write-char #\Tab stream))
(format nil "~{~a~^~:*~/printtab/~}" '(1 2 3))
This technique is useful when you want to delimit lists with arbitrary
strings specified at runtime: I'll point you to the following CLL post
by Juho snellman:
,----
| From: Juho Snellman <jsn...@iki.fi>
| Subject: Re: Newby riddle
| Date: 22 Aug 2007 17:54:53 GMT
| Message-ID: <slrnfcou3d...@sbz-30.cs.Helsinki.FI>
| (57r.jsnell@)
`----
Or to the `Re: Emacs Lisp's "mapconcat" in Common Lisp?'
thread from july this year
,----
| Subject: Re: Emacs Lisp's "mapconcat" in Common Lisp?
| Date: Mon, 13 Jul 2009 21:20:01 +0530
| Message-ID: <m3k52cy...@moon.robolove.meer.net>
| (m3k52cy446.fsf @ moon)
`----
--
Madhu
Personally, when this comes up, I just avoid the iteration in format
altogether.
(reduce (lambda (x y) (format nil "~A~C~A" x #\tab y)) '(1 2 3))
OTOH if you want to use just one format string, the above posts using
~/function/ seem to be the best way to do it.
> (reduce (lambda (x y) (format nil "~A~C~A" x #\tab y)) '(1 2 3))
How would you do the above so that it works equally well for stream
output destinations, not just when a string is produced (never mind
that it is slower in the latter case), i.e. when the first argument
to FORMAT is a parameter, and also for any format directive such as
`~S' for the elements?
In other words, neither
(reduce (lambda (x y) (format t "~A~C~A" x #\tab y)) '(1 2 3))
nor
(reduce (lambda (x y) (format nil "~S~C~S" x #\tab y)) '(1 2 3))
produce a desired result.
That's a good point. I have no idea how one would do that, I was just
answering the question he posted.
Use the ~n@T directive in the format string where n is an integer
to specify the width of the desired tab.
CL-USER 7 > (format nil "~{~a~^~5@T~}" '(1 2 3))
"1 2 3"
Carl Taylor
The ~T is not equivalent to #\Tab. But like Madhu said earlier, it
depends on your editor. Some may treat #\Tab as just some number of
spaces, so it wouldn't matter anyway.
In the end I guess my problem is just how to embed a tab char into the
format string using an 'escape sequence'. (Embedding the actual tab
char in the source isnt appealing.) This seems to work ok:
(format t (concatenate 'string "~{~a~^" (string #\Tab) "~}~%") '(1 2
3))
Otherwise the ~/ is a good way to go.
I thought there would have been an easier way to insert control chars
into the format string; seems wierd that there is a special case for ~
%, and that recognising codes like #\Tab wouldnt be in the spec.
Cheers
M
A new line is a notion that exists on all the textual output devices
of all the computers. It may be very different, eg. punching a
different card, or printing a different line on a line printer, or
possibly displaying a line in a different y coordinate on a glass tty,
but the notion exists.
On the contrary, the control character TAB doesn't exist in most
encodings. (That's why #\Tab is semi-standard: you can use it only if
it exists, and if it exists, it must be #\Tab ; to write a conformant
program you should write something like:
(defparameter *tab-char*
#+ #.(cl:if (cl:ignore-errors (cl:read-from-string "#\\Tab")) '(:and) '(:or))
#\Tab
#- #.(cl:if (cl:ignore-errors (cl:read-from-string "#\\Tab")) '(:and) '(:or))
#\Space)
and further use *tab-char* instead of #\Tab which may not exist.), and
even if it exists in the encoding used by your lisp system, nothing
says that this character corresponds to a meaningful control code for
the devices where you send it, so you should avoid using it anyways
(the same is true of the other characters that may be defined to
represent control codes).
In particular, notice that ~% does not mean to insert a new line
"character" in a string. #\Newline is a kludge introduced to ease the
processing of multi-line strings, but when you output it, or when
FORMAT encounter ~% (or ~& in the case it determines it must put a
new line), no newline "character" is output. (There does not exist
such a newline "character"). What lisp does is to call TERPRI. And
what TERPRI does depends on the device: again it may terminate the
line printing, which produce the printing of the line on a line
printer. Or it may advance to the next punch card. Or it may output
zero, one or more control codes (it may output zero control codes
when the output external format is a variable length line format with
a length, or a fixed length line format).
--
__Pascal Bourguignon__
> Thanks everyone for the different solutions.
> In this case I wasnt clear enough that I'm writing something like an
> awk script to reformat tab-delimited input, so need an actual tab.
...
> In the end I guess my problem is just how to embed a tab char into the
> format string using an 'escape sequence'. (Embedding the actual tab
> char in the source isnt appealing.) This seems to work ok:
Well, it may not be appealing to you, but inserting the literal
character into the string is the easy Lisp Way(tm) to do it. It is just
like when you want to have a literal newline character (sorry Pascal) in
a constant string. The way you do that is to literally insert the
newline character:
(defvar *a-multi-line-string*
"This is a string
that extends over several
lines in lisp.")
(defvar *a-single-line-string*
"This is a string\n on one line with odd\n characters in it.")
--
Thomas A. Russ, USC/Information Sciences Institute
I think you've found a good way to do it using concatenate. For the
record, there are all kinds of ways of doing this without using the ~
{~} format iteration, not just reduce like I initially suggested. It's
terribly boring, and probably won't attract everyone, but having
written a few files out, I'd rather go with what I consider to be
simple:
(format ostream "~A" (first list))
(dolist (x (rest list))
(format ostream "~C~A" #\tab x))
And then if you wanted that in string form you could wrap this guy
around it.
(with-output-to-string (ostream) ...)
Kyle
Everything goes better with LOOP:
(loop
for first-time? = t then nil
for element in '(1 2 3)
unless first-time? do (format t "~A" #\tab)
do (format t "~A" element) )
YMMV.
More wordy than a constructed FORMAT string. But clearer? I suppose it
depends on what you're used to, whether you spend more time with LOOP
or with FORMAT control strings.
(I suspect the final compiled code isn't all that different.)
-- Don
_______________________________________________________________________________
Don Geddis http://don.geddis.org/ d...@geddis.org
Another way is to modify the reader to accept \n \r \t etc.. as C does.
From a printf function I wrote long ago..
(I think Gareth McCaughan originally suggested this.)
(defvar *string-escape-character* #\\)
(defvar *string-escape-table*
;; built with LIST and CONS so it's legal to modify it
(list
(cons #\t #\Tab)
(cons #\n #\Newline)
(cons #\r #\Return)
...))
(defvar *escaped-string-delimiters*
(list #\" #\'))
(set-dispatch-macro-character #\# #\E
(lambda (stream sub-char infix-arg)
(declare (ignore sub-char infix-arg))
(let ((delimiter (read-char stream))
(escaped nil))
(assert (member delimiter *escaped-string-delimiters*))
(coerce (loop for next-char = (read-char stream) nconc
(cond
(escaped
(progn (setf escaped nil)
(list (or (cdr (assoc next-char *string-escape-table*))
next-char))))
((char= next-char delimiter)
(loop-finish))
((char= next-char *string-escape-character*)
(setf escaped t)
nil)
(t (list next-char))))
'string))))
(format nil #E"~{~a~^\t~}" '(1 2 3))
This would be better if you do this type of thing a lot..
Another way is to modify the reader to accept \n \r \t as C does.
From a printf function I wrote long ago..
(I think Gareth McCaughan originally suggested this.)
(defvar *string-escape-character* #\\)
(defvar *string-escape-table*
;; built with LIST and CONS so it's legal to modify it
(list
(cons #\t #\Tab)
(cons #\n #\Newline)
(cons #\r #\Return)
...))
(defvar *escaped-string-delimiters*
(list #\" #\'))
(set-dispatch-macro-character #\# #\E
(lambda (stream sub-char infix-arg)
(declare (ignore sub-char infix-arg))
(let ((delimiter (read-char stream))
(escaped nil))
(assert (member delimiter *escaped-string-delimiters*))
(coerce (loop for next-char = (read-char stream) nconc
(cond
(escaped
(progn (setf escaped nil)
(list (or (cdr (assoc next-char *string-escape-table*))
next-char))))
((char= next-char delimiter)
(loop-finish))
((char= next-char *string-escape-character*)
(setf escaped t)
nil)
(t (list next-char))))
'string))))
(format nil #E"~{~a~^\t~}" '(1 2 3))
This would be better if you do this type of thing a lot..
--
John Thingstad