Сделать строку из последовательности символов - PullRequest
5 голосов
/ 05 сентября 2011

Этот код не работает, как я ожидал.Не могли бы вы объяснить, почему?

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

Спасибо!

Ответы [ 3 ]

12 голосов
/ 05 сентября 2011

Это очень медленный и неправильный способ создания строки из последовательности символов. Основная проблема в том, что изменения не распространяются - ref создает новую ссылку на существующую строку, но после выхода из функции ссылка уничтожается.

Правильный способ сделать это:

(apply str seq)

например,

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

Если вы хотите сделать его более эффективным, вы можете использовать Java StringBuilder для сбора всех данных в строку. (Строки в Java также неизменны)

11 голосов
/ 05 сентября 2011

Вы передаете последовательность с одним символом в функцию make-str, а не сам символ.Использование first вместо 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))

Надеюсь, это поможет.

2 голосов
/ 02 февраля 2014

Вы также можете использовать clojure.string / join следующим образом:

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

Существует альтернативная форма clojure.string / join , которая принимаетразделитель.См .:

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

...