lisp - common-lisp 中更好的 pythonic `join`

标签 lisp common-lisp metaprogramming

在 Edi Weitz 的 cl cookbook 中,对于 pythonic join,建议使用这个函数:

(defun join (separator list)
  (with-output-to-string (out)
    (loop for (element . more) on list
          do (princ element out)
          when more
            do (princ separator out))))

然而,不知怎的,我在想,一定有一种方法可以用另一种方式来表达join,也许使用format的能力......

在 Seibel 的书中,(在关于 format 的章节中)我们发现将列表中的字符串连接到带有分隔符的单个字符串 ", " 来自:

(defvar l '("a" "b" "c"))

(format nil "~{~A~^, ~}" l)
;; "a, b, c"

这是一个 pythonic 连接,非常简洁; ~^ 指令使得 ", " 仅添加到最后一个元素之前,并且在没有元素跟随时不添加。

但是,在这里,分隔符字符串 ", " 是格式指令的一部分。

一个棘手的例子是(defvar sep #\Tab)。 如果 sep "#\Tab" 字面上的表示可以作为分隔符放在这个格式指令中间,结果是:

(format nil "~{~A~^#\Tab~}" l)

我们本来可以达到目标的。

显然,必须使用宏来生成格式指令...... 我尝试了 (princ-to-string sep) 之类的操作,但这给出了 "#\\Tab" 而不是 "#\Tab"

例如

(defmacro join (sep l)
  `(format nil ,(format nil "~{~A~}" `("\~\{\~A\~\^" ,(write-to-string sep) "\~\}")) l))

但是在尝试时:

(join #\Tab '("a" "b" "c"))

这个结果当然是不希望的:"a#\\Tabb#\\Tabc",因为

(macroexpand-1 '(join #\Tab '("a" "b" "c")))
;; results in:
(FORMAT NIL "~{~A~^#\\Tab~}" L)
;; instead of:
(FORMAT NIL "~{~A~^#\Tab~}" L)

但我看不出如何将这一步实现到所需的宏... 有没有人对此有所启发?

一种关于元编程问题的元编程......

好的,现在我明白了,@Rainer Joswig 已经在 What's the canonical way to join strings in a list?

解决这个问题。但是,如果有办法将 "#\\Tab" 表示为 "#\Tab",则可以得出更紧凑的定义。 但不知何故,Lisp 阅读器似乎总是将字符串中的 "\Tab" 识别为一个字母。是否可以编写一个函数来执行此操作?

备注

在 R 中,存在专门用于元编程的函数,例如 as.name("myvar") 可以从字符串“myvar”中生成符号myvar。 以及像 deparse(substitute(x)) 这样的表达式,它采用符号 x 并从中创建一个文字字符串 "x"。 Deparse 后退 print() 命令的执行,从而转义特殊符号。 deparse(deparse(substitute(x))) 例如生成 "\"x\"" - 而围绕此表达式的 parse(text = ... ) 将生成 "x"再次 parse(text = deparse(deparse(substitute(x))))。 如何在 common-lisp 中实现这样的事情? 例如,(a-special-function #\Tab) 导致(文字):"#\Tab" 作为字符串?

结语

谢谢@Sylwester!!他不用宏就解决了。而且非常优雅!

最佳答案

您的问题是您尝试将 #\Tab 添加到格式中,但这正是您使用文字的方式。如果您只是在格式字符串中插入一个制表符或由它组成的字符串,它将执行您想要的操作:

(defun join (l &key (sep ", "))
  (format nil (format nil "~a~a~a" "~{~a~^" sep "~}") l))

(join '(1 2 3)) 
; ==> "1, 2, 3"
(join '(1 2 3) :sep #\Tab) 
; ==> "1    2   3"

关于lisp - common-lisp 中更好的 pythonic `join`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50647635/

相关文章:

python - 如何在Python中修改def的行为?

c - 知道 void * 分配类型的宏

ruby - 如何将参数传递给 define_method?

Lisp:确定列表是否包含谓词

lisp - 如何比较用户输入的字符串?

random - 由 sbcl 编译的执行文件不能使随机或使打开文件工作

emacs - 我怎样才能让粘液在 Windows 上与 clisp 一起工作

Ruby Lisp 解释器转换算术符号?

recursion - 用于嵌套循环的 Lisp 宏(或函数)

common-lisp - 如何更改类的元类