On Sep 19, 5:49 am, Mark McGranaghan <[EMAIL PROTECTED]> wrote:
> Hi all,
>
> I was wondering if there was an idiomatic functional way to accomplish
> the following:
>
> We have some parameters, and we want to build a collection based on
> those parameters.  The collection could have as many as n items, and
> whether and what items get added for each of the n possibilities will
> depend on the parameters.
>
> In Ruby, this might look like:
> def build_coll(a, b)
>   coll = []
>   if a > 2
>     coll << (a * b)
>   end
>   if b > 2
>     coll << (a + b)
>   end
>   if a > b
>     coll << (a - b)
>   end
>   coll
> end
>
> build_coll(3, 1)
>
> I'd like to accomplish this using a purely functional style.  One way
> to do this would be to reduce a collection of test functions with an
> initial empty collection and a reduce function that adds the result of
> the test element if the test passes:
>
> (defn build-coll [a b]
>   (reduce
>     (fn [coll f] (if-let result (f a b) (conj coll result) coll))
>     []
>     [(fn [a b] (if (> a 2) (* a b)))
>      (fn [a b] (if (> b 2) (+ a b)))
>      (fn [a b] (if (> a b) (- a b)))]))
>
> (build-coll 3 1)
>
> Does anyone have any better ideas about how to go about this?

Hi Mark,

Not sure if this is better but here is another way to do it.

    (defn simple-build-coll [a b]
      (vec
        (filter #(not (nil? %))
                (list
                  (when (> a 2) (* a b))
                  (when (> b 2) (+ a b))
                  (when (> a b) (- a b))))))

    user=> (simple-build-coll 3 1)
    [3 2]

The vec is just to convert the collection to a vector if
thats whats being used.

If this is commonly needed in the program I would probably write
a macro to do this. Its would make the program much more
readable .. not the macro but the usage of it :)

    (defn reverse-pairs [l]
      (apply concat (reverse (partition 2 l))))

    (defmacro conj-if-aux [coll & preds_xs]
      (if preds_xs
        `(if ~(first preds_xs)
           (conj (conj-if-aux ~coll ~@(rest (rest preds_xs))) ~(second
preds_xs))
           (conj-if-aux ~coll ~@(rest (rest preds_xs))))
        coll))

    (defmacro conj-if [coll & preds_xs]
      `(conj-if-aux ~coll ~@(reverse-pairs preds_xs)))

The main logic is in conj-if-aux.
I use reverse-pairs and conj-if just to maintain the
correct order in the resulting collection (for lists and vectors).
If thats not important conj-if-aux should suffice.

reverse-pairs maintains the pairs of predicate and exprs
in the reversed list.
    user=> (reverse-pairs [:a :b :c :d :e :f])
    (:e :f :c :d :a :b)

Usage would be:

    (defn mac-build-coll [a b]
      (conj-if [9 10]
               (> a 2) (* a b)
               (> b 2) (+ a b)
               (> a b) (- a b)))

    (defn mac-build-coll2 [a b]
      (conj-if '(9 10)
               (> a 2) (* a b)
               (> b 2) (+ a b)
               (> a b) (- a b)))

    user=> (mac-build-coll 3 1)
    [9 10 3 2]
    user=> (mac-build-coll2 3 1)
    (2 3 9 10)
    user=>

Note that (1) the type of collection is maintained by
conj here (either a list or a vector) and (2) the order in
which elements are added depend on the the type of collection.
Conj appends to head of list.
9,10 are used for the initial collection (either vector or list).

Parth



>
> Thanks,
> - Mark McGranaghan
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to