zapashcanon
2 years ago
6 changed files with 184 additions and 162 deletions

2LICENSE.md

138README.md

3doc/dune

7doc/index.mld

117doc/usage.mld

79src/memo.ml
@ 1,7 +1,7 @@ 

The ISC License (ISC) 

===================== 



Copyright © 2019, Léo Andrès 

Copyright © 20192020, Léo Andrès 



Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 




@ 1,140 +1,14 @@ 

# Memo [![builds.sr.ht status](https://builds.sr.ht/~zapashcanon/memo.svg)](https://builds.sr.ht/~zapashcanon/memo?) 

# memo [![builds.sr.ht status](https://builds.sr.ht/~zapashcanon/memo.svg)](https://builds.sr.ht/~zapashcanon/memo?) 



Memo is an [OCaml] library for [memoïzation]. 

memo is an [OCaml] library for [memoïzation]. 



## Usage 



If you had a function `fibo` defined like this: 



```ocaml 

let rec fibo x = 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2) 

``` 



There's many different ways to memoïze it. 



### Simple memoïzation 



The easiest one is to rewrite it like this: 



```ocaml 

let fibo = Memo.memo (fun fibo x > 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2)) 

``` 



It'll use the `Hashtbl` module directly. 



I'd like to thank [Sylvain Conchon] who taught me memoïzation and how to write this `memo` function when I was his student. 



### Using you own type, `equal` and `hash` functions 



We provide a `Make` functor. It can be useful in case you don't want to use polymorphic equality or you are doing things like [hash consing] and you know how to compare or hash your type more efficiently. 



```ocaml 

let module Mem = Memo.Make(struct 

type t = int 

let equal = (=) 

let hash = Hashtbl.hash 

end) 



let fibo = Mem.memo (fun fibo x > 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2)) 

``` 



### Forgetful memoïzation 



We provide a `MakeWeak` functor. It works like the previous one, but the bindings in the memoïzation cache will be weak, allowing the garbage collector to remove them if they are not used somewhere else. 



```ocaml 

let module Mem = Memo.MakeWeak(struct 

type t = int 

let equal = (=) 

let hash = Hashtbl.hash 

end) 



let fibo = Mem.memo (fun fibo x > 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2)) 

``` 



I'd like to thank [JeanChristophe Filliâtre] who taugh me forgetful memoïzation when I was doing research on [binary decision diagram] under his direction while I was a first year master student. 



### Fake memoïzation 



We provide a `Fake` functor. It is useful if you want to quickly test a function you memoïzed with our `Make` or `MakeWeak` functor, but without memoïzing it. It'll basically do nothing and should be equivalent to your initial nonmemoïzed function. 



```ocaml 

let module Mem = Memo.Fake(struct 

type t = int 

let equal = (=) 

let hash = Hashtbl.hash 

end) 



let fibo = Mem.memo (fun fibo x > 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2)) 

``` 



### Using your own defined cache 



With the `Mk` functor, you can also directly provide a `Cache` module, which should have the signature `Hashtbl.S`. We will include your cache module and use it to define a `memo` function: 



```ocaml 

let module Mem = Memo.Mk( 

Hashtbl.Make(struct 

type t = int 

let equal = (=) 

let hash = Hashtbl.hash 

end) 

end) 



let fibo = Mem.memo (fun fibo x > 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2)) 

``` 



This example is useless and equivalent to using the `Make` functor directly. 



If you find a real use case for this which doesn't need new dependencies, contact me and I'll be happy to add a new functor to the library. 



It should be useful only if you want to use another `Hashtbl` implementation or things like this. 





### Tuning 



There's a default value for the initial cache size. You can set it to the value of your choice, reset it to the default and get the current value like this: 



```ocaml 

Memo.set_initial_cache_size 1024; 

Memo.reset_initial_cache_size (); 

let curr_size = Memo.get_initial_cache_size () 

``` 



Note that with the current implementation of hash tables in OCaml, it's better if you choose a power of two. You may saw some code using a prime number, it's because some years ago it was the best thing to do as the hash tables implementation was different. [JeanChristophe Filliâtre] explained this to me, thanks again ! Also keep in mind that if you use your own defined cache using the `Mk` functor, it may not be the right thing to do. 



## License 



See [LICENSE]. 



## Changelog 



See [CHANGELOG]. 

 [LICENSE] 

 [CHANGELOG] 

 [documentation] 



[CHANGELOG]: ./CHANGELOG.md 

[documentation]: https://doc.zapashcanon.fr/memo/ 

[LICENSE]: ./LICENSE.md 



[binary decision diagram]: https://en.wikipedia.org/wiki/Binary_decision_diagram 

[JeanChristophe Filliâtre]: https://www.lri.fr/~filliatr/ 

[hash consing]: https://en.wikipedia.org/wiki/Hash_consing 

[memoïzation]: https://en.wikipedia.org/wiki/Memoization 

[OCaml]: https://en.wikipedia.org/wiki/OCaml 

[Sylvain Conchon]: https://www.lri.fr/~conchon/ 
@ 0,0 +1,3 @@ 

(documentation 

(package memo) 

(mld_files usage index)) 
@ 0,0 +1,7 @@ 

{0 memo} 



[memo] is an {{:https://en.wikipedia.org/wiki/OCaml}OCaml} library for {{:https://en.wikipedia.org/wiki/Memoization}memoïzation}. 



It provides easy ways to memoïze a function, in a typesafe and modular way and the ability to get forgetful memoïzation. 



See {{:usage.html} usage} for a short explanation on how to use the library and {{:Memo/index.html} Memo} for a more detailled explanation. 
@ 0,0 +1,117 @@ 

{0 Usage} 



If you had a function [fibo] defined like this: 



{[ 

let rec fibo x = 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2) 

]} 



There's many different ways to memoïze it. 



{1:simple_memo Simple memoïzation} 



The easiest one is to rewrite it like this: 



{[ 

let fibo = Memo.memo (fun fibo x > 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2)) 

]} 



It'll use the {!module:Hashtbl} module from {!module:Stdlib} directly. 



I'd like to thank {{:https://www.lri.fr/~conchon/}Sylvain Conchon} who taught me memoïzation and how to write this [memo] function when I was his student. 



{1:custom_memo Using you own type, [equal] and [hash] functions} 



We provide a {!module:Memo.Make} functor. It can be useful in case you don't want to use polymorphic equality or you are doing things like {{:https://en.wikipedia.org/wiki/Hash_consing}hash consing} and you know how to compare or hash your type more efficiently. 



{[ 

let module Mem = Memo.Make(struct 

type t = int 

let equal = (=) 

let hash = Hashtbl.hash 

end) 



let fibo = Mem.memo (fun fibo x > 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2)) 

]} 



{1:forgetful_memo Forgetful memoïzation} 



We provide a {!module:Memo.MakeWeak} functor. It works like the previous one, but the bindings in the memoïzation cache will be weak, allowing the garbage collector to remove them if they are not used somewhere else. 



{[ 

let module Mem = Memo.MakeWeak(struct 

type t = int 

let equal = (=) 

let hash = Hashtbl.hash 

end) 



let fibo = Mem.memo (fun fibo x > 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2)) 

]} 



I'd like to thank {{:https://www.lri.fr/~filliatr/}JeanChristophe Filliâtre} who taugh me forgetful memoïzation when I was doing research on {{:https://en.wikipedia.org/wiki/Binary_decision_diagram}binary decision diagram} under his direction while I was a first year master student. 



{1:fake_memo Fake memoïzation} 



We provide a {!module:Memo.Fake} functor. It is useful if you want to quickly test a function you memoïzed with our {!module:Memo.Make} or {!module:Memo.MakeWeak} functor, but without memoïzing it. It'll basically do nothing and should be equivalent to your initial nonmemoïzed function. 



{[ 

let module Mem = Memo.Fake(struct 

type t = int 

let equal = (=) 

let hash = Hashtbl.hash 

end) 



let fibo = Mem.memo (fun fibo x > 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2)) 

]} 



{1:custom_cache Using your own defined cache} 



With the {!module:Memo.Mk} functor, you can also directly provide a [Cache] module, which should have the signature {!moduletype:Hashtbl.S}. We will include your cache module and use it to define a [memo] function: 



{[ 

let module Mem = Memo.Mk( 

Hashtbl.Make(struct 

type t = int 

let equal = (=) 

let hash = Hashtbl.hash 

end) 

end) 



let fibo = Mem.memo (fun fibo x > 

if x < 0 then invalid_arg "fibo"; 

if x < 2 then x 

else fibo (x  1) + fibo (x  2)) 

]} 



This example is useless and equivalent to using the {!module:Memo.Make} functor directly. 



If you find a real use case for this which doesn't need new dependencies, contact me and I'll be happy to add a new functor to the library. 



It should be useful only if you want to use another {!module:Hashtbl} implementation or things like this. 



{1:tuning Tuning} 



There's a default value for the initial cache size. You can set it to the value of your choice, reset it to the default and get the current value like this: 



{[ 

Memo.set_initial_cache_size 1024; 

Memo.reset_initial_cache_size (); 

let curr_size = Memo.get_initial_cache_size () 

]} 



Note that with the current implementation of hash tables in OCaml, it's better if you choose a power of two. You may saw some code using a prime number, it's because some years ago it was the best thing to do as the hash tables implementation was different. {{:https://www.lri.fr/~filliatr/}JeanChristophe Filliâtre} explained this to me, thanks again ! Also keep in mind that if you use your own defined cache thanks to the {!module:Memo.Mk} functor, it may not be the right thing to do. 
Write
Preview
Loading…
Cancel
Save
Reference in new issue