33

I'd like to implement this little code in Clojure, but I am struggling:

struct mystruct {
   int id;
   int price;
};

mystruct mydata[10];

for (int i=0; i<10; i++) {
  myfunction(mydata[i].id, mydata[i].price);
  //other things...
}

I am a beginner with Clojure and it's really complicated for me to do something simple like this, but I am really trying to learn as much as possible as I know that there are great advantages with Clojure such as using refs...

I would really appreciate it if somebody could help me. Thanks!!

1
  • Usually in the functional realm it's best to stick to map, apply, and reduce. Map has an implied for-loop ^.^ Commented Feb 13, 2021 at 0:41

3 Answers 3

44

One way to translate an imperative for loop to Clojure is to use the for macro.

(for [i (range 10)] (inc i))

The above function will return all the numbers from 0 to 9 incremented by 1. However, it appears you simply want to iterate over a sequential collection and use each item. If that's all that you need, then you don't need to reference an index value, instead you can reference each item directly.

(for [d my-vec-of-data] (my-function d))

However, for this simple case, the map function would probably be a better choice because it is designed to invoke functions with arguments from collections. The following example is equivalent to the use of for above.

(map my-function my-vec-of-data)

Both map and for return a collection of values made up of the values returned by my-function. This is because Clojure's data structures are immutable, so it's necessary to have a new collection returned. If that isn't what you need or if your function has side effects, you could use doseq instead of for, which returns nil.

Sign up to request clarification or add additional context in comments.

4 Comments

does 'i' increment automatically?
@nuvio: i will be the actual item in the sequence, not the index. And yes, the for macro will handle iterating the sequence for you.
"doseq" is great for functions where you don't need a return val because it returns a single nil instead of 1-per-element
@Chuck how can you get the actual count, since i is the actual item?
39

Jeremy's answer is good for how to do a for loop in idiomatic Clojure.

If you really want an imperative-style for loop in Clojure, you can create one with this macro:

(defmacro for-loop [[sym init check change :as params] & steps]
 `(loop [~sym ~init value# nil]
    (if ~check
      (let [new-value# (do ~@steps)]
        (recur ~change new-value#))
      value#)))

Usage as follows:

(for-loop [i 0 (< i 10) (inc i)] 
  (println i))

1 Comment

I love how in Clojure if your missing a language feature, instead of waiting for it to be added to a future version, you can just write your own.
2

doseq does something similar to a for-loop USAGE:

(doseq [i (for [i (range 10)] (inc i))]
    (println "i=" i))

The binding is similar to that of for in Clojure. However, it does not return a list by evaluating an expression inside the doseq. It performs the expression for each value in the sequence and returns nil.

To loop through a seq, you can simply use:

(doseq [value list]
    (println "Your expression here" value)

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.