Clojure While Loop

14,231

Solution 1

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

Solution 2

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

Solution 3

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

Solution 4

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.

Solution 5

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.

Share:
14,231
Hamza Yerlikaya
Author by

Hamza Yerlikaya

Updated on June 02, 2022

Comments

  • Hamza Yerlikaya
    Hamza Yerlikaya almost 2 years

    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
    
  • Kzqai
    Kzqai over 14 years
    Gotta be a shorter way 'ta do this.
  • Michiel Borkent
    Michiel Borkent about 13 years
    Right about the side effect. Maybe add an accumulator to the loop to show how a result is built functionally?
  • tenpn
    tenpn over 8 years
    this relies on some side effects to make truth-expression eventually go false.