Reputation: 13
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
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
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
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