在列表中连接字符串的规范方法是什么?

时间:2012-01-12 06:12:39

标签: common-lisp

我想将("USERID=XYZ" "USERPWD=123")转换为"USERID=XYZ&USERPWD=123"。我试过了

(apply #'concatenate 'string '("USERID=XYZ" "USERPWD=123"))

将返回""USERID=XYZUSERPWD=123"

但我不知道如何插入'&'?以下功能有效,但看起来有点复杂。

(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)))))

6 个答案:

答案 0 :(得分:41)

使用FORMAT

~{~}表示迭代,~A表示美学印刷,而~^(文档中也称为Tilde Circumflex)表示只有当某些内容出现时才会打印。

* (format nil "~{~A~^, ~}" '( 1 2 3 4 ))

"1, 2, 3, 4"
* 

答案 1 :(得分:5)

此解决方案允许我们使用FORMAT生成字符串并具有可变分隔符。目的不是为每个函数调用一个新的格式字符串。一个好的Common Lisp编译器也可能想要编译给定的固定格式字符串 - 当在运行时构造格式字符串时会失败。请参阅宏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))

说明:

"~{      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只是一个“内部”函数,不应在join之外调用。作为标记,它具有%前缀。

~/foo/是一种通过格式字符串 call a function foo的方式。

变量delim被声明为特殊,因此可以为转移到%d函数中的分隔符赋值。由于我们不能使用%d使用分隔符参数调用FORMAT函数,因此我们需要从其他地方获取它 - 这里是join函数引入的动态绑定。

函数%d的唯一目的是编写分隔符 - 它忽略format传递的参数 - 它只使用stream参数。

答案 2 :(得分:2)

Melpa托管软件包s(“久违的Emacs字符串处理库”),该软件包提供了许多简单的字符串实用程序,包括字符串连接器:

(require 's)                                                                                                                                                                                          
(s-join ", " (list "a" "b" "c"))                                                                                                                                                                      
"a, b, c"

对于它的价值,很简单,它使用了fns.c中的几个lisp函数,即mapconcatidentity

(mapconcat 'identity (list "a" "b" "c") ", ")                                                                                                                                                         
"a, b, c"

答案 3 :(得分:1)

假设有一个字符串列表和一个字符分隔符,以下内容对于短列表上的频繁调用应该有效:

(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)))))

答案 4 :(得分:1)

使用新的简单str库:

(ql:quickload "str")
(str:join "&" '("USERID=XYZ" "USERPWD=123"))

它使用其他答案中解释的格式:

(defun join (separator strings)
" "
(let ((separator (replace-all "~" "~~" separator)))
  (format nil
        (concatenate 'string "~{~a~^" separator "~}")
        strings)))

(它的作者,简单地做这样简单的事情)。

答案 5 :(得分:0)

派对有点晚了,但reduce工作正常:

(reduce (lambda (acc x)
          (if (zerop (length acc))
              x
              (concatenate 'string acc "&" x)))
        (list "name=slappy" "friends=none" "eats=dogpoo")
        :initial-value "")