Reputation: 49339
I trying clojure i am trying to figure out how to implement the following algorithm,
I am reading from an input stream i want to continue reading until it is not a delimiter character.
i can do this in java with a while loop but i can't seem to figure out how to do it in clojure?
while read readChar != delimiter do some processing.... end while
Upvotes: 11
Views: 14756
Reputation: 564
The loop approach will work fine in clojure however loop/recur are considered low level operations and higher order functions usually preferred.
Normally this sort of problem would be solved by creating a sequence of tokens (characters in your example) and applying on or more of clojure's sequence functions (doseq, dorun, take-while, etc.)
The following example reads the first username from /etc/passwd on unix like systems.
(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)))
Upvotes: 3
Reputation: 1
I came out with this version:
(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)
)
)
It looks for a String, not a single char as delimiter!
Thanks!
Upvotes: 0
Reputation: 18918
Working on Clojure 1.3.0, and for what it's worth, you can write while loops in Clojure now by doing something akin to
(while truth-expression
(call-some-function))
Upvotes: 6
Reputation: 7696
A while-loop usually involves mutable variables, i.e. waiting until a variable meets a certain condition; in Clojure you'd usually use tail-recursion (which the compiler translates into a while-loop)
The following is not quite a solution, but this variation of the for-loop might be of help in some cases:
(for [a (range 100)
b (range 100)
:while (< (* a b) 1000)]
[a b]
)
This will create a list of all the pairs of a and b until (< (* a b) 1000)
. That is it will stop as soon as the condition is met. If you replace :while with :when, they you can find all of the pairs that meet the condition, even after it finds one that doesn't.
Upvotes: 1
Reputation: 1796
I came up with this in the spirit of line-seq
. It's fully lazy and exhibits more of Clojure's functional nature than 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))))))))))
Upvotes: 2
Reputation: 18389
I don't know Clojure, but it looks that, like Scheme, it supports "let loops":
(loop [char (readChar)]
(if (= char delimiter)
'()
(do (some-processing)
(recur (readChar)))))
Hope this is enough to get you started. I referred to http://clojure.org/special_forms#toc9 to answer this question.
NOTE: I know that Clojure discourages side-effects, so presumably you want to return something useful instead of '().
Upvotes: 10