m2i3k2000
m2i3k2000

Reputation: 13

Avoid loop-recur while transforming a long sequence to a shorter one using a function that could consume an arbitrary number of elements

Say there is a list of tokens and a function that consumes an arbitrary number of tokens and returns a statement and the remaining tokens on each invocation. Extracting all statements till all the tokens are exhausted can be done using loop-recur like this:

(defn parse
  [code]
  (loop [tokens (lex code)
         statements []]
    (if (empty? tokens)
      statements
      (let [{:keys [remaining-toks statement]} (parse-statement tokens)]
        (recur remaining-toks (conj statements statement))))))

Is there any way to accomplish this without the loop-recur?

Upvotes: 1

Views: 128

Answers (3)

Steffan Westcott
Steffan Westcott

Reputation: 2201

The design notes on lazy-seq recommend putting the cons inside, which makes all items in the sequence lazily realised, not just the tail items:

(defn- parse-tokens [tokens]
  (lazy-seq
    (when (seq tokens)
      (let [{:keys [remaining-toks statement]} (parse-statement tokens)]
        (cons statement (parse-tokens remaining-toks))))))

(defn parse [code]
  (parse-tokens (lex code)))

Upvotes: 1

erdos
erdos

Reputation: 3538

Use lazy-seq to delay the evaluation of the tail of the output.

(defn- parse-tokens [tokens]
  (when (not-empty tokens)
    (let [{:keys [remaining-toks statement]} (parse-statement tokens)]
      (cons statement
            (lazy-seq (parse-tokens remaining-toks))))))

(defn parse [code]
  (parse-tokens (lex code)))

Alternatively with iterate (assuming that parse-statement returns nil for empty input):

(defn parse [code]
  (->> {:remaining-toks (lex code)}
       (iterate (comp parse-statement :remaining-toks))
       (next)
       (take-while some?)
       (map :statement)))

Upvotes: 3

Gwang-Jin Kim
Gwang-Jin Kim

Reputation: 9865

Just syntactically without loop ... recurse would be:

(defn parse-tokens [tokens & {:keys [statements] :or {statements []}}]
  (if (empty? tokens)
      statements
      (let [{:keys [remaining-toks statement]} (parse-statement tokens)]
        (parse-tokens remaining-toks (conj statements statement)))))

(defn parse [code] (parse-tokens (lex code)))

Upvotes: -2

Related Questions