В Clojure Цикл While
Я пытаюсь clojure я пытаюсь выяснить, как реализовать следующий алгоритм,
Я читаю из входного потока, который я хочу продолжить чтение, пока он не станет символом разделителя.
Я могу сделать это на java с циклом while, но я не могу понять, как это сделать в clojure?
while read readChar != delimiter do some processing.... end while
6 ответов
Я не знаю Clojure, но похоже, что, как и Scheme, он поддерживает "let loops":
(loop [char (readChar)]
(if (= char delimiter)
'()
(do (some-processing)
(recur (readChar)))))
надеюсь, этого достаточно, чтобы вы начали. Я ссылался на http://clojure.org/special_forms#toc9 чтобы ответить на этот вопрос.
примечание: Я знаю, что Clojure препятствует побочным эффектам, поэтому, предположительно, вы хотите вернуть что-то полезное вместо '().
работа над Clojure 1.3.0, и для чего это стоит, вы можете писать в то время как петли в Clojure теперь, делая что-то сродни
(while truth-expression
(call-some-function))
подход цикла будет отлично работать в clojure, однако цикл / повторение считаются операциями низкого уровня и обычно предпочтительными функциями более высокого порядка.
обычно такая проблема решается путем создания последовательности токенов (символов в вашем примере) и применения К или более функциям последовательности clojure (doseq, dorun, take-while и т. д.)
в следующем примере читается первое имя пользователя из /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)))
Я придумал это в духе 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))))))))))
цикл while обычно включает изменяемые переменные, т. е. ожидание, пока переменная не удовлетворит определенному условию; в Clojure вы обычно используете хвостовую рекурсию (которую компилятор переводит в цикл while)
следующее Не совсем решение, но этот вариант for-loop может помочь в некоторых случаях:
(for [a (range 100)
b (range 100)
:while (< (* a b) 1000)]
[a b]
)
это создаст список всех пар a и b до (< (* a b) 1000)
. То есть он прекратится, как только будет выполнено условие. Если вы замените: while с :when, они вы можете найти все из пар, которые отвечают условию, даже после того, как он находит тот, который этого не делает.
Я вышел с этой версии:
(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)
)
)
он ищет строку, а не один символ в качестве разделителя!
спасибо!