2011-03-28 20 views
10

リストを文字列に連結する関数を書く必要があります。例:ここ

(concatString(引用符( "hello" を "世界")))==> "Hello World" の文字列のリストを連結するlisp関数

は私がこれまで持っているものです。

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

私は取得しています「エラー:「hello」は不正な型指定子です」というメッセージが表示されます。私はこの機能を変更する方法の束を試してみました。私はそれを理解することができませんでした。誰にもアイデアはありますか?

答えて

14

concatenateその2番目の引数として配列型指定子を必要とします。あなたのコード内

(concatenate 'string "hello" "world") 

別のバグ::2つの文字列を連結するには、としてconcatenateを呼び出す必要がありますが、リストのcarresultに割り当てる前に、文字列であることを確認していません。それは多くの仲介文字列オブジェクトを作成しないようconcatString次の再定義がより効率的である

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

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

Imは文字列かどうかを検査します。なぜなら、代入のために数値であれば文字列に追加すべきではないからです。修正がうまくいってくれてありがとう、ありがとう! =) – MBU

+1

これは比較的悪いことです。常に新しい結果文字列を繰り返し作成して連結しています。これにより多量のゴミが発生する可能性があります。 –

+0

@Rainer Joswigどうすれば修正できますか? –

3

を動作するはずです:

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

ただ、これは文字列にすべてを変換します、リスト上のフォーマット機能を使用それらを正しい書式文字列と連結します。

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

申し訳ありませんが、私は言及している必要があります文字列ではないリスト内の項目は無視する必要があります。アイテムが数字の場合、それを文字列に追加すべきではありません。 – MBU

+0

ええ、それは問題の範囲を相当に変更します – zellio

+3

非文字列で非文字列を除外することができます:(defun my-concat(list) (format nil "〜{〜a〜}"(remove -if-not# 'stringp list))) – Tyler

2

なぜリストに自分自身を制限してコードを固定することで、私は次の実装を思い付きました?

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

シーケンスを文字列に連結するには、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は限られた長さのリストに取り組んでいます。あなたのリストがreduceフォームが優れている、call-arguments-limitマイナス1よりも長くすることはできませんことを信じる理由を持っていない場合:

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

APPLYは、制限された長さのリストでのみ機能します。 –

+0

@Rainer Joswig:True。 – Svante

+3

より多くの文字列を連結したい場合は、ストリームで処理する方が効率的です(例えば、 'with-output-to-string')、または結果の文字列をあらかじめ割り当ててからそれを埋める。 – Svante

2

は私の2セントです:

(defmacro concatString (&rest strings) `(concatenate 'string ,@strings))

関連する問題