Reputation: 1813
It is easy to apply multiple nested for loop inside a while loop in Python. However, it is not the case in Clojure. For my understanding, "for", "while" or "loop"; all of them are macro. Please comment how to write the looping with idiomatic Clojure code ?
n = 1
while n < 10:
for i in range(10):
print("for loop 1")
for j in range(10):
print("for loop 2")
print("back to loop")
for j in range(10):
print("loop3")
print("back to loop again")
n += 1
for loop does not execute, how to write idiomatic Clojure code?
(defn Example []
(def n (atom 1))
(while (< @n 10)
(do
(println @n)
;; for loop does not execute
(for [i (range 10)]
(println "for loop 1")
;; for loop does not execute
(for [j (range 10)] (println "for loop 2")))
(println "back to loop")
;; for loop does not execute
(for [j (range 10)] (println "for loop 3"))
(println "back to loop again")
(swap! n inc))))
(Example)
Upvotes: 2
Views: 173
Reputation: 29958
Here is an example:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.pprint :refer [pprint]]))
(def n (atom 0))
(defn Example
[]
(while (< @n 2)
(newline)
(println "n:" @n)
; `for` is lazy, need to force result with `vec`
(let [nested-result (vec
(for [i (range 3)]
(do
(println "i=" i)
(for [j (range 5)] ; lazy execution since no `vec`
(do
(println "j=" j)
(+ (* 10 i) j))))))] ; result of nested loops
(println :nested-result)
(pprint nested-result))
(println "back to while")
; for loop does not execute
(doseq [k (range 4)]
(println "doseq k=" k))
(println "back to loop again")
(swap! n inc)))
(dotest
(Example))
with result:
--------------------------------------
Clojure 1.10.2-alpha1 Java 15
--------------------------------------
Testing tst.demo.core
n: 0
i= 0
i= 1
i= 2
:nested-result
[j= 0
j= 1
j= 2
j= 3
j= 4
(0 1 2 3 4) j= 0
j= 1
j= 2
j= 3
j= 4
(10 11 12 13 14) j= 0
j= 1
j= 2
j= 3
j= 4
(20 21 22 23 24)]
back to while
doseq k= 0
doseq k= 1
doseq k= 2
doseq k= 3
back to loop again
n: 1
i= 0
i= 1
i= 2
:nested-result
[j= 0
j= 1
j= 2
j= 3
j= 4
(0 1 2 3 4) j= 0
j= 1
j= 2
j= 3
j= 4
(10 11 12 13 14) j= 0
j= 1
j= 2
j= 3
j= 4
(20 21 22 23 24)]
back to while
doseq k= 0
doseq k= 1
doseq k= 2
doseq k= 3
back to loop again
As @xificurC says, use doseq
for printing or other side effects (or dotimes
). A for
expression is intended to return a result sequence. Wrap it in a vec
to avoid the default lazy behavior.
See this list of documentation, especially "Getting Clojure" and the Clojure CheatSheet. The template project makes it easy to run the code if you want to try it.
Upvotes: 2
Reputation: 1281
The Clojure documentation addresses this matter in a section on "Clojure's for
" at https://clojure.org/guides/learn/flow
Upvotes: 2
Reputation: 1178
for
is lazy. If you are playing with side effects you need to replace him with his cousin doseq
.
Some notes on your code:
do
inside a while
loop is redundant, omit it.while
loop in both languages isn't really idiomatic. In python it would be another for
and in clojure dotimes
.(doseq [i (range 10)] ..)
could also be just (dotimes [i 10] ..)
Upvotes: 3