在Edi Weitz的cl食谱中,对于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
的功能……
在赛贝尔的书中(在关于format
的章节中),我们发现通过以下方式将列表中的字符串连接到使用分隔符", "
的单个字符串:
(defvar l '("a" "b" "c"))
(format nil "~{~A~^, ~}" l)
;; "a, b, c"
这是一种pythonic式的连接,非常简洁;~^
指令使", "
只添加到最后一个元素之前,当后面没有元素时不添加。
但是,在这里,分隔符字符串", "
是format指令的一部分。
一个棘手的例子是(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"
。解析会后退print()
命令的执行,从而转义特殊符号。例如,deparse(deparse(substitute(x)))
将生成"\"x\""
-而围绕此表达式的parse(text = ... )
将再次生成"x"
parse(text = deparse(deparse(substitute(x))))
。这样的事情是如何在普通的lisp中实现的?例如,导致(文字):"#\Tab"
作为字符串的(a-special-function #\Tab)
?
Epilogue
感谢@Sylwester!!他没有使用宏就解决了这个问题。而且非常优雅!
发布于 2018-06-02 06:20:45
您的问题是,您试图将#\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"
https://stackoverflow.com/questions/50647635
复制相似问题