2

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)
1
  • 2
    Only ever do def on namespace level. Use let instead. And even better don't use atoms to accumulate some state - most likely loop/recur is what you are after here. Commented Dec 17, 2020 at 14:39

3 Answers 3

3

for is lazy. If you are playing with side effects you need to replace him with his cousin doseq.

Some notes on your code:

  • a do inside a while loop is redundant, omit it.
  • the while loop in both languages isn't really idiomatic. In python it would be another for and in clojure dotimes.
  • the (doseq [i (range 10)] ..) could also be just (dotimes [i 10] ..)
Sign up to request clarification or add additional context in comments.

Comments

2

The Clojure documentation addresses this matter in a section on "Clojure's for" at https://clojure.org/guides/learn/flow

Comments

2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.