2011-09-05 48 views
5

Mã này không hoạt động như tôi mong đợi. Bạn có thể giải thích tại sao không?Tạo chuỗi từ chuỗi ký tự

(defn make-str [s c] 
    (let [my-str (ref s)] 
    (dosync (alter my-str str c)))) 

(defn make-str-from-chars 
    "make a string from a sequence of characters" 
    ([chars] make-str-from-chars chars "") 
    ([chars result] 
     (if (== (count chars) 0) result 
     (recur (drop 1 chars) (make-str result (take 1 chars)))))) 

Cảm ơn bạn!

+0

Bạn mong đợi điều gì? – dm3

+0

Tôi muốn (make-str-from-chars [\ u \ r \ a \ n \ u \ s]) trả lại "uranus" –

+1

cho bản ghi cố gắng tránh lặp/lặp lại càng nhiều càng tốt, 99% thời gian bạn không cần nó. cố gắng suy nghĩ về bản đồ/giảm/áp dụng –

Trả lời

11

Bạn chuyển một chuỗi với một ký tự trong đó đến hàm make-str của bạn, chứ không phải chính ký tự đó. Sử dụng first thay vì take sẽ cho bạn hiệu quả mong muốn.

Cũng không cần sử dụng tài liệu tham khảo. Trong thực tế việc bạn sử dụng chúng là một sự lạm dụng tổng thể của chúng. Bạn đã sử dụng bộ tích lũy trong chức năng của mình, vì vậy bạn có thể sử dụng trực tiếp str.

(defn make-str-from-chars 
    "make a string from a sequence of characters" 
    ([chars] (make-str-from-chars chars "")) 
    ([chars result] 
    (if (zero? (count chars)) 
     result 
     (recur (drop 1 chars) (str result (first chars)))))) 

Tất nhiên count không phải là rất tốt đẹp trong trường hợp này, bởi vì nó luôn luôn phải đi bộ toàn bộ chuỗi để tìm ra chiều dài của nó. Vì vậy, bạn đi qua chuỗi đầu vào nhiều lần không cần thiết. Người ta thường sử dụng seq để xác định thời điểm một chuỗi bị cạn kiệt. Chúng tôi cũng có thể sử dụng next thay vì drop để tiết kiệm một số chi phí tạo các đối tượng chuỗi không cần thiết. Hãy chắc chắn nắm bắt giá trị trả lại của seq để tránh chi phí cho các sáng tạo đối tượng sau này. Chúng tôi làm điều này trong if-let.

(defn make-str-from-chars 
    "make a string from a sequence of characters" 
    ([chars] (make-str-from-chars chars "")) 
    ([chars result] 
    (if-let [chars (seq chars)] 
     (recur (next chars) (str result (first chars))) 
     result))) 

Chức năng như thế này, chỉ trả lại bộ tích lũy khi tiêu thụ đầy đủ đầu vào, khóc cho reduce.

(defn make-str-from-chars 
    "make a string from a sequence of characters" 
    [chars] 
    (reduce str "" chars)) 

Điều này thật tuyệt và ngắn, nhưng trong trường hợp cụ thể này, chúng tôi có thể làm tốt hơn một chút bằng cách sử dụng apply. Sau đó, str có thể sử dụng toàn bộ số StringBuilder cơ bản.

(defn make-str-from-chars 
    "make a string from a sequence of characters" 
    [chars] 
    (apply str chars)) 

Hy vọng điều này sẽ hữu ích.

+0

Phiên bản đầu tiên và thứ hai của câu trả lời của bạn (sử dụng đệ quy) không hoạt động. Tôi vẫn không hiểu tại sao đệ quy lại không hoạt động. –

+0

@ user477768, họ làm ngay bây giờ, tại if-let, các tuyên bố sai thứ tự cộng với các cuộc gọi thêm xung quanh cuộc gọi fn. –

+0

@kotarak Rất sư phạm, cảm ơn. – tharibo

12

Điều này rất chậm & cách không chính xác để tạo chuỗi từ seq ký tự. Vấn đề chính, những thay đổi đó không được truyền bá - ref tạo tham chiếu mới đến chuỗi hiện có, nhưng sau khi nó thoát khỏi hàm, tham chiếu bị hủy.

Các cách chính xác để làm điều này là:

(apply str seq) 

ví dụ,

user=> (apply str [\1 \2 \3 \4]) 
"1234" 

Nếu bạn muốn làm cho nó hiệu quả hơn, sau đó bạn có thể sử dụng StringBuilder Java để thu thập tất cả dữ liệu trong chuỗi . (Các chuỗi trong Java cũng không thay đổi)

+0

Có, tôi đang cố gắng tìm hiểu điều gì đó hơn là chỉ sử dụng API. Thực hiện một cái gì đó như thế này là một cách học tập, tôi nghĩ. –

+1

Re: câu cuối cùng: 'str' đã sử dụng' StringBuilder' trong trường hợp variadic của nó, cũng như kiểu gợi ý nặng (đặc biệt là 1.3, cũng làm cho nó ': static') và' recur' cho vòng lặp lên chuỗi trong 'StringBuilder'. Chỉ một chút logic bổ sung trong đó là xử lý đặc biệt 'nil' (để' (str nil) 'trả về' "" '). Sẽ rất khó để cải thiện điều này một cách hiệu quả. –

2

Bạn cũng có thể sử dụng clojure.string/join, như sau:

(require '[clojure.string :as str]) 
(assert (= (vec "abcd")    [\a \b \c \d])) 
(assert (= (str/join (vec "abcd")) "abcd")) 

Có một dạng thay thế của clojure.string/join mà chấp nhận một tách. Xem:

http://clojuredocs.org/clojure_core/clojure.string/join

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