123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793(*
* LazyListLabels - lazily-computed lists
* Copyright (C) 2008 David Teller
*
* 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
*)##V>=5##modulePervasives=Stdlib(*$inject
##V>=5##module Pervasives = Stdlib
*)(** {6 Exceptions} *)exceptionNo_more_elementsexceptionEmpty_listexceptionInvalid_indexofintexceptionDifferent_list_sizeofstring(** {6 Types} *)type'anode_t=|Nil|Cons of'a*'atand'at=('anode_t)Lazy.ttype'aenumerable='attype'amappable='at(** {6 Access} *)letlazy_from_valv=##V<5##Lazy.lazy_from_valv##V>=5##Lazy.from_valvletnil=lazy_from_valNilletnextl=Lazy.forcelletconsht=lazy_from_val(Cons(h,t))let(^:^)=consletgetl=matchnextlwith|Nil->None|Cons(x,rest)->Some(x,rest)letpeekl=matchnextlwith|Nil->None|Cons(x,_)->Somex(** {6 Constructors} *)letfrom_whilef=letrecaux()=lazy(matchf()with|None->Nil|Somex->Cons(x,aux()))inaux()letfromf=letf'()=trySome(f())withNo_more_elements->Noneinfrom_whilef'letseqdatanextcond=letrecauxdata=ifconddatathenCons(data,lazy(aux(nextdata)))elseNilinlazy(auxdata)letunfold(data:'b)(next:'b->('a*'b)option)=letrecauxdata=matchnextdatawith|Some(a,b)->Cons(a,lazy(auxb))|None->Nilinlazy(auxdata)letfrom_loop(data:'b)(next:'b->('a*'b)):'at=letf'data=trySome(nextdata)withNo_more_elements->Noneinunfolddataf'letinitnf=letrecauxi=ifi<nthenlazy(Cons(fi,aux(i+1)))elsenilinifn<0theninvalid_arg"LazyList.init"elseaux0letmakenx=letrecauxi=ifi<nthenlazy(Cons(x,aux(i+1)))elsenilinifn<0theninvalid_arg"LazyList.make"elseaux0(**
{6 Iterators}
*)letiterfl=letrecauxl=matchnextlwith|Cons(x,t)->(ignore(fx);auxt)|Nil->()inauxlletiterifl=letrecauxil=matchnextlwith|Cons(x,t)->(fix;aux(i+1)t)|Nil->()inaux0lletmapfl=letrecauxrest=matchnextrestwith|Cons(x,(t:'at))->Cons(fx,lazy(auxt))|Nil->Nilinlazy(auxl)letmapifl=letrecauxresti=matchnextrestwith|Cons(x,(t:'at))->Cons(fix,lazy(auxt(i+1)))|Nil->Nilinlazy(auxl0)letfold_leftfinitl=letrecauxaccrest=matchnextrestwith|Cons(x,t)->aux(faccx)t|Nil->accinauxinitlletfold_rightfinitl=letrecauxrest=matchnextrestwith|Cons(x,t)->fx(auxt)|Nil->initinauxlleteager_fold_rightflinit=fold_rightfinitlletlazy_fold_rightflinit=letrecauxrest=lazybeginmatchnextrestwith|Cons(x,t)->fx(auxt)|Nil->Lazy.forceinitendinauxl(** {6 Finding}*)letmay_findpl=letrecauxl=matchnextlwith|Nil->None|Cons(x,t)->ifpxthenSomexelseauxtinauxlletmay_rfindpl=letrecauxlacc=matchnextlwith|Nil->acc|Cons(x,t)->auxt(ifpxthenSomexelseacc)inauxlNoneletmay_findipl=letrecauxli=matchnextlwith|Nil->None|Cons(x,_)whenpix->Some(i,x)|Cons(_,t)->auxt(i+1)inauxl0letmay_rfindipl=letrecauxlacci=matchnextlwith|Nil->acc|Cons(x,t)->auxt(ifpixthenSome(i,x)elseacc)(i+1)inauxlNone0letfind_exnpel=BatOption.get_exn(may_findpl)eletrfind_exnpel=BatOption.get_exn(may_rfindpl)eletfindpl=find_exnpNot_foundlletrfindpl=rfind_exnpNot_foundlletfindipl=BatOption.get_exn(may_findipl)Not_foundletrfindipl=BatOption.get_exn(may_rfindipl)Not_foundletindex_ofel=matchmay_findi(fun_x->e=x)lwith|None->None|Some(i,_)->Someiletrindex_ofel=matchmay_rfindi(fun_x->e=x)lwith|None->None|Some(i,_)->Someiletindex_ofqel=matchmay_findi(fun_x->e==x)lwith|None->None|Some(i,_)->Someiletrindex_ofqel=matchmay_rfindi(fun_x->e==x)lwith|None->None|Some(i,_)->Somei(** {6 Common functions}*)letlengthl=fold_left(funn_->n+1)0lletis_emptyl=matchnextlwith|Nil->true|Cons_->falseletwould_at_failn=letrecauxli=matchnextlwith|Nil->true|Cons(_,_)wheni=0->false|Cons(_,t)->auxt(i-1)inauxnlethdlist=matchnextlistwith|Cons(x,_)->x|Nil->raiseEmpty_listletfirst=hdletlastl=letrecauxaccl=matchnextlwith|Nil->acc|Cons(x,t)->aux(Somex)tinmatchauxNonelwith|None->raiseEmpty_list|Somex->xlettllist=matchnextlistwith|Cons(_,t)->t|Nil->raiseEmpty_listletatlistn=letrecauxlisti=match((nextlist),i)with|(Cons(x,_),0)->x|(Cons(_,t),_)->auxt(i-1)|(Nil,_)->raise(Invalid_indexn)inifn<0thenraise(Invalid_indexn)elseauxlistnletnth=atletrevlist=fold_left(funaccx->lazy_from_val(Cons(x,acc)))nillist(**Revert a list, convert it to a lazy list.
Used as an optimisation.*)letrev_of_list(list:'alist)=List.fold_left(funaccx->lazy_from_val(Cons(x,acc)))nillistleteager_append(l1:'at)(l2:'at)=letrecauxlist=matchnextlistwith|Cons(x,t)->consx(auxt)|Nil->l2inauxl1letrev_append(l1:'at)(l2:'at)=letrecauxlistacc=matchnextlistwith|Cons(x,t)->auxt(lazy_from_val(Cons(x,acc)))|Nil->accinauxl1l2(**Revert a list, convert it to a lazy list and append it.
Used as an optimisation.*)letrev_append_of_list(l1:'alist)(l2:'at):'at=letrecauxlistacc=matchlistwith|[]->acc|h::t->auxt(conshacc)inauxl1l2letappend(l1:'at)(l2:'at)=letrecauxlist=matchnextlistwith|Cons(x,(t:'at))->Cons(x,lazy(auxt))|_->Lazy.forcel2inlazy(auxl1)(*$T append
to_list (append (of_list [1;2]) (of_list [3;4])) = [1;2;3;4]
ignore (append (lazy (failwith "lazy cell")) nil); true
hd (append (cons () nil) (lazy (failwith "lazy cell"))); true
*)let(^@^)=appendletflatten(lol:('at)list)=ListLabels.fold_left~init:nil~f:appendlolletconcatlol=lazy_fold_right(funlirest->Lazy.force(appendlirest))lolnil(*$T concat
to_list (concat (of_list (List.map of_list [[1;2]; [3]; [4;5]; []; [6]; []; []]))) = [1;2;3;4;5;6]
ignore (concat (lazy (Cons ((let () = failwith "foo" in nil), nil)))); true
*)(** {6 Combinatorics} *)letcombinationsl=letrecgenl=matchlwith|[]->cons[]nil|x::l'->lazy(lettl=genl'inletnode=appendtl(map(funl->x::l)tl)inLazy.forcenode)ingenl(*$T combinations
List.sort Legacy.compare (to_list (combinations [1;2;3])) = \
[[]; [1]; [1;2]; [1;2;3]; [1;3]; [2]; [2;3]; [3]]
to_list (combinations []) = [[]]
List.sort Legacy.compare (to_list (combinations [1])) = [[]; [1]]
*)letpermutationsl=(* do a choice in [l]. [right] contain elements not to choose from. *)letrecchoose_firstamongright=matchamongwith|[]->cons[]nil|[x]->perms_starting_withxright|x::among'->(* choose [x], or don't (in which case put it in [right]) *)append(perms_starting_withx(among'@right))(choose_firstamong'(x::right))(* all permutations of [l], prefixed with [x] *)andperms_starting_withxl=map(funl->x::l)(choose_firstl[])inchoose_firstl[](*$T permutations
List.sort Legacy.compare (to_list (permutations [1;2;3])) = \
[[1;2;3]; [1;3;2]; [2;1;3]; [2;3;1]; [3;1;2]; [3;2;1]]
to_list (permutations []) = [[]]
to_list (permutations [1]) = [[1]]
*)(** {6 Conversions} *)(**
Eager conversion to list.
*)letto_listl=fold_right(funxacc->x::acc)[]l(**
Lazy conversion to stream.
*)letto_streaml=letrecauxrest=matchnextrestwith|Cons(x,t)->Stream.iconsx(Stream.slazy(fun_->auxt))|Nil->Stream.semptyinauxl(**
Eager conversion to array.
*)letto_arrayl=Array.of_list(to_listl)letenuml=letrecauxl=letreference=reflinBatEnum.make~next:(fun()->matchnext!referencewith|Cons(x,t)->reference:=t;x|Nil->raiseBatEnum.No_more_elements)~count:(fun()->length!reference)~clone:(fun()->aux!reference)inauxl(**
Lazy conversion from lists
Albeit slower than eager conversion, this is the default mechanism for converting from regular
lists to lazy lists. This for two reasons :
* if you're using lazy lists, total speed probably isn't as much an issue as start-up speed
* this will let you convert regular infinite lists to lazy lists.
*)letof_listl=letrecaux=function|[]->nil|h::t->lazy(Cons(h,auxt))inauxl(**
Lazy conversion from stream.
*)letof_streams=letrecauxs=let(__strm:_Stream.t)=sinmatchStream.peek__strmwith|Someh->(Stream.junk__strm;lazy(Cons(h,auxs)))|None->nilinauxs(**
Eager conversion from lists
*)leteager_of_listl=ListLabels.fold_right~init:nil~f:(funxacc->lazy_from_val(Cons(x,acc)))l(**
Eager conversion from array
*)letof_arrayl=ArrayLabels.fold_right~init:nil~f:(funxacc->lazy_from_val(Cons(x,acc)))l(**
Lazy conversion from enum
*)letof_enume=letrecaux()=lazy(matchBatEnum.getewith|Somex->Cons(x,aux())|None->Nil)inaux()(**
{6 Predicates}
*)letfilterfl=letrecnext_truel=matchnextlwith(*Compute the next accepted predicate without thunkification*)|Cons(x,l)whennot(fx)->next_truel|l->linletrecauxl=lazy(matchnext_truelwith|Cons(x,l)->Cons(x,auxl)|Nil->Nil)inauxlletfilter_mapfl=letrecnext_truel=matchnextlwith(*Compute the next accepted predicate without thunkification*)|Cons(x,l)->beginmatchfxwith|Somev->Some(v,l)|None->next_truelend|Nil->Noneinletrecauxl=lazy(matchnext_truelwith|Some(x,l)->Cons(x,auxl)|None->Nil)inauxl(*let filter f l =
let rec aux rest =
match next rest with
| Cons (x, t) when f x -> Cons (x, lazy (aux t))
| Cons (_, t) -> aux t
| Nil -> Nil
in lazy (aux l)*)letexistsfl=letrecauxrest=matchnextrestwith|Cons(x,_)whenfx->true|Cons(_,t)->auxt|Nil->falseinauxl(*$T exists
exists (fun x -> x = 3) (append (of_list [0;1;2]) (map (fun () -> 3) eternity))
not (exists (fun x -> x < 0) (init 100 (fun i -> i)))
*)letfor_allfl=letrecauxrest=matchnextrestwith|Cons(x,t)whenfx->auxt|Cons_->false|Nil->trueinauxl(*$T for_all
not (for_all (fun x -> x <> 3) (append (of_list [0;1;2]) (map (fun () -> 3) eternity)))
for_all (fun x -> x >= 0) (init 100 (fun i -> i))
*)letrangeab=letrecincreasinglohi=iflo>hithennilelselazy(Cons(lo,increasing(lo+1)hi))in(* and decreasing lo hi = if lo > hi then
nil
else
lazy (Cons hi (decreasing lo (hi - 1)))*)ifb>=athenincreasingabelse(*decreasing b a*)nilletdropnl=letrecauxli=ifi=0thenlelsematchnextlwith|Nil->raise(Invalid_indexn)|Cons(_,t)->auxt(i-1)inauxlnletsplit_atnli=letlast_n=refninletlast_li=refliinletrectakenli=last_n:=n;last_li:=li;ifn=0thenlazyNilelselazy(match(Lazy.forceli)with|Nil->Nil|Cons(x,xs)->Cons(x,take(n-1)xs))intakenli,lazy(Lazy.force(drop!last_n!last_li))letsplit_nth=split_atletmeme=exists((=)e)letmemqe=exists((==)e)letassocel=snd(find(fun(a,_)->a=e)l)letassqel=snd(find(fun(a,_)->a==e)l)letmem_assocel=BatOption.is_some(may_find(fun(a,_)->a=e)l)letmem_assqel=BatOption.is_some(may_find(fun(a,_)->a==e)l)(* let rec aux rest = match next rest with
| Cons (h, t) ->
(match f h with
| None -> lazy (aux t)
| Some x -> cons x (lazy (aux t)))
| Nil -> Nil
in lazy (aux l)*)letunique?(cmp=compare)l=letset=ref(BatMap.PMap.createcmp)inletshould_keepx=ifBatMap.PMap.memx!setthenfalseelse(set:=BatMap.PMap.addxtrue!set;true)in(* use a stateful filter to remove duplicate elements *)filtershould_keeplletunique_eq?(eq=(=))l=letrecnext_truel=matchnextlwith(*Compute the next accepted predicate without thunkification*)|Cons(x,l)whenexists(eqx)l->next_truel|l->linletrecauxl=lazy(matchnext_truelwith|Cons(x,l)->Cons(x,auxl)|Nil->Nil)inauxlletremove_ifpl=letrecauxaccl=matchnextlwith|Nil->rev_of_listacc|Cons(h,t)whenph->rev_append_of_listacct|Cons(h,t)->aux(h::acc)tinaux[]lletremove_all_suchpl=filter_map(funy->ifpythenNoneelseSomey)lletremovexl=remove_if((=)x)lletremove_allxl=remove_all_such((=)x)l(** An infinite list of nothing *)letreceternity=lazy(Cons((),eternity))lettakenl=fst(split_atnl)letdrop_whilep=letrecauxl=matchnextlwith|Nil->nil|Cons(h,t)whenph->auxt|Cons(_,_)->linaux(* TODO: make lazy *)lettake_whilep=letrecauxaccl=matchnextlwith|Cons(h,t)whenph->aux(h::acc)t|Cons_|Nil->rev_of_listaccinaux[]letsort?(cmp=Pervasives.compare)l=of_list(List.sortcmp(to_listl))letstable_sortcmpl=of_list(List.stable_sortcmp(to_listl))letmap2fl1l2=letrecauxl1l2=match(nextl1,nextl2)with|(Cons(h1,t1),Cons(h2,t2))->lazy(Cons(fh1h2,auxt1t2))|(Nil,Nil)->nil|(Cons_,Nil)|(Nil,Cons_)->raise(Different_list_size"LazyList.map2")inauxl1l2letiter2fl1l2=letrecauxl1l2=match(nextl1,nextl2)with|(Cons(h1,t1),Cons(h2,t2))->fh1h2;auxt1t2|(Nil,Nil)->()|(Cons_,Nil)|(Nil,Cons_)->raise(Different_list_size"LazyList.iter2")inauxl1l2letfold_left2faccl1l2=letrecauxaccl1l2=match(nextl1,nextl2)with|(Cons(h1,t1),Cons(h2,t2))->aux(facch1h2)t1t2|(Nil,Nil)->acc|(Cons_,Nil)|(Nil,Cons_)->raise(Different_list_size"LazyList.fold_left2")inauxaccl1l2letfold_right2fl1l2acc=letrecauxl1l2=match(nextl1,nextl2)with|(Cons(h1,t1),Cons(h2,t2))->fh1h2(auxt1t2)|(Nil,Nil)->acc|(Cons_,Nil)|(Nil,Cons_)->raise(Different_list_size"LazyList.fold_right2")inauxl1l2letfor_all2pl1l2=letrecauxl1l2=match(nextl1,nextl2)with|(Cons(h1,t1),Cons(h2,t2))->ph1h2&&(auxt1t2)|(Nil,Nil)->true|(Cons_,Nil)|(Nil,Cons_)->raise(Different_list_size"LazyList.for_all2")inauxl1l2letequaleql1l2=letrecauxl1l2=match(nextl1,nextl2)with|(Cons(h1,t1),Cons(h2,t2))->eqh1h2&&(auxt1t2)|(Nil,Nil)->true|(Cons_,Nil)|(Nil,Cons_)->falseinauxl1l2(*$T equal
equal (equal (=)) (init 3 (range 0)) (init 3 (range 0))
not (equal (equal (=)) (of_list [(of_list [0; 1; 2])]) (of_list [(of_list [0; 42; 2])]))
not (equal (=) (range 0 2) (range 0 3))
not (equal (=) (range 0 3) (range 0 2))
*)letexists2pl1l2=letrecauxl1l2=match(nextl1,nextl2)with|(Cons(h1,t1),Cons(h2,t2))->ph1h2||(auxt1t2)|(Nil,Nil)->false|(Cons_,Nil)|(Nil,Cons_)->raise(Different_list_size"LazyList.exists2")inauxl1l2letcombinel1l2=letrecauxl1l2=match(nextl1,nextl2)with|(Cons(h1,t1),Cons(h2,t2))->lazy(Cons((h1,h2),(auxt1t2)))|(Nil,Nil)->nil|(Cons_,Nil)|(Nil,Cons_)->raise(Different_list_size"LazyList.combine")inauxl1l2letuncombinel=let(l1,l2)=BatEnum.uncombine(enuml)in(of_enuml1,of_enuml2)(*let uncombine l =
let rec aux l = match next l with
| Cons ((h1, h2), t) -> lazy (let (t1, t2) = aux t in
Cons (h1, t1), Cons(h2, t2))
| Nil -> lazy (Nil, Nil)
in aux l*)(*let uncombine l =
unfold l (fun l -> match peek l with
| None -> None
| Cons (h1, h2), t*)letprint?(first="[^")?(last="^]")?(sep="; ")print_aoutt=BatEnum.print~first~last~sepprint_aout(enumt)moduleInfix=structlet(^:^),(^@^)=(^:^),(^@^)endmoduleExceptionless=struct(** Exceptionless counterparts for error-raising operations*)letfind=may_findletrfind=may_rfindletfindi=may_findiletrfindi=may_rfindiletatlistn=letrecauxlisti=match(nextlist,i)with|(Cons(x,_),0)->`Okx|(Cons(_,t),_)->auxt(i-1)|(Nil,_)->`Invalid_indexninifn<0then`Invalid_indexnelseauxlistnletassoca(l:'at)=trySome(assocal)withNot_found->Noneletassqal=trySome(assqal)withNot_found->Noneletsplit_atnl=try`Ok(split_atnl)withNot_found->`Invalid_indexnendmoduleLabels=structletiter~fx=iterfxletiter2~fx=iter2fxletiteri~fx=iterifxletmap~fx=mapfxletmap2~fx=map2fxletmapi~fx=mapifxletfilter~f=filterfletexists~f=existsfletexists2~f=exists2fletfor_all~f=for_allfletfor_all2~f=for_all2fletfilter_map~f=filter_mapfletfind~f=findfletfindi~f=findifletrfind~f=rfindfletrfindi~f=rfindifletfind_exn~f=find_exnfletrfind_exn~f=rfind_exnfletremove_if~f=remove_iffletremove_all_such~f=remove_all_suchflettake_while~f=take_whilefletdrop_while~f=drop_whilefletfold_left~f~init=fold_leftfinitletfold_right~f~init=fold_rightfinitletfold_left2~f~init=fold_left2finitletfold_right2~fl1l2~init=fold_right2fl1l2initmoduleExceptionless=structletfind~f=Exceptionless.findfletrfind~f=Exceptionless.rfindfletfindi~f=Exceptionless.findifletrfindi~f=Exceptionless.rfindifletassq=Exceptionless.assqletassoc=Exceptionless.assocletat=Exceptionless.atletsplit_at=Exceptionless.split_atendend