Here we present a special tree data type which is useful as memoizing data structure e.g. for the Fibonacci function.

+

Here we present a special tree data type

+

({{HackagePackage|id=data-inttrie}})

+

which is useful as memoizing data structure e.g. for the Fibonacci function.

<haskell>

<haskell>

memoizeInt :: (Int -> a) -> (Int -> a)

memoizeInt :: (Int -> a) -> (Int -> a)

Line 172:

Line 173:

wonderous 1 = 0

wonderous 1 = 0

wonderous x

wonderous x

−

| x `mod` 2 == 0 = 1 + wonderous (x `div` 2)

+

| even x = 1 + wonderous (x `div` 2)

−

| otherwise = 1 + wonderous (3*x+1)

+

| otherwise = 1 + wonderous (3*x+1)

</haskell>

</haskell>

This function is not at all understood by mathematicians and has a surprisingly complex recursion pattern, so if you need to call it many times with different values, optimising it would not be easy.

This function is not at all understood by mathematicians and has a surprisingly complex recursion pattern, so if you need to call it many times with different values, optimising it would not be easy.

Line 190:

Line 191:

wonderous2' 1 = 0

wonderous2' 1 = 0

wonderous2' x

wonderous2' x

−

| x `mod` 2 == 0 = 1 + wonderous2 (x `div` 2)

+

| even x = 1 + wonderous2 (x `div` 2)

−

| otherwise = 1 + wonderous2' (3*x+1)

+

| otherwise = 1 + wonderous2' (3*x+1)

</haskell>

</haskell>

When using this pattern in your own code, note carefully when to call the memoised version (wonderous2 in the above example) and when not to. In general, the partially memoised version (wonderous2' in the above example) should call the memoised version if it needs to perform a recursive call. However, in this instance, we only memoize for small values of x, so the branch of the recursion that passes a larger argument need not bother checking the memo table. (This does slow the array initialization, however.)

When using this pattern in your own code, note carefully when to call the memoised version (wonderous2 in the above example) and when not to. In general, the partially memoised version (wonderous2' in the above example) should call the memoised version if it needs to perform a recursive call. However, in this instance, we only memoize for small values of x, so the branch of the recursion that passes a larger argument need not bother checking the memo table. (This does slow the array initialization, however.)

−

Thanks to [[LazyEvaluation]], we can even memoise an infinite domain, though we lose constant time lookup. This data structure is O(log N):

+

Thanks to [[lazy evaluation]], we can even memoise an infinite domain, though we lose constant time lookup. This data structure is O(log N):

<haskell>

<haskell>

Line 232:

Line 233:

wonderous3' 1 = 0

wonderous3' 1 = 0

wonderous3' x

wonderous3' x

−

| x `mod` 2 == 0 = 1 + wonderous3 (x `div` 2)

+

| even x = 1 + wonderous3 (x `div` 2)

−

| otherwise = 1 + wonderous3 (3*x+1)

+

| otherwise = 1 + wonderous3 (3*x+1)

</haskell>

</haskell>

Revision as of 06:16, 19 January 2012

Memoization is a technique for storing values of a function instead of recomputing them each time the function is called.

When using this pattern in your own code, note carefully when to call the memoised version (wonderous2 in the above example) and when not to. In general, the partially memoised version (wonderous2' in the above example) should call the memoised version if it needs to perform a recursive call. However, in this instance, we only memoize for small values of x, so the branch of the recursion that passes a larger argument need not bother checking the memo table. (This does slow the array initialization, however.)
Thanks to lazy evaluation, we can even memoise an infinite domain, though we lose constant time lookup. This data structure is O(log N):

5 Memoizing polymorphic functions

What about memoizing polymorphic functions defined with polymorphic recursion?
How can such functions be memoized?
The caching data structures used in memoization typically handle only one type of argument at a time.
For instance, one can have finite maps of differing types, but each concrete finite map holds just one type of key and one type of value.