функция lisp для объединения списка строк

Мне нужно написать функцию, которая объединит список в строку. пример:

(concatString (цитата ("hello ""мир"))) = = > "hello world"

вот что у меня пока есть:

(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)))          
        )
      )
  )
)

Я получаю сообщение "Error:" hello" is and illegal Type specifier", когда я пытаюсь его запустить. Я пробовал множество способов изменить эту функцию, и я не смог понять это. у кого-нибудь есть идеи?

7 ответов


concatenate требует спецификатор типа последовательности в качестве второго аргумента. Чтобы объединить две строки, вы должны вызвать concatenate as:

(concatenate 'string "hello" "world")

еще одна ошибка в вашем коде: вы не убедитесь в том, что car списка является строкой, прежде чем присваивать его result. Исправить ваш код, я придумал следующую реализацию:

(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"

следующее переопределение concatString более эффективно, поскольку он не создает много посредников объекты строку:

(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))))))

просто используйте функцию format в списке, это преобразует все в строки и объединит их с правильной строкой формата.

(defun my-concat( list )
  (format nil "~{~a~}" list))

Если вы хотите объединить их с пробелом, используйте эту форму с директивой"~^":

(defun my-concat( list )
  (format nil "~{~a~^ ~}" list))

Если вы хотите, чтобы отфильтровать результаты, вы можете просто преобразовать список перед форматированием.

(defun my-concat(list)
  (format nil "~{~a~^ ~}" (remove-if-not #'stringp list)))

чтобы объединить последовательности в строку, используйте concatenate 'string.

(defun concat-strings (list)
  (apply #'concatenate 'string list))

чтобы удалить что-либо из списка, которое не является строкой, используйте remove-if-not.

(defun concat-strings (list)
  (apply #'concatenate 'string
         (remove-if-not #'stringp list)))

если аргумент не является списком, ошибка будет сигнализироваться remove-if-not. Вы можете добавить утверждение раньше, конечно, чтобы дать более конкретное сообщение об ошибке,но это не добавляет значения здесь.

(defun concat-strings (list)
  (assert (listp list)
          "This is not a list: ~s." list)
  (apply #'concatenate 'string
         (remove-if-not #'stringp list)))

EDIT:

как отмечает Райнер, apply работает только на списки ограниченной длины. Если у вас нет оснований полагать, что ваш список не может быть больше call-arguments-limit минус один, a reduce форма лучше:

(defun concat-strings (list)
  (reduce (lambda (a b)
            (concatenate 'string a b))
          (remove-if-not #'stringp list)))

Common Lisp язык, 2-е издание

объединить тип результата & rest последовательности

Это должно работать

(concatenate 'string result item)

по словам Общая Поваренная Книга Lisp :

(concatenate 'string "Karl" " " "Marx")
"Karl Marx"

зачем ограничивать себя в списках?

(defun concatenate-strings (sequence)
  (reduce #'(lambda (current next)
              (if (stringp next)
                (concatenate 'string current next)
                current))
          sequence
          :initial-value ""))

вот мои два цента:

(defmacro concatString (&rest strings `'(concatenate ' строка , @strings))