2011-03-28 40 views
10

Tôi cần phải viết một hàm sẽ nối một danh sách thành một chuỗi. Ví dụ:

(concatString (quote ("hello" "thế giới"))) ==> "hello world"chức năng lisp để nối một danh sách các chuỗi

đây là những gì tôi có cho đến nay:

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

Tôi nhận được một Thông báo "Lỗi:" hello "là và loại thông số bất hợp pháp" khi tôi cố gắng chạy nó. Tôi đã thử một loạt các cách để sửa đổi chức năng này và tôi havent đã có thể tìm ra nó. Có ai có ý tưởng nào?

Trả lời

14

concatenate đòi hỏi một chuỗi xác định kiểu như là đối số thứ hai. Để nối hai chuỗi, bạn nên gọi concatenate như:

(concatenate 'string "hello" "world") 

lỗi khác trong mã của bạn: bạn không chắc chắn rằng car của danh sách là một chuỗi trước khi gán nó vào result. Bằng cách sửa chữa mã của bạn, tôi đã đưa ra với việc thực hiện sau đây:

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

Các định nghĩa lại sau concatString là hiệu quả hơn vì nó không tạo ra nhiều đối tượng chuỗi trung gian:

(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 kiểm tra xem chuỗi của nó có phải vì gán hay không, nếu số của nó không được thêm vào chuỗi. Cảm ơn bạn rất nhiều mặc dù sửa chữa đã làm việc !!! =) – MBU

+1

Đó là tương đối xấu: bạn đang nối nhiều lần tạo chuỗi kết quả mới mọi lúc. Điều này có thể tạo ra một lượng rác lớn. –

+0

@Rainer Joswig Tôi có thể sửa chữa nó bằng cách nào? –

13

Chỉ cần sử dụng chức năng định dạng trên một danh sách, điều này sẽ chuyển đổi tất cả mọi thứ để dây và nối chúng với chuỗi định dạng đúng.

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

Nếu bạn muốn nối chúng với một không gian sử dụng hình thức này với "~ ^" chỉ thị:

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

Nếu bạn muốn lọc ra các kết quả, bạn chỉ có thể chuyển đổi trước khi định dạng nó.

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

xin lỗi, tôi có nên nói rằng nếu nó nên bỏ qua các mục trong danh sách không phải là chuỗi. vì vậy nếu một mục là một số thì không nên thêm nó vào chuỗi. – MBU

+0

Vâng, điều đó thay đổi phạm vi của vấn đề khá một chút – zellio

+3

Bạn có thể lọc ra không dây với loại bỏ-nếu-không: (defun my-concat (danh sách) (định dạng nil "~ {~ a ~}" (loại bỏ -if-# 'stringp list))) – Tyler

2

Tại sao giới hạn mình vào danh sách ?

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

Để ghép chuỗi vào chuỗi, sử dụng concatenate 'string.

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

Để xóa mọi thứ khỏi danh sách không phải là chuỗi, hãy sử dụng remove-if-not.

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

Nếu đối số không phải là danh sách, lỗi sẽ được báo hiệu bởi remove-if-not.Bạn có thể thêm xác nhận trước, tất nhiên, để cung cấp cho một thông báo lỗi cụ thể hơn, nhưng nó không thực sự thêm giá trị ở đây.

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

EDIT:

Như Rainer lưu ý, apply chỉ hoạt động trên danh sách dài hạn. Nếu bạn không có lý do để tin rằng danh sách của bạn không thể lâu hơn call-arguments-limit trừ một, một hình thức reduce là tốt hơn:

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

APPLY chỉ hoạt động với danh sách độ dài giới hạn. –

+0

@Rainer Joswig: Đúng vậy. – Svante

+3

Xem xét lại, cần lưu ý rằng nếu bạn muốn nối một số chuỗi lớn hơn, nó hiệu quả hơn để làm việc với các luồng (ví dụ, 'with-output-to-string'), hoặc preallocate chuỗi kết quả và sau đó điền vào nó. – Svante

2

Dưới đây là hai của tôi cent:

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

Các vấn đề liên quan