You can subscribe to this list here.
2003 |
Jan
|
Feb
(81) |
Mar
(97) |
Apr
(88) |
May
(80) |
Jun
(170) |
Jul
(9) |
Aug
|
Sep
(18) |
Oct
(58) |
Nov
(19) |
Dec
(7) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2004 |
Jan
(22) |
Feb
(9) |
Mar
(28) |
Apr
(164) |
May
(186) |
Jun
(101) |
Jul
(143) |
Aug
(387) |
Sep
(69) |
Oct
(14) |
Nov
(8) |
Dec
(99) |
2005 |
Jan
(10) |
Feb
(34) |
Mar
(24) |
Apr
(7) |
May
(41) |
Jun
(20) |
Jul
(3) |
Aug
(23) |
Sep
(2) |
Oct
(26) |
Nov
(41) |
Dec
(7) |
2006 |
Jan
(6) |
Feb
(3) |
Mar
(11) |
Apr
|
May
|
Jun
(5) |
Jul
(8) |
Aug
(20) |
Sep
|
Oct
(6) |
Nov
(5) |
Dec
|
2007 |
Jan
|
Feb
(1) |
Mar
|
Apr
(3) |
May
(2) |
Jun
|
Jul
|
Aug
(1) |
Sep
(7) |
Oct
(6) |
Nov
(19) |
Dec
(11) |
2008 |
Jan
|
Feb
(7) |
Mar
(9) |
Apr
(21) |
May
(42) |
Jun
(27) |
Jul
(28) |
Aug
(26) |
Sep
(16) |
Oct
(32) |
Nov
(49) |
Dec
(65) |
2009 |
Jan
(35) |
Feb
(20) |
Mar
(36) |
Apr
(42) |
May
(111) |
Jun
(99) |
Jul
(70) |
Aug
(25) |
Sep
(15) |
Oct
(29) |
Nov
(3) |
Dec
(18) |
2010 |
Jan
(10) |
Feb
(4) |
Mar
(57) |
Apr
(63) |
May
(71) |
Jun
(64) |
Jul
(30) |
Aug
(49) |
Sep
(11) |
Oct
(4) |
Nov
(2) |
Dec
(3) |
2011 |
Jan
(1) |
Feb
(1) |
Mar
|
Apr
|
May
|
Jun
(1) |
Jul
(1) |
Aug
(2) |
Sep
|
Oct
|
Nov
|
Dec
(1) |
2012 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
(1) |
Sep
|
Oct
|
Nov
|
Dec
|
2013 |
Jan
|
Feb
(1) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2014 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(2) |
Jul
|
Aug
|
Sep
(1) |
Oct
|
Nov
(1) |
Dec
|
2015 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
(1) |
Sep
|
Oct
|
Nov
|
Dec
|
2021 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(1) |
Oct
|
Nov
|
Dec
|
2022 |
Jan
|
Feb
(1) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2023 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(1) |
Oct
|
Nov
|
Dec
(1) |
2024 |
Jan
(1) |
Feb
(3) |
Mar
(6) |
Apr
(2) |
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(1) |
2025 |
Jan
(2) |
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(1) |
Aug
(1) |
Sep
(5) |
Oct
|
Nov
|
Dec
|
From: Alan P. <ap...@re...> - 2003-06-09 06:13:49
|
In article <01b201c32e41$ce0853b0$2413f9ca@WARP>, Nicolas Cannasse wrote: > > I'll try to review theses patch when I have some time. > I think it's good idea to remove the try..catch blocks when we have a "fast" > count in concat and append, because they occur in each call to next . But > for fold / iter , I'm not sure, mainly because a fast count can still be > O(N) and cost more than having one try...catch block setuped. Could you do > some benchs using ExtList.enum instead of Enum.init ? The "fast" count is > O(N) here in this case ( but is computed only once ). With big lists ( "when > we need speed" ) I'm pretty sure it will be better not to call count. I was operating under the assumption that "fast" would be set only for data structures that had their count readily available -- for instance, dynarray and init. |
From: Nicolas C. <war...@fr...> - 2003-06-09 04:45:54
|
> Here's an updated version of my patch to enum.ml, including using fast > in Enum.concat. A try block on every call to next() was quite > expensive. I wasn't able to think of anything useful to do with > t.count (as opposed to tn.count) in concat, though. > > If it's more convenient, I've also posted my modified enum.ml at: > > http://recalcitrant.org/~apost/enum.ml Thanks Alan. I'll try to review theses patch when I have some time. I think it's good idea to remove the try..catch blocks when we have a "fast" count in concat and append, because they occur in each call to next . But for fold / iter , I'm not sure, mainly because a fast count can still be O(N) and cost more than having one try...catch block setuped. Could you do some benchs using ExtList.enum instead of Enum.init ? The "fast" count is O(N) here in this case ( but is computed only once ). With big lists ( "when we need speed" ) I'm pretty sure it will be better not to call count. Nicolas Cannasse |
From: Alan P. <ap...@re...> - 2003-06-09 04:00:25
|
Here's an updated version of my patch to enum.ml, including using fast in Enum.concat. A try block on every call to next() was quite expensive. I wasn't able to think of anything useful to do with t.count (as opposed to tn.count) in concat, though. If it's more convenient, I've also posted my modified enum.ml at: http://recalcitrant.org/~apost/enum.ml --- /usr/local/src/ocaml-extlib/extlib-dev/enum.ml Tue Jun 3 19:21:46 2003 +++ ./enum.ml Sun Jun 8 19:20:07 2003 @@ -24,7 +24,7 @@ mutable fast : bool; } -(* raised by 'next' functions, should NOT goes outside the API *) +(* raised by 'next' functions, should NOT go outside the API *) exception No_more_elements let _dummy () = assert false @@ -50,7 +50,7 @@ f (n - 1 - !count)); clone = (fun () -> init !count f); fast = true; - } + } let force t = let rec clone enum count = @@ -158,90 +158,153 @@ t.clone() let iter f t = - let rec loop () = - f (t.next()); - loop(); - in - try - loop(); - with - No_more_elements -> () + if t.fast then + (* 10% faster than the exception-based implementation. + * A tail-recursive approach was slower than the for loop. + * Unfortunately, "for _ =" is not allowed. + *) + for idx = 1 to t.count () do + f (t.next ()) + done + else + let rec loop () = + f (t.next()); + loop(); + in + try + loop(); + with + No_more_elements -> () let iteri f t = - let rec loop idx = - f idx (t.next()); - loop (idx+1); - in - try - loop 0; - with - No_more_elements -> () + if t.fast then + for idx = 0 to (t.count ()) - 1 do + f idx (t.next ()) + done + else + let rec loop idx = + f idx (t.next()); + loop (idx+1); + in + try + loop 0; + with + No_more_elements -> () let iter2 f t u = - let rec loop () = - f (t.next()) (u.next()); - loop () - in - try - loop () - with - No_more_elements -> () + if t.fast then + for idx = 1 to min (t.count ()) (u.count ()) do + f (t.next ()) (u.next ()) + done + else + let rec loop () = + f (t.next()) (u.next()); + loop () + in + try + loop () + with + No_more_elements -> () let iter2i f t u = - let rec loop idx = - f idx (t.next()) (u.next()); - loop (idx + 1) - in - try - loop 0 - with - No_more_elements -> () + if t.fast then + for idx = 0 to (min (t.count ()) (u.count ())) - 1 do + f idx (t.next ()) (u.next ()) + done + else + let rec loop idx = + f idx (t.next()) (u.next()); + loop (idx + 1) + in + try + loop 0 + with + No_more_elements -> () let fold f init t = - let acc = ref init in - let rec loop() = - acc := f (t.next()) !acc; - loop() - in - try - loop() - with - No_more_elements -> !acc + if t.fast then begin + (* Runs in 61% of the time required by the exception-based + * implementation. + * A tail-recursive approach was 5% slower than the for loop. + *) + let acc = ref init in + for idx = 1 to (t.count ()) do + acc := f (t.next ()) !acc + done; + !acc + end + else + let acc = ref init in + let rec loop() = + acc := f (t.next()) !acc; + loop() + in + try + loop() + with + No_more_elements -> !acc let foldi f init t = - let acc = ref init in - let rec loop idx = - acc := f idx (t.next()) !acc; - loop (idx + 1) - in - try - loop 0 - with - No_more_elements -> !acc + if t.fast then begin + let acc = ref init in + for idx = 0 to (t.count ()) - 1 do + acc := f idx (t.next ()) !acc + done; + !acc + end + else + let acc = ref init in + let rec loop idx = + acc := f idx (t.next()) !acc; + loop (idx + 1) + in + try + loop 0 + with + No_more_elements -> !acc let fold2 f init t u = - let acc = ref init in - let rec loop() = - acc := f (t.next()) (u.next()) !acc; - loop() - in - try - loop() - with - No_more_elements -> !acc + if t.fast then begin + let acc = ref init in + for idx = 0 to (min (t.count ()) (u.count ())) - 1 do + acc := f (t.next ()) (u.next ()) !acc + done; + !acc + end + else + let acc = ref init in + let rec loop() = + acc := f (t.next()) (u.next()) !acc; + loop() + in + try + loop() + with + No_more_elements -> !acc let fold2i f init t u = - let acc = ref init in - let rec loop idx = - acc := f idx (t.next()) (u.next()) !acc; - loop (idx + 1) - in - try - loop 0 - with - No_more_elements -> !acc + if t.fast then begin + let acc = ref init in + for idx = 0 to (min (t.count ()) (u.count ())) - 1 do + acc := f idx (t.next ()) (u.next ()) !acc + done; + !acc + end + else + let acc = ref init in + let rec loop idx = + acc := f idx (t.next()) (u.next()) !acc; + loop (idx + 1) + in + try + loop 0 + with + No_more_elements -> !acc let find f t = + (* Using count is slower on long enums, and only 5-10% faster on very + * short ones, so don't bother. + *) let rec loop () = let x = t.next() in if f x then x else loop() @@ -293,40 +356,73 @@ from2 next (fun () -> filter f (t.clone())) let rec filter_map f t = - let rec next () = - match f (t.next()) with - | None -> next() - | Some x -> x - in + let rec next () = + match f (t.next()) with + | None -> next() + | Some x -> x + in from2 next (fun () -> filter_map f (t.clone())) +(* + * Note that e.next can change, but only from false to true. + * + * TODO: given that ta.count is called O(N) times, perhaps its result should be + * cached locally instead. This could be significant in the case where + * ta is a deeply nested append. + *) let rec append ta tb = - let append_next = ref _dummy in - append_next := (fun () -> - try - ta.next() - with - No_more_elements -> - append_next := tb.next; - !append_next ()); - { - count = (fun () -> ta.count() + tb.count()); - next = (fun () -> !append_next ()); - clone = (fun () -> append (ta.clone()) (tb.clone())); + let e = { + count = _dummy; + next = _dummy; + clone = _dummy; fast = ta.fast && tb.fast; } + and append_count = ref (fun () -> (ta.count ()) + (tb.count ())) + and append_clone = ref (fun () -> append (ta.clone ()) (tb.clone ())) + and append_next = ref _dummy in + let ta_done () = + append_count := tb.count; + append_clone := tb.clone; + append_next := tb.next; + e.fast <- tb.fast; + !append_next () + in + append_next := + if ta.fast then + (fun () -> + match ta.count () with + | 0 -> ta_done () + | _ -> ta.next ()) + else + (fun () -> + try + ta.next() + with + No_more_elements -> ta_done ()); + e.count <- (fun () -> !append_count ()); + e.next <- (fun () -> !append_next ()); + e.clone <- (fun () -> !append_clone ()); + e let rec concat t = let concat_ref = ref _dummy in - let rec concat_next() = - let tn = t.next() in - concat_ref := (fun () -> - try - tn.next() - with - No_more_elements -> - concat_next()); + let rec concat_next () = + let tn = t.next () in + concat_ref := + if tn.fast then + (fun () -> + match tn.count () with + | 0 -> concat_next () + | _ -> tn.next ()) + else + (fun () -> + try + tn.next () + with + No_more_elements -> + concat_next ()); !concat_ref () in concat_ref := concat_next; - from2 (fun () -> !concat_ref ()) (fun () -> concat (t.clone())) \ No newline at end of file + from2 (fun () -> !concat_ref ()) (fun () -> concat (t.clone ())) + |
From: John M. S. <sk...@oz...> - 2003-06-08 15:39:44
|
Brian Hurt wrote: > Here's an interesting debate point- what does a 1.0 release of ExtLib look > like? How close are we? > > Starting this discussion is tantaumont to saying "I think it's ready now", > which I'm not. I'm just thinking of maybe getting a rough sketch of what > people want/are working on. > > Here's my short list in approximate order of priority: > - fix the bug in psqueue > - unit tests at least some > - generic red-black tree library > - doubly linked list library > - bit fields/bit sets > > Comments? It is necessary to provide on-site documentation to what is *officially* working properly, and to include that in any release. If you do that, it doesn't matter if the other half of the library isn't ready. Release it anyhow. release early, release often is often quoted (though I don't know who said it). What would be important to me, if I found I wanted to use some functions from it, is that it can be EASILY USED BY DUMB PROGRAMMERS WHO NEVER WRITE OCAML. By which I mean, it should be trivial for them to download the library, build it, do some rudimentary testing, install it .. and then it is available for building the *actual* ocaml product they really want. On a Unix system an important requirement is to have a standard default place for it to live, so I can write scripts to build my ocaml software which happens to use ExtLib *as if* it were part of the standard distribution (after its been installed). I do hate the idea of polluting the offical distribution directories with third party software, since it is sometimes nice to rm -rf the whole official distribution installation and rebuild it (and if ExtLib lived inside, it too would have to be rebuilt). OTOH, even Python provides a 'site-packages' directory .. .. which is a subdirectory of the main library directory :( -- John Max Skaller, mailto:sk...@oz... snail:10/1 Toxteth Rd, Glebe, NSW 2037, Australia. voice:61-2-9660-0850 |
From: John M. S. <sk...@oz...> - 2003-06-08 15:28:21
|
Brian Hurt wrote: > This is what I was talking about when I mentioned "makefile hacks". > Unfortunately, I know enough about the build environment of Windows > (despite several years busily repressing memories) that I am wary of doing > and scripting hacks. The only scripting language I am sure exists is > Ocaml. Then use it. Only, please get rid of primary makefiles. Make is a very stupid, ugly, unworkable tool. Its use should be avoided at all costs. It offers only one advantage: familiarity for uses to say make to build something. I don't know if this can be done but a simple makefile: all: ocaml xlmake <arguments> which simply invokes the ocaml program xlmake with the arguments to the command make, would even solve that problem. Otherwise I'm happy to read the doce enough to learn I have to type xlmake or ocaml xlmake.ml to do a make. You may note that to do this efficiently would require some kind of topological dependency sorting: well, ExtLib has a data structure for that doesn't it? :-) -- John Max Skaller, mailto:sk...@oz... snail:10/1 Toxteth Rd, Glebe, NSW 2037, Australia. voice:61-2-9660-0850 |
From: Alan P. <ap...@re...> - 2003-06-08 11:10:08
|
Here's a little patch cleaning up Enum.append slightly from the previous one. --- enum.ml 2003/06/08 10:09:07 1.4 +++ enum.ml 2003/06/08 10:49:45 @@ -379,30 +379,26 @@ } and append_count = ref (fun () -> ta.count() + tb.count()) and append_clone = ref (fun () -> append (ta.clone()) (tb.clone())) - and append_next = ref _dummy + and append_next = ref _dummy in + let ta_done () = + append_count := tb.count; + append_clone := tb.clone; + append_next := tb.next; + e.fast <- tb.fast; + !append_next () in append_next := begin if ta.fast then (fun () -> match ta.count () with - | 0 -> - append_count := tb.count; - append_clone := tb.clone; - append_next := tb.next; - e.fast <- tb.fast; - !append_next () + | 0 -> ta_done () | _ -> ta.next ()) else (fun () -> try ta.next() with - No_more_elements -> - append_count := tb.count; - append_next := tb.next; - append_clone := tb.clone; - e.fast <- tb.fast; - !append_next ()) + No_more_elements -> ta_done ()) end; e.count <- (fun () -> !append_count ()); e.next <- (fun () -> !append_next ()); |
From: Alan P. <ap...@re...> - 2003-06-08 10:25:40
|
Here's a patch adding support for t.fast to Enum.iter*, fold*, and append. Note that I still think that having Enum and Stream as separate types would be a clearer way of doing this. I tried to follow the style being used in the file, though it is my opinion that tab characters are evil evil evil. I was really surprised that ocaml does not allow: for _ = 1 to 5 do print_string "hi\n" done ^^^ --- /usr/local/src/ocaml-extlib/extlib-dev/enum.ml Tue Jun 3 19:21:46 2003 +++ ./enum.ml Sun Jun 8 03:07:45 2003 @@ -24,7 +24,7 @@ mutable fast : bool; } -(* raised by 'next' functions, should NOT goes outside the API *) +(* raised by 'next' functions, should NOT go outside the API *) exception No_more_elements let _dummy () = assert false @@ -50,7 +50,7 @@ f (n - 1 - !count)); clone = (fun () -> init !count f); fast = true; - } + } let force t = let rec clone enum count = @@ -158,90 +158,153 @@ t.clone() let iter f t = - let rec loop () = - f (t.next()); - loop(); - in - try - loop(); - with - No_more_elements -> () + if t.fast then + (* 10% faster than the exception-based implementation. + * A tail-recursive approach was slower than the for loop. + * Unfortunately, "for _ =" is not allowed. + *) + for idx = 1 to t.count () do + f (t.next ()) + done + else + let rec loop () = + f (t.next()); + loop(); + in + try + loop(); + with + No_more_elements -> () let iteri f t = - let rec loop idx = - f idx (t.next()); - loop (idx+1); - in - try - loop 0; - with - No_more_elements -> () + if t.fast then + for idx = 0 to (t.count ()) - 1 do + f idx (t.next ()) + done + else + let rec loop idx = + f idx (t.next()); + loop (idx+1); + in + try + loop 0; + with + No_more_elements -> () let iter2 f t u = - let rec loop () = - f (t.next()) (u.next()); - loop () - in - try - loop () - with - No_more_elements -> () + if t.fast then + for idx = 1 to min (t.count ()) (u.count ()) do + f (t.next ()) (u.next ()) + done + else + let rec loop () = + f (t.next()) (u.next()); + loop () + in + try + loop () + with + No_more_elements -> () let iter2i f t u = - let rec loop idx = - f idx (t.next()) (u.next()); - loop (idx + 1) - in - try - loop 0 - with - No_more_elements -> () + if t.fast then + for idx = 0 to (min (t.count ()) (u.count ())) - 1 do + f idx (t.next ()) (u.next ()) + done + else + let rec loop idx = + f idx (t.next()) (u.next()); + loop (idx + 1) + in + try + loop 0 + with + No_more_elements -> () let fold f init t = - let acc = ref init in - let rec loop() = - acc := f (t.next()) !acc; - loop() - in - try - loop() - with - No_more_elements -> !acc + if t.fast then begin + (* Runs in 61% of the time required by the exception-based + * implementation. + * A tail-recursive approach was 5% slower than the for loop. + *) + let acc = ref init in + for idx = 1 to (t.count ()) do + acc := f (t.next ()) !acc + done; + !acc + end + else + let acc = ref init in + let rec loop() = + acc := f (t.next()) !acc; + loop() + in + try + loop() + with + No_more_elements -> !acc let foldi f init t = - let acc = ref init in - let rec loop idx = - acc := f idx (t.next()) !acc; - loop (idx + 1) - in - try - loop 0 - with - No_more_elements -> !acc + if t.fast then begin + let acc = ref init in + for idx = 0 to (t.count ()) - 1 do + acc := f idx (t.next ()) !acc + done; + !acc + end + else + let acc = ref init in + let rec loop idx = + acc := f idx (t.next()) !acc; + loop (idx + 1) + in + try + loop 0 + with + No_more_elements -> !acc let fold2 f init t u = - let acc = ref init in - let rec loop() = - acc := f (t.next()) (u.next()) !acc; - loop() - in - try - loop() - with - No_more_elements -> !acc + if t.fast then begin + let acc = ref init in + for idx = 0 to (min (t.count ()) (u.count ())) - 1 do + acc := f (t.next ()) (u.next ()) !acc + done; + !acc + end + else + let acc = ref init in + let rec loop() = + acc := f (t.next()) (u.next()) !acc; + loop() + in + try + loop() + with + No_more_elements -> !acc let fold2i f init t u = - let acc = ref init in - let rec loop idx = - acc := f idx (t.next()) (u.next()) !acc; - loop (idx + 1) - in - try - loop 0 - with - No_more_elements -> !acc + if t.fast then begin + let acc = ref init in + for idx = 0 to (min (t.count ()) (u.count ())) - 1 do + acc := f idx (t.next ()) (u.next ()) !acc + done; + !acc + end + else + let acc = ref init in + let rec loop idx = + acc := f idx (t.next()) (u.next()) !acc; + loop (idx + 1) + in + try + loop 0 + with + No_more_elements -> !acc let find f t = + (* Using count is slower on long enums, and only 5-10% faster on very + * short ones, so don't bother. + *) let rec loop () = let x = t.next() in if f x then x else loop() @@ -293,29 +356,62 @@ from2 next (fun () -> filter f (t.clone())) let rec filter_map f t = - let rec next () = - match f (t.next()) with - | None -> next() - | Some x -> x - in + let rec next () = + match f (t.next()) with + | None -> next() + | Some x -> x + in from2 next (fun () -> filter_map f (t.clone())) +(* + * Note that e.next can change, but only from false to true. + * + * TODO: given that ta.count is called O(N) times, perhaps its result should be + * cached locally instead. This could be significant in the case where + * ta is a deeply nested append. + *) let rec append ta tb = - let append_next = ref _dummy in - append_next := (fun () -> - try - ta.next() - with - No_more_elements -> - append_next := tb.next; - !append_next ()); - { - count = (fun () -> ta.count() + tb.count()); - next = (fun () -> !append_next ()); - clone = (fun () -> append (ta.clone()) (tb.clone())); + let e = { + count = _dummy; + next = _dummy; + clone = _dummy; fast = ta.fast && tb.fast; } + and append_count = ref (fun () -> ta.count() + tb.count()) + and append_clone = ref (fun () -> append (ta.clone()) (tb.clone())) + and append_next = ref _dummy + in + append_next := begin + if ta.fast then + (fun () -> + match ta.count () with + | 0 -> + append_count := tb.count; + append_clone := tb.clone; + append_next := tb.next; + e.fast <- tb.fast; + !append_next () + | _ -> ta.next ()) + else + (fun () -> + try + ta.next() + with + No_more_elements -> + append_count := tb.count; + append_next := tb.next; + append_clone := tb.clone; + e.fast <- tb.fast; + !append_next ()) + end; + e.count <- (fun () -> !append_count ()); + e.next <- (fun () -> !append_next ()); + e.clone <- (fun () -> !append_clone ()); + e +(* + * TODO: use t.fast + *) let rec concat t = let concat_ref = ref _dummy in let rec concat_next() = @@ -329,4 +425,4 @@ !concat_ref () in concat_ref := concat_next; - from2 (fun () -> !concat_ref ()) (fun () -> concat (t.clone())) \ No newline at end of file + from2 (fun () -> !concat_ref ()) (fun () -> concat (t.clone())) |
From: Brian H. <bh...@sp...> - 2003-06-06 18:12:43
|
On Fri, 6 Jun 2003, Nicolas Cannasse wrote: > Hi list, > > Here's a module proposal for adding to the ExtLib. > This is a binary search tree ( module BinTree ). > Operations add, remove, find are done in Log(n) when the tree is optimized. > This is a mutable version of the data structure. > Is there a reason you did this as a mutable tree and not the "standard" applicative version? Brian |
From: Amit D. <ad...@Co...> - 2003-06-06 11:22:46
|
> > is there any advantage of > > > > > t : ('a ,'b) tree ref; > > over: > > > > mutable t : ('a, 'b) tree; > It's for recursion, since are left and right trees are both ('a , 'b ) tree > ref. The problem is that you cannot pass a mutable field to a function > like a ref. I'm a bit confused... unless you want to make a change to a subtree from two places, this shouldn't be necessary. Taking a quick peek at the code, it doesn't seem like this happens (but maybe I missed something). If this isn't the case, you could just as easily pass around the unmutable structure, and assign it where you need. > > > Also, what are the relative advantages of having this vs. > > putting a mutable wrapper around Map? Map, if I'm not mistaken, > > makes some guarantees of the trees being balanced (both add > > and remove in Map call the balancing function). > > Map does not allow to have several items with the same key in it. > Calling add is replacing the current binded item if any. > I think it's more convenient to have a structure that can "shadow" items > just like Hashtbl do. Ok, makes sense! However, the main thrust behind my suggestion was more about the worst-case performance of insertion and removal... Map already has the balancing code written, even if a wrapper isn't appropriate, perhaps you could steal'n'hack the bal, add and remove functions from there? -Amit |
From: Nicolas C. <war...@fr...> - 2003-06-06 10:27:06
|
> is there any advantage of > > > type ('a,'b) t = { > > cmp : ('a -> 'a -> int); > > t : ('a ,'b) tree ref; > > } > > over: > > type ('a,'b) t = { > cmp : ('a -> 'a -> int); > mutable t : ('a, 'b) tree; > } > > Doesn't the first version require a little bit more memory? > I don't think it's a big deal, but... It's for recursion, since are left and right trees are both ('a , 'b ) tree ref. The problem is that you cannot pass a mutable field to a function like a ref. > Also, what are the relative advantages of having this vs. > putting a mutable wrapper around Map? Map, if I'm not mistaken, > makes some guarantees of the trees being balanced (both add > and remove in Map call the balancing function). Map does not allow to have several items with the same key in it. Calling add is replacing the current binded item if any. I think it's more convenient to have a structure that can "shadow" items just like Hashtbl do. Nicolas Cannasse |
From: Nicolas C. <war...@fr...> - 2003-06-06 10:26:10
|
> is there any advantage of > > > type ('a,'b) t = { > > cmp : ('a -> 'a -> int); > > t : ('a ,'b) tree ref; > > } > > over: > > type ('a,'b) t = { > cmp : ('a -> 'a -> int); > mutable t : ('a, 'b) tree; > } > > Doesn't the first version require a little bit more memory? > I don't think it's a big deal, but... It's for recursion, since are left and right trees are both ('a , 'b ) tree ref. The problem is that you cannot pass a mutable field to a function like a ref. > Also, what are the relative advantages of having this vs. > putting a mutable wrapper around Map? Map, if I'm not mistaken, > makes some guarantees of the trees being balanced (both add > and remove in Map call the balancing function). Map does not allow to have several items with the same key in it. Calling add is replacing the current binded item if any. I think it's more convenient to have a structure that can "shadow" items just like Hashtbl do. Nicolas Cannasse |
From: Amit D. <ad...@Co...> - 2003-06-06 09:35:20
|
Just a quick question, is there any advantage of > type ('a,'b) t = { > cmp : ('a -> 'a -> int); > t : ('a ,'b) tree ref; > } over: type ('a,'b) t = { cmp : ('a -> 'a -> int); mutable t : ('a, 'b) tree; } Doesn't the first version require a little bit more memory? I don't think it's a big deal, but... Also, what are the relative advantages of having this vs. putting a mutable wrapper around Map? Map, if I'm not mistaken, makes some guarantees of the trees being balanced (both add and remove in Map call the balancing function). -Amit > > This is a multi-part message in MIME format. > > ------=_NextPart_000_0015_01C32C45.52938130 > Content-Type: text/plain; > charset="iso-8859-1" > Content-Transfer-Encoding: 7bit > > Hi list, > > Here's a module proposal for adding to the ExtLib. > This is a binary search tree ( module BinTree ). > Operations add, remove, find are done in Log(n) when the tree is optimized. > This is a mutable version of the data structure. > > Nicolas Cannasse > > ------=_NextPart_000_0015_01C32C45.52938130 > Content-Type: application/octet-stream; > name="binTree.ml" > Content-Transfer-Encoding: 7bit > Content-Disposition: attachment; > filename="binTree.ml" > > > type ('a,'b) tree = > | Empty > | Tree of ('a * 'b * ('a,'b) tree ref * ('a,'b) tree ref) > > type ('a,'b) t = { > cmp : ('a -> 'a -> int); > t : ('a ,'b) tree ref; > } > > let empty() = > { > cmp = compare; > t = ref Empty; > } > > let make cmp = > { > cmp = cmp; > t = ref Empty; > } > > let rec add t key item = > let rec loop r = > match !r with > | Empty -> r := Tree(key,item,ref Empty,ref Empty) > | Tree(k,i,tl,tr) -> > let n = t.cmp k key in > if n = 0 then r := Tree(key,item,tl,ref (Tree(k,i,ref Empty,tr))) > else if n > 0 then loop tl > else loop tr > in > loop t.t > > let rec length t = > let rec loop r = > match !r with > | Empty -> 0 > | Tree(_,_,tl,tr) -> 1+(loop tl)+(loop tr) > in > loop t.t > > let rec depth t = > let rec loop r = > match !r with > | Empty -> 0 > | Tree(_,_,tl,tr) -> 1+(max (loop tl) (loop tr)) > in > loop t.t > > let rec find t key = > let rec loop r = > match !r with > | Empty -> raise Not_found > | Tree(k,i,tl,tr) -> > let n = t.cmp k key in > if n = 0 then i > else if n > 0 then loop tl > else loop tr > in > loop t.t > > let iter f t = > let rec loop r = > match !r with > | Empty -> () > | Tree(k,i,tl,tr) -> > loop tl; > f k i; > loop tr > in > loop t.t > > let copy t = > let rec loop r = > match !r with > | Empty -> ref Empty > | Tree (k,i,tl,tr) -> > ref (Tree(k,i,loop tl,loop tr)) > in > { > cmp = t.cmp; > t = loop t.t; > } > > let clear t = t.t := Empty > > let rec remove t key = > let p = ref Empty in > let rec remove_aux r = > match !r with > | Empty -> raise Not_found > | Tree(k,i,tleft,tright) -> > let n = t.cmp k key in > if n = 0 then r else begin > p := !r; > if n > 0 then remove_aux tleft else remove_aux tright > end > in > let found = remove_aux t.t in > match !found with > | Empty -> assert false > | Tree(k,i,fleft,fright) -> > let attach subtree = > match !p with > | Empty -> t.t := subtree > | Tree(_,_,pleft,pright) -> > if found == pleft then > pleft := subtree > else > pright := subtree > in > if !fright = Empty then attach !fleft > else begin > let lp = ref Empty in > let rec mostleft_aux t = > match !t with > | Empty -> assert false > | Tree(_,_,left,_) -> > if !left = Empty then t else begin > lp := !t; > mostleft_aux left; > end > in > let mostleft = mostleft_aux fright in > let mkey,mitem,mleft,mright = > (match !mostleft with > | Empty -> assert false > | Tree infos -> infos) in > match !lp with > | Empty -> > attach (Tree(mkey,mitem,fleft,mright)) > | Tree(_,_,lpleft,lpright) -> > attach (Tree(mkey,mitem,fleft,fright)); > lpleft := !mright; > end > > type ('a,'b) istack = > | SItem of ('a * 'b) > | STree of ('a , 'b) tree ref > > let rec make_enum f s = > let rec next () = > match !s with > | [] -> raise Enum.No_more_elements > | (SItem x) :: h -> > s := h; > f x > | (STree t) :: h -> > let rec loop t = > match !t with > | Empty -> > s := h; > next() > | Tree (k,i,left,right) -> > s := (SItem (k,i)) :: (STree right) :: !s; > loop left > in > loop t > in > let rec tcount t = > match !t with > | Empty -> 0 > | Tree(_,_,tl,tr) -> 1 + tcount tl + tcount tr > in > let count() = > List.fold_left (fun acc c -> > match c with > | SItem _ -> acc + 1 > | STree t -> acc + tcount t > ) 0 !s > in > let clone() = > make_enum f (ref !s) > in > Enum.make ~next ~count ~clone > > let pairs t = > make_enum (fun x -> x) (ref [STree t.t]) > > let enum t = > make_enum snd (ref [STree t.t]) > > let keys t = > make_enum fst (ref [STree t.t]) > > let optimize t = assert false > > let to_list t = > let rec loop dst r = > match !r with > | Empty -> dst > | Tree(k,i,tl,tr) -> > let dst = loop dst tl in > let r = [ (k,i) ] in > Obj.set_field (Obj.repr dst) 1 (Obj.repr r); > loop r tr > in > let x = [Obj.magic()] in > ignore(loop x t.t); > List.tl x > > let optimize t = > let l = ref (to_list t) in > let len = List.length !l in > let rec opt_list len = > if len = 0 then > Empty > else if len = 1 then begin > match !l with > | [] -> assert false > | (k,i) :: q -> > l := q; > Tree(k,i,ref Empty,ref Empty) > end else begin > let left = opt_list (len/2) in > let k, i = (match !l with > | [] -> assert false > | x :: q -> > l := q; > x) in > let right = opt_list ((len-1)/2) in > Tree(k,i,ref left,ref right) > end > in > t.t := opt_list len > ------=_NextPart_000_0015_01C32C45.52938130 > Content-Type: application/octet-stream; > name="binTree.mli" > Content-Transfer-Encoding: 7bit > Content-Disposition: attachment; > filename="binTree.mli" > > > type ('a,'b) t > > val empty : unit -> ('a,'b) t > val make : ('a -> 'a -> int) -> ('a,'b) t > > (* O(N) *) > val length : ('a,'b) t -> int > val depth : ('a,'b) t -> int > val copy : ('a,'b) t -> ('a,'b) t > val optimize : ('a,'b) t -> unit > > (* log N *) > val add : ('a,'b) t -> 'a -> 'b -> unit > val find : ('a,'b) t -> 'a -> 'b (* raise Not_found *) > val remove : ('a,'b) t -> 'a -> unit (* raise Not_found *) > > val enum : ('a,'b) t -> 'b Enum.t > val keys : ('a,'b) t -> 'a Enum.t > val pairs : ('a,'b) t -> ('a * 'b) Enum.t > val to_list : ('a,'b) t -> ('a * 'b) list > > val iter : ('a -> 'b -> unit) -> ('a,'b) t -> unit > > val clear : ('a,'b) t -> unit > ------=_NextPart_000_0015_01C32C45.52938130-- > > > > ------------------------------------------------------- > This SF.net email is sponsored by: Etnus, makers of TotalView, The best > thread debugger on the planet. Designed with thread debugging features > you've never dreamed of, try TotalView 6 free at www.etnus.com. > _______________________________________________ > ocaml-lib-devel mailing list > oca...@li... > https://lists.sourceforge.net/lists/listinfo/ocaml-lib-devel > |
From: Nicolas C. <war...@fr...> - 2003-06-06 09:30:01
|
> Tree node here takes 3 (for Tree+tuple, and two refs) allocations where > it can take 2 (or 1?) with: > > | Tree of ('a, 'b) tree_rec > > and type ('a, 'b) tree_rec = > { > a : 'a; > b : 'b; > mutable left : ('a, 'b) tree; > mutable right : ('a, 'b) tree; > } > > and this is more readable IMHO. True, thanks. That piece of code is pretty old, I just added the enum features without thinking about the internal representation. I'll update it this way when I have some time. Nicolas Cannasse |
From: Nicolas C. <war...@fr...> - 2003-06-06 07:06:03
|
Hi list, Here's a module proposal for adding to the ExtLib. This is a binary search tree ( module BinTree ). Operations add, remove, find are done in Log(n) when the tree is optimized. This is a mutable version of the data structure. Nicolas Cannasse |
From: MikhailFedotov <mi...@ki...> - 2003-06-05 07:08:56
|
Hi! > This is what I was talking about when I mentioned "makefile hacks". > Unfortunately, I know enough about the build environment of Windows > (despite several years busily repressing memories) that I am > wary of doing and scripting hacks. The only scripting language I am > sure exists is Ocaml. > > Hmm. That might be enough. All I need to do is: > > - move the original code foo.ml to a backup file. > > - concatenate the original code and the unit test code into > the new foo.ml I thought that the only problem is that internal functions are not present in the external interface, and can be solved by extending external inteface to include everything for the test version of the library. This way you'll end up with "testing the library", not just the code, as well as library compiling process, etc. More clean approach, without unwanted surprises. Mikhail |
From: Nicolas C. <war...@fr...> - 2003-06-05 02:33:01
|
> Here's an interesting debate point- what does a 1.0 release of ExtLib look > like? How close are we? > > Starting this discussion is tantaumont to saying "I think it's ready now", > which I'm not. I'm just thinking of maybe getting a rough sketch of what > people want/are working on. > > Here's my short list in approximate order of priority: > - fix the bug in psqueue > - unit tests at least some > - generic red-black tree library > - doubly linked list library > - bit fields/bit sets > > Comments? I think we should first review the current code, implement missing functions, add documentation everywhere, and have a look at several past projects ( CDK and friends ) to see if we've been including all needed "standard" functions. Then we should be able to make a first release, and then continue adding useful modules. Nicolas Cannasse |
From: Nicolas C. <war...@fr...> - 2003-06-05 02:10:09
|
[...] > Asserts get used *in addition to* strong type checking and unit tests. > And stay in the final "production" code. Yes, this is way overkill. As > the guy said when asked what do with his mother-in-law's mortal remains, > "embalm, cremate, then bury. Take no chances." There is a flag in ocamlc that does what you want : -noassert : Don't compile assertion checks. That way you may have a "debug" version and "release" version of the library. Note that the "assert false" are not removed, because they're handled as special cases. Nicolas Cannasse |
From: Brian H. <bri...@ql...> - 2003-06-04 22:35:25
|
Here's an interesting debate point- what does a 1.0 release of ExtLib look like? How close are we? Starting this discussion is tantaumont to saying "I think it's ready now", which I'm not. I'm just thinking of maybe getting a rough sketch of what people want/are working on. Here's my short list in approximate order of priority: - fix the bug in psqueue - unit tests at least some - generic red-black tree library - doubly linked list library - bit fields/bit sets Comments? Brian |
From: Brian H. <bh...@sp...> - 2003-06-04 22:02:57
|
On Thu, 5 Jun 2003, Mikhail Fedotov wrote: > Hi! > > This won't be unit testing in the usual sense then. Unit > testing covers external interface of the library; the main > point is to figure out that something is broken; as long as > it is known, it is easy to find exact error line, even by > putting "let () = print_string error_here in" in the code. > > Including tests for internal functions does not buy much > but makes the whole thing more complex. This kind of > testing is usually implemented by inserting asserts in C > code and by raising Invalid_argument in some ocaml > functions. My general experience has been that the smaller the hunk of code you test, the more likely you are to find problems and the easier it is to get full coverage (all possible conditions are tested). Yes, this makes things more complex. What I want is classic C conditional compilation. I want to be able to do "make test" which builds a copy of the libraries with unit testing enabled, and runs the unit tests. "make" would then build copies of the library sans the unit test code for "production". Asserts get used *in addition to* strong type checking and unit tests. And stay in the final "production" code. Yes, this is way overkill. As the guy said when asked what do with his mother-in-law's mortal remains, "embalm, cremate, then bury. Take no chances." > > If you really need to test internal function I think (with > my limited ocaml expirience) that it is possible to compile > a special version of the library by copying library.ml to > testlibrary.ml and creating the specil version of the > library out of it which would include all the functions. I > don't remember if .mli file is strictly required (I've > never tried to build a library without it), but if it is > not, then it is even easier. > This is what I was talking about when I mentioned "makefile hacks". Unfortunately, I know enough about the build environment of Windows (despite several years busily repressing memories) that I am wary of doing and scripting hacks. The only scripting language I am sure exists is Ocaml. Hmm. That might be enough. All I need to do is: - move the original code foo.ml to a backup file. - concatenate the original code and the unit test code into the new foo.ml - do the same with foo.mli - Compile foo.ml[i] - run the unit tests - delete the new foo.ml[i] - move the originals back Brian |
From: Mikhail F. <mi...@ki...> - 2003-06-04 21:00:07
|
Hi! > > > > My preference would be to be able to compile the > > > > library *without* the unit testing code in it. > > > > This will require some thought, and probably > > > > some makefile hacking. > > > > > > Why Brian ? > > > I mean, having a big ExtLib.cma is not a problem, > since > > > we're using different modules, > > > > Unit testing code is usually implemented as a seperate > > program which uses the library, not as part of the > library. > > > > This actually limits the ability of the unit testing to > nail down what > just broke. For example, the PSQueue module has internal > functions > "rotate_right" and "rotate_left". I'd like the unit > tests to test those > functions independently- and balance as seperate from > rotate. Allowing me > to tell if I broke insert, balance, or a rotate. > > My inclination is to agree with Nicolas and just toss the > test functions > into the library and worry about size when it becomes a > problem. This won't be unit testing in the usual sense then. Unit testing covers external interface of the library; the main point is to figure out that something is broken; as long as it is known, it is easy to find exact error line, even by putting "let () = print_string error_here in" in the code. Including tests for internal functions does not buy much but makes the whole thing more complex. This kind of testing is usually implemented by inserting asserts in C code and by raising Invalid_argument in some ocaml functions. If you really need to test internal function I think (with my limited ocaml expirience) that it is possible to compile a special version of the library by copying library.ml to testlibrary.ml and creating the specil version of the library out of it which would include all the functions. I don't remember if .mli file is strictly required (I've never tried to build a library without it), but if it is not, then it is even easier. Mikhail |
From: Brian H. <bh...@sp...> - 2003-06-04 19:12:08
|
On Wed, 4 Jun 2003, Mikhail Fedotov wrote: > Hi! > > > > My preference would be to be able to compile the > > > library *without* the unit testing code in it. > > > This will require some thought, and probably > > > some makefile hacking. > > > > Why Brian ? > > I mean, having a big ExtLib.cma is not a problem, since > > we're using different modules, > > Unit testing code is usually implemented as a seperate > program which uses the library, not as part of the library. > This actually limits the ability of the unit testing to nail down what just broke. For example, the PSQueue module has internal functions "rotate_right" and "rotate_left". I'd like the unit tests to test those functions independently- and balance as seperate from rotate. Allowing me to tell if I broke insert, balance, or a rotate. My inclination is to agree with Nicolas and just toss the test functions into the library and worry about size when it becomes a problem. Brian |
From: Nicolas C. <war...@fr...> - 2003-06-04 05:56:06
|
> > > My preference would be to be able to compile the > > > library *without* the unit testing code in it. > > > This will require some thought, and probably > > > some makefile hacking. > > > > Why Brian ? > > I mean, having a big ExtLib.cma is not a problem, since > > we're using different modules, > > Unit testing code is usually implemented as a seperate > program which uses the library, not as part of the library. Mea Culpa ! I really misunderstood the topic of this thread :) I was thinking that we could add an "Unit test library" to the ExtLib, which would of course be compiled with it. As for "writing unit tests for ExtLib" , if someone have enough will to so so, then why not... but I don't see myself covering all Enum cases that can occur with unit tests :-) I think we can make releases/bugfix enough often to wait for the users to find bugs. Nicolas Cannasse |
From: Mikhail F. <mi...@ki...> - 2003-06-04 05:43:16
|
Hi! > > My preference would be to be able to compile the > > library *without* the unit testing code in it. > > This will require some thought, and probably > > some makefile hacking. > > Why Brian ? > I mean, having a big ExtLib.cma is not a problem, since > we're using different modules, Unit testing code is usually implemented as a seperate program which uses the library, not as part of the library. Mikhail |
From: Nicolas C. <war...@fr...> - 2003-06-04 02:50:32
|
> let default_has_next next count = > let lookahead = ref None in > let has_next' () = > match lookahead with > Some x -> true > | None -> try > lookahead := Some (next ()); > true > with No_more_elements -> false > and next' () = > match lookahead with > Some x -> > lookahead := None; > x > | None -> > next() > and count' () -> > match lookahead with > Some _ -> 1 + (count ()) > | None -> count() This is a little more complicated than that... Actually calling count() can make next being called ( if this is a count calling force ), and then the return of count() will be the total number of elements, including the lookhead, so you'll count 1 more element since you're adding 1. You could first call count and then match again the lookahead to see if still Some.... but that's pretty inneficient. You can have a look at current Enum.peek implementation, I think it resolves the problem in a nice and elegant way. Nicolas Cannasse |
From: Nicolas C. <war...@fr...> - 2003-06-04 02:48:41
|
> > > I think it would be a good idea to add unit tests to all the library > > > functions using OUnit: > > > http://home.wanadoo.nl/maas/ocaml/ > > > > > > I think unit tests are *less* necessary in Ocaml due to our wonderful type > > > system, but they're still a good idea. > > > > I agree, but I'm not familiar enough with others langages Unit tests library > > to see if this implementation is a convenient way of doing unit tests. Maybe > > we could write a library mixing unit and benchmarks tests ? ( something > > similar to Doug Bagley's one ). Theses two should stick well together. > > > > My preference would be to be able to compile the library *without* the > unit testing code in it. This will require some thought, and probably > some makefile hacking. Why Brian ? I mean, having a big ExtLib.cma is not a problem, since we're using different modules, the ocaml linker will find itself which ones to choose. Of course, you also need the CMIs since there is currently no way of packaging several CMIs. But compilation speed shouldn't be a problem. BTW, I'm planning to add soon XmlLight to the ExtLib , and I think the best is to link XmlLight modules with others, even if we have separate compilation. About compilation and makefiles issues, I'm also planning to add some options to ocamake so you can write some kind of Ocamakefile and you don't need make any more (with all the problems of calling RM or DEL for cleaning, / against \ in paths , and so on...). Then the ExtLib will be simply compiled (installed ?) by running "ocamake" in its directory. Nicolas Cannasse |