在 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/