【问题标题】:What's the canonical way to join strings in a list?在列表中加入字符串的规范方法是什么?
【发布时间】:2012-02-08 11:52:17
【问题描述】:

我想将("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)))))

【问题讨论】:

    标签: common-lisp


    【解决方案1】:

    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"
    

    【讨论】:

      【解决方案2】:

      此解决方案允许我们使用FORMAT 来生成字符串并具有变量分隔符。目的不是为每次调用此函数创建一个新的格式字符串。一个好的 Common Lisp 编译器也可能想要编译一个给定的固定 format string - 当 format string 在运行时被构造时它会被破坏。见宏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 函数。由于我们不能让 Lisp 使用分隔符参数从 FORMAT 调用 %d 函数,因此我们需要从其他地方获取它 - 这里是从 join 函数引入的动态绑定中获取。

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

      【讨论】:

      • 亲爱的 Rainer,你为什么要让变量名以% 开头,这是有原因的吗? (declare (special delim)) 是一种使 delim 变量动态绑定的方法吗?这个功能究竟是如何工作的?为什么%d 函数需要这些参数?
      • 在哪里可以找到有关~/ / 指令的任何信息? - 我在 CLTL 中找不到任何关于它的信息 ...
      • @coredump 谢谢!好的,仍在学习在哪里搜索/收集 cl 的信息 - 显然:D。
      • @Gwang-JinKim:你可以在 Common Lisp Hyperspec 中找到~/,使用主索引,非字母字符:lispworks.com/documentation/HyperSpec/Front/X_Mast_9.htm
      • @RainerJoswig:非常感谢!谢谢你的详细解释!这是我正在寻找的东西。但是,您对我的问题中的"#\\Tab""#\Tab" 问题有什么想法吗?
      【解决方案3】:

      有了新的简单的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)))
      

      (它的作者,让简单的事情变得如此简单)。

      【讨论】:

        【解决方案4】:

        聚会有点晚了,但reduce 工作正常:

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

        【讨论】:

          【解决方案5】:

          假设一个字符串列表和单个字符分隔符,以下应该可以有效地在短列表上频繁调用:

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

          【讨论】:

            【解决方案6】:

            使用FORMAT

            ~{~} 表示迭代,~A 表示美学打印,~^(在文档中又称为 Tilde Circumflex)表示打印 ,只有当它后面有东西时。

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

            【讨论】:

            • 不幸的是,这在 Emacs "elisp" 中不起作用。它们具有不同的格式功能。在 Emacs 中有没有类似的方法可以做到这一点?
            • @russ:可能。不是 elisp 向导,我回到了基本的 Lisp...(defun join-to-str (thing &rest strings) (labels ((recurser (strings) (cond ((> (length strings) 1) (append (list (car strings) thing) (recurser (cdr strings)))) (t (cons (car strings) nil))))) (apply 'concat (recurser strings))))
            • @russ 迟到总比不到好,如果你要连接字符串:(mapconcat 'identity '("one" "two") ", ") 但如果它们不是字符串,你想将 'identity 更改为 (lambda (x) (format "%s" x))
            猜你喜欢
            • 2010-12-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多