Representation of your array actually depends on you needs of using it, not initializing it. For example, if you have dense matrix, you most probably should use vector of vectors like this:
[[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[9, 8, 7, 6, 5],
[4, 3, 2, 1, 0],
[0, 1, 2, 3, 4]]
or single vector with some additional info on raw length:
{:length 5
:data
[0, 1, 2, 3, 4,
5, 6, 7, 8, 9,
9, 8, 7, 6, 5,
4, 3, 2, 1, 0,
0, 1, 2, 3, 4]
}
and if you need sparse matrix, you can use hash-maps:
{0 {0 0, 4 4},
2 {2 7},
3 {0 4, 2 2}}
(since your 2D array is small and you generate next value based on previous one, I believe first option is better suited for you).
If you are going to make a lot of matrix-specific manipulations (multiplication, decomposition, etc) you may want to use some existing libraries like Incanter.
And as for filling, my proposal is to use transients and store interim results, i.e. (for one-dimensional vector):
(defn make-array [initial-value f length]
(loop [result (transient []), length-left length, interim-value initial-value]
(if (= length-left 0)
(persistent! result)
(recur (conj! result (f interim-value)) (- length-left 1) (f interim-value))))
Transients will avoid creating new data structure on each new element, and interim value will avoid need in reading previous element from transient structure.