Question

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
Was it helpful?

Solution

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

OTHER TIPS

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

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

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

Full paste.

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.

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!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top