我正在将一个数学描述的算法的实现写入OCaml。我正在努力保持实现的清洁和贴近本文,并且只使用记忆定义这些定义良好的技术来提高性能。
不幸的是,虽然memoization在整数上运行得很好,但对于大型或复杂的数据类型,它仍然可以是O(n)。通过在记忆之前枚举数据类型,可以获得O(1)性能。
例如,我们在下面的示例中看到memo len
仍然是O(n),但DroidMemoized.len
将是O(1),而DroidMemoized
是Droid
的替代品1}}。
但是,是否可以自动从DroidMemoized
创建Droid
?例如,是否有模块EnumMemo,我们可以写let DroidMemoized = EnumMemo.Make(Droid)
?
module Droid = struct
type t = string
let empty = ""
let beep x = x ^ " notdroids"
let boop x = x ^ " lookingfor"
let len x = String.length x
end
let memo f =
let m = Hashtbl.create 9 in
fun x ->
try Hashtbl.find m x
with Not_found ->
let y = f x in
Hashtbl.add m x y; y
module DroidMemoized = struct
(* Boilerplate *)
let to_id_h : (Droid.t, int) Hashtbl.t = Hashtbl.create 9
let from_id_h : (int, Droid.t) Hashtbl.t = Hashtbl.create 9 (*or use array*)
let size = ref 0
let from_id i = (Hashtbl.find from_id_h i)
let to_id (x :Droid.t) : int =
if Hashtbl.mem to_id_h x
then Hashtbl.find to_id_h x
else (size := (!size)+1;
Hashtbl.add to_id_h x (!size);
Hashtbl.add from_id_h (!size) x;
!size)
(* Function wrappers *)
let empty = to_id Droid.empty
let beep = memo ( fun x -> to_id (Droid.beep (from_id x)) )
let boop = memo ( fun x -> to_id (Droid.boop (from_id x)) )
let len = memo ( fun x -> (Droid.len (from_id x)) )
end
答案 0 :(得分:0)
为Functors很容易生成样板,但我能为函数包装器做的最好的事情是使用外部实用程序来解析OCaml签名,例如
#!/usr/bin/env perl
@signature=`ocaml < droid.ml`;
our $t;
foreach(@signature) {
if (/type t = (\w+)/ ){
$t=$1;
} elsif (/val (\w+) : (\w+)$/){
print "let $1 = " .
( ($2 eq $t) ? " to_id " : "") .
"( Droid.$1 )\n";
} elsif (/val (\w+) : (\w+) -> (\w+)$/){
print "let $1 = " .
( ($3 eq "unit") ? "(" : "memo (") .
" fun x -> " .
( ($3 eq $t) ? "to_id (" : "(") .
( ($2 eq $t) ? " from_id" : "") .
" (Droid.$1 x))\n";
}
}
我已将其刷新为Perl脚本以记忆模块:https://github.com/gmatht/joshell/blob/master/scripts/memoize.pl