views:

115

answers:

5

Hi,

I'm looking for a clean, idiomatic way to do a "backwards reduce" in Clojure.

I've got

(def fns '(fn1 fn2 fn3))
(def val 42)

I'd like to obtain (fn3 (fn2 (fn1 val))), and I'm not picky about the order. So I'd like to consecutively apply a sequence of functions to a value, rather than consecutively a sequence of values to a function.

Suggestions? Thanks!

+3  A: 

I'm not a very advanced Clojure user either, so the language of the API description tends to look a bit terse and cryptic to me. But given my limited understanding, the "->" macro struck my eye. It "threads" a value x through a series of "forms," which looks to me suspiciously similar to what you're after.

Since it's a macro, I'm not sure if you can use (apply) to push it inside your list of fn's, but I'd give it a shot!

Carl Smotricz
My sources of wisdom: http://clojure.org/api#toc35 and http://clojure.org/cheatsheet.
Carl Smotricz
Since it's a macro you would have to define another macro to use apply on it.
cobbal
(defmacro back-thread [val, fns] ‘(-> ~val ~@fns))
cobbal
+1  A: 

how about ...

(defn back-reduce
([firstfunction restfunctions val]
    (if (= restfunctions nil)
        (firstfunction val)
        (back-reduce (car restfunctions) (cdr restfunctions) (firstfunction val))  
    )
))
yankee2905
+6  A: 

This is just reduce. Remember functions are objects too. Also, you are still applying your functions in order f1, then f2, etc. Don't be confused with the fact that it reads (f4 (f3 ...

So just use a regular reduce.

(reduce #(%2 %1) val functions)
z5h
Thanks very much! BTW, I noticed that (def fns '(fn1 fn2 fn3)) doesn't work here, it's necessary to use (def fns [#'fn1 #'fn2 #'fn3])
Dan
works! brilliant!(reduce #(%2 %1) 42 [inc dec inc]) => 43
Arthur Ulfeldt
caffeine.cc: A quoted list gives you symbols rather than the values (functions) the symbols evaluate to; that's the whole point of quoting something. You could've done `(list fn1 fn2 fn3)` or `[fn1 fn2 fn3]`; the vector version is more idiomatic in Clojure.
Brian Carper
+4  A: 

Yet another way to think about it is as a composition of functions:

user=> ((apply comp [inc dec inc]) 42)
43
Timothy Pratley
I like this solution, but I think you will have to reverse the collection of functions. `((comp f1 f2 f3) x)` evaluates to `(f1 (f2 (f3 x)))` and the asker wanted it the other way around.
Jonas
Quite right. The question did state: "I'm not picky about the order", which I took to mean either they were commutative or could be arranged either way. However for some functions of course, order is very important ((apply comp [/ +]) 1 2) -> 1/3 ;;; ((apply comp (reverse [/ +])) 1 2) -> 1/2
Timothy Pratley
I really like this solution too, it wound up in my code. Thanks!
Dan
+1  A: 

Simplest way to write it down in Clojure would be:

((comp fn3 fn2 fn1) 42)
Michiel Borkent