madeinQuant
madeinQuant

Reputation: 1813

How to apply nested for loop inside a while loop in Clojure?

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

Answers (3)

Alan Thompson
Alan Thompson

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

Biped Phill
Biped Phill

Reputation: 1281

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

Upvotes: 2

xificurC
xificurC

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:

  • 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] ..)

Upvotes: 3

Related Questions