2
votes

I am trying to learn the programming language Clojure. I have a hard time understanding it though. I would like to try to implement something as simple as this (for example):

int[] array = new int[10];
array[0] =1; 
int n = 10; 

for(int i = 0; i <= n; i++){
    array[i] = 0; 
}
return array[n]; 

Since I am new to Clojure, I don't even understand how to implement this. It would be super helpful if someone could explain or give a similar example of how arrays/for-loops works in Clojure. I've tried to do some research, but as for my understanding Clojure doesn't really have for-loops.

4
The code you want to translate is very strange. It contains a number of off-by-one errors that would result in a runtime error, attempting to read and write array[10], which is out of bounds. Further, it initializes a whole array but then throw it away and return a single element. If you want to translate code, it will be easier if the code you are translating has clear features.amalloy

4 Answers

9
votes

The Clojure way to write the Java for loop you wrote is to do consider why you're looping in the first place. There are many options for porting a Java loop to Clojure. Choosing among them depends on what your goal is.

Ways to Make Ten Zeroes

As Carcigenicate posted, if you need ten zeros in a collection, consider:

(repeat 10 0)

That returns a sequence – a lazy one. Sequences are one of Clojure's central abstractions. If instead the ten zeros need to be accessible by index, put them in a vector with:

(vec (repeat 10 0))

or

(into [] (repeat 10 0))

Or, you could just write the vector literal directly in your code:

[0 0 0 0 0 0 0 0 0 0]

And if you specifically need a Java array for some reason, then you can do that with to-array:

(to-array (repeat 10 0))

But remember the advice from the Clojure reference docs on Java interop:

Clojure supports the creation, reading and modification of Java arrays [but] it is recommended that you limit use of arrays to interop with Java libraries that require them as arguments or use them as return values.

Those docs list some functions for working with Java arrays primarily when they're required for Java interop or "to support mutation or higher performance operations". In nearly all cases Clojurists just use a vector.

Looping in Clojure

What if you're doing something other than producing ten zeros? The Clojure way to "loop" depends on what you need.

You may need recursion, for which Clojure has loop/recur:

(loop [x 10]
  (when-not (= x 0)
    (recur (- x 2))))

You may need to calculate a value for every value in some collection(s):

(for [x coll])
  (calculate-y x))

You might need to iterate over multiple collections, similar to nested loops in Java:

(for [x ['a 'b 'c] 
      y [1 2 3]]
  (foo x y))

If you just need to produce a side effect some number of times, repeatedly is your jam:

(repeatedly 10 some-fn)

If you need to produce a side effect for each value in a collection, try doseq:

(doseq [x coll]
  (do-some-side-effect! x))

If you need to produce a side effect for a range of integers, you could use doseq like this:

(doseq [x (range 10)] 
  (do-something! x))

...but dotimes is like doseq with a built-in range:

(dotimes [x 9] 
  (do-something! x))

Even more common than those loopy constructs are Clojure functions which produce a value for every element in a collection, such as map and its relatives, or which iterate over collections either for special purposes (like filter and remove) or to create some new value or collection (like reduce).

7
votes

I think that last point in @Lee's answer should be heavily emphasized.

Unless you absolutely need arrays for performance reasons, you should really be using vectors here. That entire chunk of code suddenly becomes trivial once you start using native Clojure structures:

; Create 10 0s, then put them in a vector
(vec (repeat 10 0))

You can even skip the call to vec if you're ok using a lazy sequence instead of a strict vector.

It should also be noted though that pre-initializing the elements to 0 is unnecessary. Unlike arrays, vectors are variable length; they can be added to and expanded after creation. It's much cleaner to just have an empty vector and add elements to it as needed.

3
votes

You can create and modify arrays in clojure, your example would look like:

(defn example []
  (let [arr (int-array 10)
        n 10]
    (aset arr 0 1)
    (doseq [i (range (inc n))]
      (aset arr i 0))
    (aget arr n)))

Be aware that this example will throw an exception since array[i] is out of bounds when i = 10.

You will usually use vectors in preference to arrays since they are immutable.