Ask HN: How to compose two mapping functions into a third?
I'd like a good way to do the following, where "good" means some balance of simple and efficient.
I have a bunch of mapping functions. Each takes an integer interval [START,END] and a function FN to call once for each integer N in the interval, passing two arguments: N and some value computed from N.
For example, say MAP1 works this way, passing N and N+10 to the function provided:
(defun map1 (fn start end)
(loop for n from start to end do
(funcall fn n (+ n 10))))
...so if you gave MAP1 a function that printed out its arguments, you'd get this: (map1 (lambda (n val) (format t "~a ~a~%" n val)) 1 3) =>
1 11
2 12
3 13
Now say MAP2 does the same, only instead of N+10 it passes N^2: (defun map2 (fn start end)
(loop for n from start to end do
(funcall fn n (expt n 2))))
(map2 (lambda (n val) (format t "~a ~a~%" n val)) 1 3) =>
1 1
2 4
3 9
What I want is a way to make a new mapping function MAP3 that works the same way but composes the computations in MAP1 and MAP2. For simplicity, say I just want to add the values computed by MAP1 and MAP2. Then MAP3 should do this: (map3 (lambda (n val) (format t "~a ~a~%" n val)) 1 3) =>
1 12
2 16
3 22
Any nice solutions? The rules are: (1) it's ok to modify the definition of "mapping function", as long as it's simple, and (2) I don't want to have to make a local copy of everything one of these functions does (because the intervals can be large).Edit: A solution in Common Lisp would be nice, but I'm interested in good approaches to this in general. Perhaps coroutines?
16 comments
[ 0.23 ms ] story [ 50.7 ms ] threadhttp://en.wikipedia.org/wiki/Deforestation_(computer_science)
It's only valid in a pure language, and in a strict language, it has the embarassing side-effect that it can sometimes make non-terminating programs terminate.
http://en.wikipedia.org/wiki/Deforestation_%28computer_scien...
And no, I don't know why this isn't mentioned in the FAQ or (better yet) the formatting options...
http://homepages.inf.ed.ac.uk/wadler/papers/deforest/defores...
(defun compmap (fn mapper1 mapper2 start end) (cond ((= start end) '()) (else (cons (fn start (+ (mapper1 start start) (mapper2 start start))) (compmap fn mapper1 mapper2 (+ start 1) end)))))
(loop for n from start to end do
then you will write the content of the first mapping as a function foo and call the general mapper and pass it foo to accomplish the same thing map1 does.
same with map2, except baz instead of foo.
then map3 is easy. call the general mapper again and pass in something like this: (lambda (x) (baz (foo x)))
or actually it looks like you wanted to add the results, so (lambda (x) (+ (baz x) (foo x)))
basically if you define the interesting part of map1 as one individual thing, and ditto for map2, then you can write the combination easily.
i hope that's clear and didn't miss the point somehow.
The reason I find this interesting is that it's the first real problem I've run into where CL didn't have an easy way to do what I want. There are workarounds, of course. But it's interesting to run into a production (as opposed to academic) problem where the elegant solution isn't simply available.
http://series.sourceforge.net
"A series is a data structure much like a sequence, with similar kinds of operations. The difference is that in many situations, operations on series may be composed functionally and yet execute iteratively, without the need to construct intermediate series values explicitly. In this manner, series provide both the clarity of a functional programming style and the efficiency of an iterative programming style."
http://portal.acm.org/citation.cfm?id=165214
(no offense)
Think of it in terms of doing all of your transformations in one pass, if that helps.
I guess I'll just add that it's essential that all of these be mapping functions (i.e. they take a functional argument that they call back over some range, passing a value computed using that range).