I need to write a function that will concatenate a list into a string. example:
(concatString (quote ("hello" " world"))) ==> "hello world"here is what i have so far:
(defun concatString (list)
"A non-recursive function that concatenates a list of strings."
(cond
((not (listp list))
(princ "Error: argument to concatNR must be a list")(terpri) ())) ; check if parameter is a list
(if (not (null list)) ;check if list is not null
(let ((result (car list)))
(dolist (item (cdr list))
(if (stringp 开发者_运维知识库item)
(setq result (concatenate result item)))
)
)
)
)
I'm getting a "Error: "hello" is and illegal type specifier" message when i try to run it. I've tried a bunch of ways to modify this function and i havent been able to figure it out. does anyone have any ideas?
concatenate
requires a sequence type specifier as its second argument. To concatenate two strings, you should call concatenate
as:
(concatenate 'string "hello" "world")
Another bug in your code: you do not make sure that the car
of the list is a string before assigning it to result
. By fixing your code, I came up with the following implementation:
(defun concatString (list)
"A non-recursive function that concatenates a list of strings."
(if (listp list)
(let ((result ""))
(dolist (item list)
(if (stringp item)
(setq result (concatenate 'string result item))))
result)))
;; tests
> (concatString (list "hello" " world"))
"hello world"
> (concatString (list "hello" 1 2 3 " world"))
"hello world"
> (concatString (list "hello" 1 2 "3" " world"))
"hello3 world"
> (concatString (list 1 2 3 "hello" " world"))
"hello world"
The following redefinition of concatString
is more efficient as it does not create many intermediary string objects:
(defun concatString (list)
"A non-recursive function that concatenates a list of strings."
(if (listp list)
(with-output-to-string (s)
(dolist (item list)
(if (stringp item)
(format s "~a" item))))))
Just use the format function on a list, this will convert everything to strings and concatenate them with the correct format string.
(defun my-concat( list )
(format nil "~{~a~}" list))
If you want to concatenate them with a space use this form with the "~^" directive:
(defun my-concat( list )
(format nil "~{~a~^ ~}" list))
If you'd like to filter out the results, you can just transform the list before formatting it.
(defun my-concat(list)
(format nil "~{~a~^ ~}" (remove-if-not #'stringp list)))
To concatenate sequences to a string, use concatenate 'string
.
(defun concat-strings (list)
(apply #'concatenate 'string list))
To remove anything from the list that is not a string, use remove-if-not
.
(defun concat-strings (list)
(apply #'concatenate 'string
(remove-if-not #'stringp list)))
If the argument is not a list, an error will be signaled by remove-if-not
. You can add the assertion before, of course, to give a more specific error message, but it does not really add value here.
(defun concat-strings (list)
(assert (listp list)
"This is not a list: ~s." list)
(apply #'concatenate 'string
(remove-if-not #'stringp list)))
EDIT:
As Rainer notes, apply
only works on lists of limited length. If you do not have reason to believe that your list cannot be longer than call-arguments-limit
minus one, a reduce
form is better:
(defun concat-strings (list)
(reduce (lambda (a b)
(concatenate 'string a b))
(remove-if-not #'stringp list)))
Common Lisp the Language, 2nd Edition
concatenate result-type &rest sequences
This one should work
(concatenate 'string result item)
According to the Common Lisp Cookbook :
(concatenate 'string "Karl" " " "Marx")
"Karl Marx"
Why limit yourself to lists?
(defun concatenate-strings (sequence)
(reduce #'(lambda (current next)
(if (stringp next)
(concatenate 'string current next)
current))
sequence
:initial-value ""))
Here are my two cents:
(defmacro concatString (&rest strings) `(concatenate 'string ,@strings) )
精彩评论