开发者

Make String from Sequence of Characters

开发者 https://www.devze.com 2023-04-03 04:08 出处:网络
This code does not work as I expected. Could you please explain why? (defn make-str [s c] (let [my-str (ref s)]

This code does not work as I expected. Could you please explain why?

(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 开发者_Go百科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))))))

Thank you!


This is very slow & incorrect way to create string from seq of characters. The main problem, that changes aren't propagated - ref creates new reference to existing string, but after it exits from function, reference is destroyed.

The correct way to do this is:

(apply str seq)

for example,

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

If you want to make it more effective, then you can use Java's StringBuilder to collect all data in string. (Strings in Java are also immutable)


You pass a sequence with one character in it to your make-str function, not the character itself. Using first instead of take should give you the desired effect.

Also there is no need to use references. In effect your use of them is a gross misuse of them. You already use an accumulator in your function, so you can use str directly.

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

Of course count is not very nice in this case, because it always has to walk the whole sequence to figure out its length. So you traverse the input sequence several times unnecessarily. One normally uses seq to identify when a sequence is exhausted. We can also use next instead of drop to save some overhead of creating unnecessary sequence objects. Be sure to capture the return value of seq to avoid overhead of object creations later on. We do this in the 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)))

Functions like this, which just return the accumulator upon fully consuming its input, cry for reduce.

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

This is already nice and short, but in this particular case we can do even a little better by using apply. Then str can use the underlying StringBuilder to its full power.

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

Hope this helps.


You can also use clojure.string/join, as follows:

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

There is an alternate form of clojure.string/join which accepts a separator. See:

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

0

精彩评论

暂无评论...
验证码 换一张
取 消