我想写一个lisp
函数,它在字符串中进行多次搜索和替换。例如,我想将"a"
和"t"
分别替换为字符"e"
中的"d"
和"bat"
。
我该怎么做?
答案 0 :(得分:3)
这是一个纯功能版本:
(map 'string (lambda (c)
(case c
(#\a #\e)
(#\t #\d)
(t c)))
"bat")
==> "bed"
为了使这个更通用,你可以在编译时使用宏构建lambda:
(defmacro make-translation-lambda (from to)
`(lambda (c) (case c ,@(map 'list (lambda (i o) `(,i ,o)) from to) (t c))))
(map 'string (make-translation-lambda "at" "ed") "bat")
==> "bed"
请注意,宏make-translation-lambda
的参数必须是字符串文字。
或者,更灵活但效率更低,你可以做到
(defun translate-string (input from to)
(assert (= (length from) (length to)))
(map 'string
(lambda (c)
(let ((pos (position c from)))
(if pos
(char to pos)
c)))
input))
(translate-string "bed" "at" "ed")
==> "bed"
使用宏make-translation-lambda
的版本的性能与正在翻译的字符串呈线性关系(O(length(input))
)。
函数translate-string
的效果为O(length(input) * length(from))
。
答案 1 :(得分:1)
如果您希望一次从原始字符串替换一个字符,类似于tr unix实用程序的工作方式,您应该processing the string one character a time并收集转换后的字符:
(defun transform-chars (replacements str)
"replacements is a list of lists: (FROM-CHAR TO-CHAR)"
(coerce
(loop for char across str
for tr = (assoc char replacements)
if (null tr) collect char
else collect (second tr))
'string))
(transform-chars '((#\a #\e) (#\t #\d)) "bat")
我在这些子句中使用LOOP
宏:
我们也是coercing the collected characters from a list into a string。
答案 2 :(得分:0)
仅供记录:
(defun make-sparse-charmap (from to)
(loop with map =
(loop with map = (make-string 128 :initial-element #\x)
for i from 0 below 128 do
(setf (char map i) (code-char i))
finally (return map))
for x across from
for y across to do
(setf (char map (char-code x)) y)
finally (return map)))
(defun tr (source from to)
(loop with map = (make-sparse-charmap from to)
and result = (make-string (length source) :initial-element #\x)
for c across source
for i from 0 do
(setf (char result i) (char map (char-code c)))
finally (return result)))
对于Unicode字符串来说,这可能不是最好的主意,但对于ASCII来说,这样做很好。
修改的
略微修改它,没有额外的lambdas生成。