I want to convert ("USERID=XYZ" "USERPWD=123")
to "USERID=XYZ&USERPWD=123"
. I tried
(apply #'concatenate 'string '("USERID=XYZ" "USERPWD=123"))
which will return ""USERID=XYZUSERPWD=123"
.
But i do not know how to insert '&'? The following function works but seems a bit complicated.
(defun join (list &optional (delim "&"))
(with-output-to-string (s)
(when list
(format s "~A" (first list))
(dolist (element (rest list))
(format s "~A~A" delim element)))))
Use FORMAT.
~{
and ~}
denote iteration, ~A
denotes aesthetic printing, and ~^
(aka Tilde Circumflex in the docs) denotes printing the , only when something follows it.
* (format nil "~{~A~^, ~}" '( 1 2 3 4 ))
"1, 2, 3, 4"
*
This solution allows us to use FORMAT
to produce a string and to have a variable delimiter. The aim is not to cons a new format string for each call of this function. A good Common Lisp compiler also may want to compile a given fixed format string - which is defeated when the format string is constructed at runtime. See the macro formatter
.
(defun %d (stream &rest args)
"internal function, writing the dynamic value of the variable
DELIM to the output STREAM. To be called from inside JOIN."
(declare (ignore args)
(special delim))
(princ delim stream))
(defun join (list delim)
"creates a string, with the elements of list printed and each
element separated by DELIM"
(declare (special delim))
(format nil "~{~a~^~/%d/~:*~}" list))
Explanation:
"~{ iteration start
~a print element
~^ exit iteration if no more elements
~/%d/ call function %d with one element
~:* move one element backwards
~}" end of iteration command
%d
is just an 'internal' function, which should not be called outside join
. As a marker for that, it has the %
prefix.
~/foo/
is a way to call a function foo
from a format string.
The variables delim
are declared special, so that there can be a value for the delimiter transferred into the %d
function. Since we can't make Lisp call the %d
function from FORMAT
with a delimiter argument, we need to get it from somewhere else - here from a dynamic binding introduced by the join
function.
The only purpose of the function %d
is to write a delimiter - it ignores the arguments passed by format
- it only uses the stream
argument.
Assuming a list of strings and a single character delimiter, the following should work efficiently for frequent invocation on short lists:
(defun join (list &optional (delimiter #\&))
(with-output-to-string (stream)
(join-to-stream stream list delimiter)))
(defun join-to-stream (stream list &optional (delimiter #\&))
(destructuring-bind (&optional first &rest rest) list
(when first
(write-string first stream)
(when rest
(write-char delimiter stream)
(join-to-stream stream rest delimiter)))))
With the newish and simple str library:
(ql:quickload "str")
(str:join "&" '("USERID=XYZ" "USERPWD=123"))
It uses format like explained in the other answers:
(defun join (separator strings)
" "
(let ((separator (replace-all "~" "~~" separator)))
(format nil
(concatenate 'string "~{~a~^" separator "~}")
strings)))
(author of it, to make simple things like this simple).
A bit late to the party, but reduce
works fine:
(reduce (lambda (acc x)
(if (zerop (length acc))
x
(concatenate 'string acc "&" x)))
(list "name=slappy" "friends=none" "eats=dogpoo")
:initial-value "")