Clojure Пока цикл
Я пытаюсь clojure Я пытаюсь выяснить, как реализовать следующий алгоритм,
Я читаю из входного потока, который я хочу продолжить, пока он не будет символом разделителя.
Я могу сделать это в java с циклом while, но я не могу понять, как это сделать в clojure?
while
read
readChar != delimiter
do some processing....
end while
Ответы
Ответ 1
Я не знаю Clojure, но похоже, что, подобно Схеме, он поддерживает "let loops":
(loop [char (readChar)]
(if (= char delimiter)
'()
(do (some-processing)
(recur (readChar)))))
Надеюсь, этого достаточно, чтобы вы начали. Я ответил на http://clojure.org/special_forms#toc9, чтобы ответить на этот вопрос.
ПРИМЕЧАНИЕ. Я знаю, что Clojure предотвращает побочные эффекты, поэтому предположительно вы хотите вернуть что-то полезное вместо '().
Ответ 2
Работа над Clojure 1.3.0, и для чего это стоит, вы можете писать while в циклах в Clojure сейчас, делая что-то похожее на
(while truth-expression
(call-some-function))
Ответ 3
Подход цикла будет отлично работать в clojure, однако loop/recur считаются низкоуровневыми операциями и обычно предпочтительными функциями более высокого порядка.
Обычно такая проблема будет решена путем создания последовательности токенов (символов в вашем примере) и применения на или более функций последовательности clojure (доза, дорун, взятие и т.д.).
В следующем примере читается первое имя пользователя из /etc/passwd для unix-подобных систем.
(require '[clojure.java [io :as io]])
(defn char-seq
"create a lazy sequence of characters from an input stream"
[i-stream]
(map char
(take-while
(partial not= -1)
(repeatedly #(.read i-stream)))))
;; process the sequence one token at a time
;; with-open will automatically close the stream for us
(with-open [is (io/input-stream "/etc/passwd")]
(doseq [c (take-while (partial not= \:) (char-seq is))]
;; your processing is done here
(prn c)))
Ответ 4
Я придумал это в духе line-seq
. Он полностью ленив и обладает большей функциональностью Clojure, чем loop
.
(defn delim-seq
([#^java.io.Reader rdr #^Character delim]
(delim-seq rdr delim (StringBuilder.)))
([#^java.io.Reader rdr #^Character delim #^StringBuilder buf]
(lazy-seq
(let [ch (.read rdr)]
(when-not (= ch -1)
(if (= (char ch) delim)
(cons (str buf) (delim-seq rdr delim))
(delim-seq rdr delim (doto buf (.append (char ch))))))))))
Полная вставка.
Ответ 5
A while-loop обычно включает переменные переменные, т.е. до тех пор, пока переменная не встретит определенное условие; в Clojure вы обычно используете хвостовую рекурсию (которую компилятор переводит в цикл while)
Следующее не совсем решение, но эта вариация цикла for может быть полезной в некоторых случаях:
(for [a (range 100)
b (range 100)
:while (< (* a b) 1000)]
[a b]
)
Это создаст список всех пар a и b до (< (* a b) 1000)
. То есть он прекратится, как только условие будет выполнено. Если вы замените: while: when, они могут найти все пары, которые удовлетворяют этому условию, даже после того, как он найдет тот, который этого не делает.
Ответ 6
Я вышел с этой версией:
(defn read-until
[^java.io.Reader rdr ^String delim]
(let [^java.lang.StringBuilder salida (StringBuilder.) ]
(while
(not (.endsWith (.toString salida) delim))
(.append salida (str (char (.read rdr))))
)
(.toString salida)
)
)
Он ищет строку, а не один char как разделитель!
Спасибо!