Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/andreaferretti/memo
Memoization for Nim
https://github.com/andreaferretti/memo
Last synced: 25 days ago
JSON representation
Memoization for Nim
- Host: GitHub
- URL: https://github.com/andreaferretti/memo
- Owner: andreaferretti
- Created: 2015-06-25T18:28:21.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2022-02-25T11:14:12.000Z (over 2 years ago)
- Last Synced: 2024-08-04T03:05:19.711Z (3 months ago)
- Language: Nim
- Size: 15.6 KB
- Stars: 77
- Watchers: 5
- Forks: 10
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- awesome-nim - memo - Memoization for Nim. (Language Features / Macros)
README
Memoize Nim functions
=====================[![Build Status](https://travis-ci.org/andreaferretti/memo.svg?branch=master)](https://travis-ci.org/andreaferretti/memo)
[![nimble](https://raw.githubusercontent.com/yglukhov/nimble-tag/master/nimble_js.png)](https://github.com/yglukhov/nimble-tag)This small package offers a function and a macro to memoize Nim functions.
Usage
-----If `f(a: A): B` is a function, one can obtain a memoized version of `f` by doing
```nim
import memo
let g = memoize(f)
````g` will then be equivalent to `f` (modulo side effects), but results of calling `g`
will be cached. The function `memoize` can be used on any function, but will not
handle correctly recursive functions, as self calls of `f`, both direct and indirect,
will still keep refering to the non-memoize version of `f`.If you have access to the definition of `f`, one can do better with the `memoized`
macro. Usage is as follows:```nim
import memo
proc f(a: A): B {.memoized.} =
...
```Then `f` will be memoized and recursive calls will be handled correctly (both
direct self-recursion and mutual recursion).Example
-------```nim
import memoproc fib(n : int) : int {.memoized.} =
if n < 2: n
else: fib(n-1) + fib(n-2)when isMainModule:
echo fib(40)
```This small program returns very fast, while without the `memoized` pragma, it takes
a few seconds before producing a result. For an example of mutual recursive functions```nim
import memoproc fib(n : int) : int
proc fib1(n : int) : int {.memoized.} =
if n < 2: n
else: fib(n-1) + fib(n-2)proc fib(n : int) : int {.memoized.} =
if n < 2: n
else: fib1(n-1) + fib1(n-2)when isMainModule:
echo fib(80)
```Restrictions
------------* `memoize` function, as opposed to `memoized` macro, can only memoize functions
of a single argument, altough one can convert any function in this form by using
a tuple argument
* types of all arguments have to implement ``hash``, since they will be used as
parts of a key in a hashtableAn example of the first issue would be memoizing the Levenshtein distance for
strings, as it is a function of two arguments. It can be done like this:```nim
import memotemplate tail(s: string): string = s[1 .. s.high]
template head(s: string): char = s[0]
# `memoized` macro handles multiple arguments:
proc lev(a: string, b: string): int {.memoized.} =
if a.len == 0: return b.len
if b.len == 0: return a.len
let
d1 = lev(a.tail, b) + 1
d2 = lev(a, b.tail) + 1
d3 = lev(a.tail, b.tail) + (if a.head == b.head: 0 else: 1)
result = min(min(d1, d2), d3)# `memoize` function does not:
template memTwoArg =
let levMem: proc(int): int = memoize(lev)
assert: not compiles memTwoArgwhen isMainModule:
echo levenshtein("submarine", "subreddit")
```Resetting the cache
-------------------The `{.memoized.}` macro also generates a function that can be used to reset the
cache where previous results are stored. If `name` is the name of the function,
the auxiliary function to reset the cache is called `resetCacheName`.Thus, you can do the following
```nim
proc fib(n : int) : int {.memoized.} =
if n < 2: n
else: fib(n-1) + fib(n-2)echo fib(40)
resetCacheFib()
echo fib(50)
```This allows to avoid memory leaks by accumulating too many values in the cache.