[clean-list] Memoization of recursive functions using Clean arrays
Maks Verver
maksverver at geocities.com
Wed Aug 22 20:44:13 MEST 2012
On Tue, 21 Aug 2012 13:16:51 +0200
Pieter Koopman <pieter at cs.ru.nl> wrote:
> 1) Most likely you have to recompile _SystemArray with the -ci flag
> (throw away the .o object file).
That's it. For future reference, I did this:
make -C path-to-clean-dir CLMFLAGS+=-ci cleanup install
> 2) For instance
> memoize :: ((Int -> e) -> (Int -> e)) Int -> (Int -> e)
> memoize f max = select (memo f max)
> where
> memo :: ((Int -> e) -> (Int -> e)) Int -> {e}
> memo f max = array
> where array = { f (select array) i \\ i <- [0..max] }
I see. That's a useful trick, though in this case you've reshuffled
the definitions considerably.
In this case I can also fix my problem by introducing a function to
restrict the kind of array used:
lazyArray :: u:{a} -> u:{a}
lazyArray a = a
And then stay relatively close to the original, simpler definition:
memoize :: ((Int -> a) -> Int -> a) Int -> (Int -> a)
memoize f max = g
where g = select (lazyArray {f g i \\ i <- [0..max]})
> The standard solution is to add an argument to fix the the type.
I can't imagine that's always a nice solution, considering that adding
an argument to a constant expression apparently affects the efficiency
of the resulting program.
> 3) It is essential to define the memo object as a local constant,
> that is a definition without arguments. In that way it will become a
> local graph which is shared in all compuations. Each local definition
> with arguments is a function. Functions are lifted to the global
> level by the compiler an evaluated for each argument all over again.
> This is exactly what you try to avoid.
This sounds reasonable but I still have trouble applying this to
concrete programs. For example, it doesn't explain why this is fast,
even though I use a lambda-closure:
memoize :: ((Int -> Int) -> Int -> Int) Int -> (Int -> Int)
memoize f max = g
where
g = \i -> memo.[i]
memo =: lazyArray { f g i \\ i <- [0..max] }
... while adding a type to memo makes it slow:
memoize :: ((Int -> Int) -> Int -> Int) Int -> (Int -> Int)
memoize f max = g
where
g = \i -> memo.[i]
memo :: {Int} // <-- only added this
memo =: lazyArray { f g i \\ i <- [0..max] }
What is the type inferred by the compiler in the first case that would
result in a completely different kind of evaluation?
Or does adding a type declaration turn `memo' from a graph into a
(constant) function? In that case, shouldn't the compiler reject my
use of `=:' instead of `='?
- Maks.
More information about the clean-list
mailing list