I have two strings of the same length which differ in exactly one character and I want a string of all the characters which are pairwise equal. So basically something like this which evaluates to a string instead of a list of characters:
(loop for a across "abcd"
for b across "abce"
when (char= a b) collect a)
Although performance isn't an issue here, I found it cumbersome to have a (coerce ... 'string)
around it.
So I came up with something like
(loop with result = ""
for a across "abcd"
for b across "abce"
when (char= a b)
do (setf result (concatenate 'string result (string a)))
finally (return result))
which does the job but looks not very elegant to me.
(map 'string (lambda (a b) (when (char= a b) a)) "abcd" "abce")
looks nicer but is not working because NIL
is not a character when a
and b
are not equal.
Is there a more elegant idiom to iterate over a string and get a string back?
(loop with result = ""
for a across "abcd"
for b across "abce"
when (char= a b)
do (setf result (concatenate 'string result (string a)))
finally (return result))
Repeated concatenate are not a that good idea for longer strings.
Alternatives:
Loop into a list and coercing to a string
CL-USER 3 > (loop for a across "abcd"
and b across "abce"
when (char= a b) collect a into list
finally (return (coerce list 'string)))
"abc"
Using stream and converting it to a string
CL-USER 4 > (with-output-to-string (*standard-output*)
(loop for a across "abcd"
and b across "abce"
when (char= a b) do (write-char a)))
"abc"
Using an adjustable string
CL-USER 5 > (loop with string = (make-array 0
:element-type 'character
:adjustable t
:fill-pointer 0)
for a across "abcd"
for b across "abce"
when (char= a b) do (vector-push-extend a string)
finally (return string))
"abc"