Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure While Loop

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
like image 229
Hamza Yerlikaya Avatar asked Jun 28 '09 00:06

Hamza Yerlikaya


People also ask

Do while loops Clojure?

Following is the syntax of the 'while' statement. The while statement is executed by first evaluating the condition expression (a Boolean value), and if the result is true, then the statements in the while loop are executed. The process is repeated starting from the evaluation of the condition in the while statement.

Is Vector a Clojure?

Clojure collections "collect" values into compound values. There are four key Clojure collection types: vectors, lists, sets, and maps.

What is seq Clojure?

Clojure defines many algorithms in terms of sequences (seqs). A seq is a logical list, and unlike most Lisps where the list is represented by a concrete, 2-slot structure, Clojure uses the ISeq interface to allow many data structures to provide access to their elements as sequences.


4 Answers

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

like image 185
Nathan Shively-Sanders Avatar answered Oct 19 '22 03:10

Nathan Shively-Sanders


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))
like image 38
wrongusername Avatar answered Oct 19 '22 03:10

wrongusername


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)))
like image 45
user499049 Avatar answered Oct 19 '22 02:10

user499049


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.

like image 37
drewr Avatar answered Oct 19 '22 02:10

drewr