Search code examples
clojureclojure-contrib

Building tables in clojure


If I wanted to build a table in Clojure of vector duplicates, I'd write:

(take 2 (repeat [1 2 3]))

But how would I expand this notion of a table function to build something like:

Input 1: [a^2 2 6 2] where a^2 is some input function, 2 is min value, 6 is max value, and 2 is step size.

Output 1: [4,16,36]

Input 2: [b^2 10 -5 -2]

Output 2: [100 64 36 16 4 0 4 16]

This outputs a 4x3 matrix

Input 3: [(+ (* 10 i) j) [1 4] [1 3]]

where (+ (* 10 i) j) is 10i+j (some given input function), [1 4] is the min and max of i, and [1 3] is the min and max of j.

Output 3: [[11 12 13] [21 22 23] [31 32 33] [41 42 43]]


Solution

  • You want to use for in a nested fashion:

    (for [i (range 1 (inc 4))]
      (for [j (range 1 (inc 3))]
        (+ (* 10 i) j)))
    ;; '((11 12 13) (21 22 23) (31 32 33) (41 42 43))
    

    EDIT: expanded with an example implementation

    For example:

    (defn build-seq [f lower upper step]
      (for [i (range lower (+ upper step) step)]
        (f i)))
    
    (build-seq #(* % %) 2 6 2)
    ;; '(4 16 36)
    
    (defn build-table [f [ilower iupper] [jlower jupper]]
      (for [i (range ilower (inc iupper))]
        (for [j (range jlower (inc jupper))]
          (f i j))))
    
    (build-table #(+ (* 10 %) %2) [1 4] [1 3])
    ;; '((11 12 13) (21 22 23) (31 32 33) (41 42 43))
    

    Your three input/output samples do not display a consistent signature for one variable and two ; furthermore, the step argument seems to be optional. I'm skeptical about the existence of a nice API that would retain the samples' syntax, but I can try something different (even if I do believe the simple embedded for forms are a better solution):

    (defn flexible-range [{:keys [lower upper step] :or {lower 0}}]
      (let [[upper step] (cond
                           (and upper step) [(+ upper step) step]
                           step  (if (pos? step)
                                   [Double/POSITIVE_INFINITY step]
                                   [Double/NEGATIVE-INFINITY step])
                           upper (if (< lower upper)
                                   [(inc upper)  1]
                                   [(dec upper) -1])
                           :else   [Double/POSITIVE_INFINITY 1])]
        (range lower upper step)))
    
    (defn build-table
      ([f [& params]]
        (for [i (flexible-range params)]
          (f i)))
      ([f [& iparams] [& jparams]]
        (for [i (flexible-range iparams)]
          (for [j (flexible-range jparams)]
            (f i j)))))
    
    (build-table #(* % %) [:lower 2 :upper 6 :step  2])
    ;; '(4 16 36)
    
    (build-table #(+ (* 10 %) %2) [:lower 1 :upper 4]
                                  [:lower 1 :upper 3])
    ;; '((11 12 13) (21 22 23) (31 32 33) (41 42 43))