common-lisp 中更好的 pythonic `join`
Nicer pythonic `join` in common-lisp
在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 reader 似乎在一个字符串中总是将 "\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"
在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 reader 似乎在一个字符串中总是将 "\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"