2011-03-28 12 views
10

Ho bisogno di scrivere una funzione che concatenerà una lista in una stringa. Esempio:

(concatString (citazione ("ciao" "mondo"))) ==> "ciao mondo"funzione lisp per concatenare un elenco di stringhe

Ecco quello che ho finora:

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

sto diventando un "Errore:" ciao "è e identificatore di tipo illegale" messaggio quando provo ad eseguirlo. Ho provato un sacco di modi per modificare questa funzione e non sono stato in grado di capirlo. Qualcuno ha qualche idea?

risposta

14

concatenate necessario un tipo sequenza specificatore come secondo argomento. Per concatenare due stringhe, si dovrebbe chiamare concatenate come:

(concatenate 'string "hello" "world") 

Un altro bug nel codice: non fare in modo che il car della lista è una stringa prima di assegnarlo a result. Fissando il codice, mi si avvicinò con la seguente implementazione:

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

Il seguente ridefinizione dei concatString è più efficiente in quanto non crea molti oggetti stringa intermedi:

(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 controllo se è una stringa perché per l'assegnazione, se è un numero non dovrebbe essere aggiunto alla stringa. Grazie mille anche se la correzione ha funzionato !!! =) – MBU

+1

Questo è relativamente cattivo: si concatenano ripetutamente creando nuove stringhe di risultati in ogni momento. Questo probabilmente crea una grande quantità di spazzatura. –

+0

@Rainer Joswig Come posso risolvere il problema? –

13

basta usare la funzione di formattazione in un elenco, questo permette di convertire tutto in stringhe e concatenarli con la stringa di formato corretta.

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

Se si desidera concatenare loro con uno spazio utilizzare questo modulo con il "~ ^" direttiva:

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

Se vuoi per filtrare i risultati, si può solo trasformare il elenco prima di formattarlo.

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

dispiace, avrei dovuto menzionati che se questo dovesse ignorare elementi nella lista che non sono stringhe. quindi se un oggetto è un numero non dovrebbe aggiungerlo alla stringa. – MBU

+0

Sì, questo cambia un po 'l'ambito del problema – zellio

+3

Puoi filtrare le non stringhe con remove-if-not: (defun my-concat (list) (formato nil "~ {~ a ~}" (rimuovi -se-non # 'elenco di stringhe))) – Tyler

2

perché limitarsi alle liste ?

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

Per concatenare sequenze a una stringa, utilizzare concatenate 'string.

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

Per rimuovere qualche cosa dalla lista che non è una stringa, utilizzare remove-if-not.

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

Se l'argomento non è un elenco, un errore sarà segnalato da remove-if-not.È possibile aggiungere l'asserzione prima, ovviamente, per dare un messaggio di errore più specifico, ma in realtà non aggiunge valore qui.

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

EDIT:

Come osserva Rainer, apply opera solo su elenchi di lunghezza limitata. Se non si ha ragione di credere che l'elenco non può essere più lungo di call-arguments-limit meno uno, una forma reduce è meglio:

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

APPLY funziona solo con un elenco di lunghezza limitata. –

+0

@Rainer Joswig: vero. – Svante

+3

Rivisitando, si dovrebbe notare che se si desidera concatenare un numero maggiore di stringhe, è più efficiente lavorare con i flussi (ad esempio 'with-output-to-string'), o preallocare la stringa dei risultati e quindi riempilo. – Svante

2

Qui sono i miei due centesimi:

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