Reputation: 10781
I want to be able to loop through a collection but maintain a counter with that loop. I managed to create e.g.
(for [[email i] (map vector emails (range))]
...)
to do this, but ideally I'd prefer a fori
macro (unless a function is possible but my gut says it's gotta be a macro) that lets me do equivalently
(fori [email i emails]
...)
or
(fori [[email i] emails]
...)
I've no experience with macros, and looking at the source for the for
macro it looks pretty intimidating. Any help?
Super-cool would be a macro that follows the for
syntax and allows e.g.
(fori [email i emails
line j (lines email)]
...)
Upvotes: 0
Views: 118
Reputation: 92057
You can write a macro that expands to for
, but I'm not really convinced there's much point. There are numerous built-ins for exactly this purpose, and the fori
syntax doesn't really read very well. At the very least it should look like
(fori [[email i] emails,
[line j] (lines email)]
...)
But if you really wanted to write it exactly as you said, it's certainly not impossible:
(defmacro fori [bindings body]
(letfn [(clauses [bindings]
(lazy-seq
(cond (empty? bindings) ()
(keyword? (first bindings)) (concat (take 2 bindings)
(clauses (drop 2 bindings)))
:else (let [[name index value] (take 3 bindings)]
(list* [index name] `(map-indexed vector ~value)
(clauses (drop 3 bindings)))))))]
`(for [~@(clauses bindings)]
~body)))
user> (macroexpand-1 '(fori [email i xs,
:when (even? i)
line j (lines email)]
[i line]))
(for [[i email] (map-indexed vector xs)
:when (even? i)
[j line] (map-indexed vector (lines email))]
[i line])
Upvotes: 2
Reputation: 13079
(defn fori [coll]
(map-indexed (fn [idx itm] [idx itm]) coll))
Is probably what you want. e.g.
(fori [:a :b :c :d])
produces
([0 :a] [1 :b] [2 :c] [3 :d])
BTW, for is not a loop but creates a list comprehension. If you're looking to do something with side effects inside a loop you should look into doseq.
Upvotes: 0