String 从字符序列生成字符串

String 从字符序列生成字符串,string,clojure,state,sequence,String,Clojure,State,Sequence,这段代码没有像我预期的那样工作。你能解释一下原因吗 (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]

这段代码没有像我预期的那样工作。你能解释一下原因吗

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

谢谢大家!

这是一种非常缓慢且不正确的从字符序列创建字符串的方法。主要的问题是,更改不会传播-ref会创建对现有字符串的新引用,但在它退出函数后,引用会被销毁

正确的方法是:

(apply str seq)
比如说,

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

如果您想让它更有效,那么可以使用Java的StringBuilder来收集字符串中的所有数据。(Java中的字符串也是不可变的)

将包含一个字符的序列传递给
make str
函数,而不是字符本身。首先使用
而不是
take
应该可以获得所需的效果

也没有必要使用引用。实际上,你对它们的使用是对它们的严重滥用。您已经在函数中使用了累加器,因此可以直接使用
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))))))
当然,在这种情况下,
count
不是很好,因为它必须遍历整个序列才能计算出它的长度。因此,不必要地多次遍历输入序列。通常使用
seq
来识别序列何时耗尽。我们还可以使用
next
而不是
drop
来节省一些创建不必要的序列对象的开销。确保捕获
seq
的返回值,以避免以后创建对象的开销。我们在
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)))
像这样的函数在完全消耗其输入后才返回累加器,需要
reduce

(defn make-str-from-chars
  "make a string from a sequence of characters"
  [chars]
  (reduce str "" chars))
这已经很简单了,但是在这种特殊情况下,我们可以通过使用
apply
做得更好。然后
str
可以充分利用底层的
StringBuilder

(defn make-str-from-chars
  "make a string from a sequence of characters"
  [chars]
  (apply str chars))
希望这有帮助。

您也可以使用clojure.string/join,如下所示:

(require '[clojure.string :as str] )
(assert (= (vec "abcd")                [\a \b \c \d] ))
(assert (= (str/join  (vec "abcd"))    "abcd" ))
clojure.string/join的另一种形式接受分隔符。见:


答案的第一个和第二个版本(使用递归)不起作用。我仍然不明白为什么递归不起作用。@user477768,他们现在知道了,在if-let,语句的顺序错误,加上在fn调用周围添加了偏执狂。@kotarak是一个非常清晰简洁的例子,它逐渐从“做它的方式”发展到“做它的正确方式”。谢谢你的回答是的,我正在努力学习一些不仅仅是使用API的东西。我认为实现这样的东西是一种学习方式。Re:最后一句:
str
已经在其可变的情况下使用了
StringBuilder
,以及大量的类型暗示(特别是在1.3中,这也使得它成为
:static
)对于在
StringBuilder
中构建字符串的循环,使用
recur
。其中唯一的额外逻辑是对
nil
的特殊处理(因此
(str nil)
返回
“”
)。在性能方面很难改进。我希望(从chars[\u\r\a\n\u\s]生成str)返回“天王星”作为记录,尽量避免循环/重复,99%的时间你不需要它。试着用map/reduce来思考/apply@Hamza谢谢你的建议!