123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104(*
* Cache - Simple (and maybe complex) caching structures
* Copyright (C) 2011 Batteries Included Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version,
* with the special exception on linking described in file LICENSE.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*)openBatInnerPervasivestype('a,'b)manual_cache={get:'a->'b;del:'a->unit;enum:unit->('a*'b)BatEnum.t}letmake_ht~gen~init_size=letht=BatHashtbl.createinit_size in{get=(funk->tryBatHashtbl.findhtkwithNot_found->genk|>tap(BatHashtbl.addhtk));del=(funk->BatHashtbl.removehtk);enum=(fun()->BatHashtbl.enumht)}(* For tests, use side effects to count number of times the function
is run *)(*$T make_ht
let runs = ref 0 in let c = make_ht ~gen:(fun x -> incr runs; x) ~init_size:5 in let s = c.get 3 + c.get 4 + c.get 3 in s = 10 && !runs = 2
*)letmake_map~gen=letm=refBatMap.emptyin{get=(funk->tryBatMap.findk!mwithNot_found->genk|>tap(funv->m:=BatMap.addkv!m));del=(funk->m:=BatMap.removek!m);enum=(fun()->BatMap.enum!m)}(*$T make_map
let runs = ref 0 in let c = make_map ~gen:(fun x -> incr runs; x) in let s = c.get 3 + c.get 4 + c.get 3 in s = 10 && !runs = 2
*)type('a,'b)auto_cache='a->'bletlru_cache~gen~cap=letentries =refNoneinletauxentries =BatHashtbl.createcapinletlen=ref0inletentry_genkv=incrlen;letn=BatDllist.create(k,v)inBatHashtbl.addauxentrieskn;ninletentry_findk_dll=tryletn=BatHashtbl.findauxentrieskinBatDllist.removen;nwithNot_found->entry_genk(genk)inletentry_removen=letlru=BatDllist.prevninletk=BatDllist.getlru|>fstinBatHashtbl.removeauxentriesk;BatDllist.removelru;decrleninletgetk=match!entrieswith(* remove match by replacing get after first run? *)|Somedll->(* not at head of list *)let(k0,v)=BatDllist.getdllinifk=k0then v(* specialcase headof list *)elsebeginletn=entry_findkdllin(* Putn at the head of the list *)BatDllist.splicendll;entries:=Somen;(*Remove the tail if over capacity *)if!len>capthenentry_removen;(* BatDllist.print (BatTuple.Tuple2.print BatPervasives.print_any BatPervasives.print_any) BatIO.stdout n; *)(* return the value *)BatDllist.getn|>sndend|None->(* no list - generate it *)letv=genkinentries:=Some(entry_genkv);vinget(*WARNING: s is evaluated right to left *)(*$T lru_cache
let runs = ref 0 in let id = lru_cache ~gen:(fun x -> incr runs; x) ~cap:3 in \
let s = id 1 + id 1 + id 3 + id 3 + id 2 + id 1 in\
s = 11 && !runs = 3
let runs = ref 0 in let id = lru_cache ~gen:(fun x -> incr runs; x) ~cap:3 in \
let s = id 1 + id 1 + id 4 + id 3 + id 2 + id 1 in \
s = 12 && !runs = 5
*)