tags:

views:

65

answers:

2

I'm halfway through figuring out a solution to my question, but I have a feeling that it won't be very efficient. I've got a 2 dimensional cell structure of variable length arrays that is constructed in a very non-functional way in Matlab that I would like to convert to Clojure. Here is an example of what I'm trying to do:

pre = cell(N,1);
aux = cell(N,1);
for i=1:Ne
  for j=1:D
    for k=1:length(delays{i,j})
        pre{post(i, delays{i, j}(k))}(end+1) = N*(delays{i, j}(k)-1)+i;
        aux{post(i, delays{i, j}(k))}(end+1) = N*(D-1-j)+i; % takes into account delay
    end;
  end;
end;

My current plan for implementation is to use 3 loops where the first is initialized with a vector of N vectors of an empty vector. Each subloop is initialized by the previous loop. I define a separate function that takes the overall vector and the subindices and value and returns the vector with an updated subvector.

There's got to be a smarter way of doing this than using 3 loop/recurs. Possibly some reduce function that simplifies the syntax by using an accumulator.

+1  A: 

I'm not 100% sure I understand what your code is doing (I don't know Matlab) but this might be one approach for building a multi-dimensional vector:

(defn conj-in
  "Based on clojure.core/assoc-in, but with vectors instead of maps."
  [coll [k & ks] v]
  (if ks
    (assoc coll k (conj-in (get coll k []) ks v))
    (assoc coll k v)))

(defn foo []
  (let [w 5, h 4, d 3
        indices (for [i (range w)
                      j (range h)
                      k (range d)]
                  [i j k])]
    (reduce (fn [acc [i j k :as index]]
              (conj-in acc index
                       ;; do real work here
                       (str i j k)))
            [] indices)))

user> (pprint (foo))
[[["000" "001" "002"]
  ["010" "011" "012"]
  ["020" "021" "022"]
  ["030" "031" "032"]]
 [["100" "101" "102"]
  ["110" "111" "112"]
  ["120" "121" "122"]
  ["130" "131" "132"]]
 [["200" "201" "202"]
  ["210" "211" "212"]
  ["220" "221" "222"]
  ["230" "231" "232"]]
 [["300" "301" "302"]
  ["310" "311" "312"]
  ["320" "321" "322"]
  ["330" "331" "332"]]
 [["400" "401" "402"]
  ["410" "411" "412"]
  ["420" "421" "422"]
  ["430" "431" "432"]]]

This only works if indices go in the proper order (increasing), because you can't conj or assoc onto a vector anywhere other than one-past-the-end.

I also think it would be acceptable to use make-array and build your array via aset. This is why Clojure offers access to Java mutable arrays; some algorithms are much more elegant that way, and sometimes you need them for performance. You can always dump the data into Clojure vectors after you're done if you want to avoid leaking side-effects.

(I don't know which of this or the other version performs better.)

(defn bar []
  (let [w 5, h 4, d 3
        arr (make-array String w h d)]
    (doseq [i (range w)
            j (range h)
            k (range d)]
      (aset arr i j k (str i j k)))
    (vec (map #(vec (map vec %)) arr))))  ;yikes?
Brian Carper
I'm not sure if I'll use the aset or the more functional version...however I imagine that the functional array setting could be turned into a macro or function that simplifies things. Thanks.
Nick
+1  A: 

Look to Incanter project that provide routines for work with data sets, etc.

Alex Ott

related questions