* [Caml-list] looping recursion @ 2004-07-27 23:43 briand 2004-07-28 0:27 ` John Prevost 2004-07-28 0:37 ` skaller 0 siblings, 2 replies; 44+ messages in thread From: briand @ 2004-07-27 23:43 UTC (permalink / raw) To: caml-list Well without an eof-object to check for (as in scheme) I've started using the following code chunk to read all the lines out of a file : let read_file filename = let file = open_in filename in let rec proc line = print_string line; print_newline(); try proc (input_line file); with End_of_file -> true; in proc (input_line file); works great until you read enough lines and then you get: Stack overflow during evaluation (looping recursion?). naturally I suspected that the try was perhaps ruining the tail-callness of the code, so I moved it outside of the recursive proc, i.e. let rec proc line = ... in try proc (input_line file); with End_of_file -> true; and that works just fine. Is there a better way to just pull in all the lines of a file ? I've just gotten used to doing it like this, because in scheme it's very clean. And since I'm not actually accumulating anything, I'm most confused as to how the stack space is disappearing. Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-27 23:43 [Caml-list] looping recursion briand @ 2004-07-28 0:27 ` John Prevost 2004-07-28 0:38 ` John Prevost 2004-07-28 1:05 ` briand 2004-07-28 0:37 ` skaller 1 sibling, 2 replies; 44+ messages in thread From: John Prevost @ 2004-07-28 0:27 UTC (permalink / raw) To: Ocaml Mailing List On Tue, 27 Jul 2004 16:43:10 -0700, briand@aracnet.com <briand@aracnet.com> wrote: > Well without an eof-object to check for (as in scheme) I've started > using the following code chunk to read all the lines out of a file : {...} > works great until you read enough lines and then you get: > > Stack overflow during evaluation (looping recursion?). > > naturally I suspected that the try was perhaps ruining the > tail-callness of the code, so I moved it outside of the recursive > proc, i.e. Yup--this is one of the big things you have to watch out for when doing tail loops. The easy way to think of it is "try always makes stack frames, so use try at the top level and then escape all the way out". In essence, if there's a try block around your expression, it's not in tail position (even if every try block will return the same thing.) Here's an example of a loop that makes that obvious: exception Boom let rec loop x = try if x < 100 then loop (x + x) else raise Boom with Boom -> x ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 0:27 ` John Prevost @ 2004-07-28 0:38 ` John Prevost 2004-07-28 1:17 ` skaller 2004-07-28 1:05 ` briand 1 sibling, 1 reply; 44+ messages in thread From: John Prevost @ 2004-07-28 0:38 UTC (permalink / raw) To: Ocaml Mailing List Oops. Hit the send button before I meant to... Anyway, that little loop with Boom example should show why the try prevents the inner call from being in tail position: because the try block's expressions can refer to variables in the scope of the original function call, there's a possible continuation (you raise an exception) that brings those variables back into scope. The compiler *could*, I suppose, do some magic and notice when there are no locally bound variables in the exception code path, but even just thinking about how that might be implemented, I think it could get messy. What if it's not always the same try block that's around the call that would otherwise be a tail call? What if the fact that there are multiple exception handlers is important, like: exception Boom2 of int let rec loop2 x = try if x < 100 then loop2 (x + x) else raise (Boom2 0) with Boom2 y -> raise (Boom2 (y + 1)) let use_loop2 x = try loop2 x with Boom2 y -> y In this second example, use_loop2 is using the exceptions in loop2 to calculate the result. (Admittedly, this is a bizarre little setup.) So figuring out if it's "safe" to make it a tail call is hard--not to mention it would probably end up confusing the user even more if it did work only part of the time. (How do I change this so it's recongnized as tail recursive again?) Hope this helps. :) I tried to find an FAQ entry on this, because I thought there was one, but was unable to find anything. John. ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 0:38 ` John Prevost @ 2004-07-28 1:17 ` skaller 0 siblings, 0 replies; 44+ messages in thread From: skaller @ 2004-07-28 1:17 UTC (permalink / raw) To: John Prevost; +Cc: Ocaml Mailing List On Wed, 2004-07-28 at 10:38, John Prevost wrote: > > exception Boom2 of int > > let rec loop2 x = > try > if x < 100 then loop2 (x + x) else raise (Boom2 0) > with Boom2 y -> raise (Boom2 (y + 1)) > > let use_loop2 x = try loop2 x with Boom2 y -> y > > In this second example, use_loop2 is using the exceptions in loop2 to > calculate the result. (Admittedly, this is a bizarre little setup.) I'm actually doing something like this (unfortunately) in the Felix compiler. Basically, Felix has overloading and requires the function argument type to be specified, but the return type is deduced bottom up by actually examining the function's return statement argument type. Unfortunately, this situation is complicated by the fact a function can call itself. However, we cant just plug in a type variable for the function's return type, calculate the result, and then unify to eliminate the variable, as Ocaml could, because in Felix functions can be overloaded. So to calculate in function f, what the type of f(a) is, we again have to do overload resolution. Having done that we need the return type of the function .. but we can't calculate it because that's what we're already in the middle of doing, and we'd get an infinite loop. you might think a type variable could be introduced here, and eliminated when we pop back, but that isn't so. The problem is we might have in f: return g (f a); and now, the recursion isn't tail, and we have to have a definite type for f a, in order to calculate which g is called, in order to calculate the type of f. So I just throw an exception and ignore that return statement. Such recursions (in the client Felix code) usually have conditionals wrapped around them and one branch had better give a result: otherwise the client must specify the return type. [I suspect in terminating code this cannot be needed but I'm not sure] the actual algorithm unifies the specified return type (if given) with all the types of return statements (which don't lead to infinite recursion). I have tried to localise exception handling in my code but this is one case where I couldn't figure out how to do so. It is of course vital that the exception handlers stack up here, so the exceptions propagate far enough but no further. [The code is extremely messy and I'm not even sure it gives the correct result when it does succeed :] -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 0:27 ` John Prevost 2004-07-28 0:38 ` John Prevost @ 2004-07-28 1:05 ` briand 2004-07-28 1:43 ` Brian Hurt 1 sibling, 1 reply; 44+ messages in thread From: briand @ 2004-07-28 1:05 UTC (permalink / raw) To: John Prevost; +Cc: Ocaml Mailing List After sufficient googling I finally found a post which yields the answer : let readfile chan = let rec loop rlst = let line, eof = try (input_line chan), false with | End_of_file -> "", true in if not eof then loop (line :: rlst) else List.rev rlst in loop [] ;; but then it gets better. Imagine my surprise when the following failed: let data = readfile (open_in "data") in let split_lines = List.map (fun line -> line; ) data in print_string "Done.\n";; That's just not good ! I can't even use standard map without the stack blowing up ? This is a Bad Thing (TM). Oh but wait. I peruse the manual. map_rev is what I want. So I'm going to make a bold statement that .map which blows up shouldn't even exist and .rev_map should be renamed to .map. Naturally someone will be telling me very shortly why you can't do this... Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 1:05 ` briand @ 2004-07-28 1:43 ` Brian Hurt 2004-07-28 2:49 ` briand 2004-07-28 7:27 ` [Caml-list] looping recursion skaller 0 siblings, 2 replies; 44+ messages in thread From: Brian Hurt @ 2004-07-28 1:43 UTC (permalink / raw) To: briand; +Cc: John Prevost, Ocaml Mailing List On Tue, 27 Jul 2004 briand@aracnet.com wrote: > That's just not good ! I can't even use standard map without the > stack blowing up ? This is a Bad Thing (TM). Take a look at ExtLib: http://sourceforge.net/projects/ocaml-lib/ This contains a drop-in compatible replacement to the List library, which includes a List.map function that doesn't blow up. If your list is that long, I'd actually recommend doing one of two things: 1) Switch off your list data structure for a more complicated data structure, like a Set. If you do any sort of complicated access patterns, this will likely be a big win, or 2) If you only ever need to iterate through the list (and thus have simple access requirements), consider switching over to using an ExtLib style enum (same basic concept as Java's Iterator class). This allows you do "wavefront" processing- apply the map to the list items as you read them in. Very long lists are a sign that you're using the wrong data structure. -- "Usenet is like a herd of performing elephants with diarrhea -- massive, difficult to redirect, awe-inspiring, entertaining, and a source of mind-boggling amounts of excrement when you least expect it." - Gene Spafford Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 1:43 ` Brian Hurt @ 2004-07-28 2:49 ` briand 2004-07-28 3:12 ` Brian Hurt ` (2 more replies) 2004-07-28 7:27 ` [Caml-list] looping recursion skaller 1 sibling, 3 replies; 44+ messages in thread From: briand @ 2004-07-28 2:49 UTC (permalink / raw) To: Brian Hurt; +Cc: John Prevost, Ocaml Mailing List >>>>> "Brian" == Brian Hurt <bhurt@spnz.org> writes: Brian> Take a look at ExtLib: Brian> http://sourceforge.net/projects/ocaml-lib/ thanks for the link. Brian> This contains a drop-in compatible replacement to the List Brian> library, which includes a List.map function that doesn't blow Brian> up. Brian> If your list is that long, I'd actually recommend doing one Brian> of two things: it really is. although I'd actually like it to be an array. it's the classic situation, read whole file - iterate on elements. easy to do in a list since I don't need to know the size. some minor tweaks and I can include the size in a data file and just fill an array directly. however, my larger point is the fact that the "standard" map blows up like that. as a long time scheme user I just find that plain weird. Now Everybody else seems to think I'm weird because I think that's weird ;-) Brian> Very long lists are a sign that you're using the wrong data Brian> structure. I'm not sure I understand that. I have 10^6 data points to work on. I'm thinking a list or an array is the right data structure. Although an array is more right. I mean if you are not worried about efficiency then incremental transformation by map'ing is a very elegant and clean way to go about doing that sort of work. Part of the problem is that powerful computers make for lazy programmers :-) It's just so easy to read the 10^6 elements into a list and then just keep map'ing them to the final value when it only takes seconds to do :-) If it took 2 minutes I'd be more inclined to think about the problem. Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 2:49 ` briand @ 2004-07-28 3:12 ` Brian Hurt 2004-07-28 3:20 ` Brian Hurt 2004-07-28 5:54 ` brogoff 2 siblings, 0 replies; 44+ messages in thread From: Brian Hurt @ 2004-07-28 3:12 UTC (permalink / raw) To: briand; +Cc: John Prevost, Ocaml Mailing List On Tue, 27 Jul 2004 briand@aracnet.com wrote: > it really is. > although I'd actually like it to be an array. > it's the classic situation, read whole file - iterate on elements. > easy to do in a list since I don't need to know the size. > > some minor tweaks and I can include the size in a data file and just > fill an array directly. > > however, my larger point is the fact that the "standard" map blows up > like that. as a long time scheme user I just find that plain weird. > Now Everybody else seems to think I'm weird because I think that's > weird ;-) > > Brian> Very long lists are a sign that you're using the wrong data > Brian> structure. > > I'm not sure I understand that. I have 10^6 data points to work on. > I'm thinking a list or an array is the right data structure. Although > an array is more right. I mean if you are not worried about > efficiency then incremental transformation by map'ing is a very > elegant and clean way to go about doing that sort of work. > > Part of the problem is that powerful computers make for lazy > programmers :-) It's just so easy to read the 10^6 elements into a > list and then just keep map'ing them to the final value when it only > takes seconds to do :-) If it took 2 minutes I'd be more inclined to > think about the problem. > You want an enumeration. You're basically taking a file, reading it into a list, mapping the list multiple different times, and then writting out the result, right? Well, enumerations work the same way. You take the file, and create an enumeration of the lines of the file (there's a function that does that). You then map the enumeration multiple times, same map functions. Then you write out the result. As a programmer, it doesn't change the problem all that much. But it allows the library to optimize things. Maps of enumerations are applied lazily- that means they aren't calculated until the result is needed. By creating the enumeration of the file doesn't read the file at that point. When you go to write the result out, the output function grabs the first line from the enumeration. This causes the enumeration to actually go read the first line of the input file, and apply all the appropriate maps to it, before writting it to the output file. And so on. You write the code as if the entire data structure were present right now, but it works like a stream processor. With no extra work on the part of the programmer. -- "Usenet is like a herd of performing elephants with diarrhea -- massive, difficult to redirect, awe-inspiring, entertaining, and a source of mind-boggling amounts of excrement when you least expect it." - Gene Spafford Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 2:49 ` briand 2004-07-28 3:12 ` Brian Hurt @ 2004-07-28 3:20 ` Brian Hurt 2004-07-28 5:54 ` brogoff 2 siblings, 0 replies; 44+ messages in thread From: Brian Hurt @ 2004-07-28 3:20 UTC (permalink / raw) To: briand; +Cc: Ocaml Mailing List Hit "send" before I meant too- sorry. On Tue, 27 Jul 2004 briand@aracnet.com wrote: > however, my larger point is the fact that the "standard" map blows up > like that. as a long time scheme user I just find that plain weird. > Now Everybody else seems to think I'm weird because I think that's > weird ;-) I agree with you. Or rather, I don't think it's weird so much as really bad. The problem is correct, and (while not as efficient as it might be) it sounds like it's not horribly inefficient either. It's certainly not a bad idea to get it working first, and then worry about algorithmic inefficiencies. I'd much rather have a program that takes two minutes to return the right answer, than a program that returns the wrong answer in 2 seconds (or segfaults in 2 milliseconds, but that isn't a danger in Ocaml). -- "Usenet is like a herd of performing elephants with diarrhea -- massive, difficult to redirect, awe-inspiring, entertaining, and a source of mind-boggling amounts of excrement when you least expect it." - Gene Spafford Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 2:49 ` briand 2004-07-28 3:12 ` Brian Hurt 2004-07-28 3:20 ` Brian Hurt @ 2004-07-28 5:54 ` brogoff 2004-07-28 7:22 ` Alex Baretta 2 siblings, 1 reply; 44+ messages in thread From: brogoff @ 2004-07-28 5:54 UTC (permalink / raw) To: briand; +Cc: Brian Hurt, John Prevost, Ocaml Mailing List On Tue, 27 Jul 2004 briand@aracnet.com wrote: > >>>>> "Brian" == Brian Hurt <bhurt@spnz.org> writes: > it really is. > although I'd actually like it to be an array. > it's the classic situation, read whole file - iterate on elements. > easy to do in a list since I don't need to know the size. > > some minor tweaks and I can include the size in a data file and just > fill an array directly. > > however, my larger point is the fact that the "standard" map blows up > like that. as a long time scheme user I just find that plain weird. > Now Everybody else seems to think I'm weird because I think that's > weird ;-) > > Brian> Very long lists are a sign that you're using the wrong data > Brian> structure. > > I'm not sure I understand that. I've heard the argument plenty of times, and I'm familiar with all of the common data structures (and the uncommon ones in Okasaki's book), and I don't buy that argument either. It's a limitation of the current OCaml list library functions and the current implementation of OCaml. You can easily fix it yourself by writing mapand friends to be tail recursive in the straightforward way (suboptimal efficiency, but better than failing) or the non-obvious way using Obj to make the equivalent of set-cdr! (what ExtLib does) or wait until the implementors decide to fix this by adding a language feature and reimplementing List. Don't hold your breath for that last option. > Part of the problem is that powerful computers make for lazy > programmers :-) It's just so easy to read the 10^6 elements into a > list and then just keep map'ing them to the final value when it only > takes seconds to do :-) If it took 2 minutes I'd be more inclined to > think about the problem. Sometimes lists are best. If lists are OK for 100, 1_000, or 10_000 items, why not 100_000 or 1_000_000? Map and friends shouldn't blow stack. -- Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 5:54 ` brogoff @ 2004-07-28 7:22 ` Alex Baretta 2004-07-28 16:38 ` brogoff 0 siblings, 1 reply; 44+ messages in thread From: Alex Baretta @ 2004-07-28 7:22 UTC (permalink / raw) To: brogoff, Ocaml brogoff wrote: > Sometimes lists are best. If lists are OK for 100, 1_000, or 10_000 items, > why not 100_000 or 1_000_000? Map and friends shouldn't blow stack. > > -- Brian Actually, it's not that bad. Sometimes I design an algorithm in terms of basic data structures, such as lists, and transformations applied through higher order iterators. Such "featurs" as the stack overflow in List.map or the Linux out-of-memory SIGKILL give me a clear indication of the simplemindedness of such algorithms and force me to rethink them in terms of more efficient structures and techniques, typically streams and laziness. Alex ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 7:22 ` Alex Baretta @ 2004-07-28 16:38 ` brogoff 2004-07-28 19:40 ` Jon Harrop 2004-07-29 6:28 ` Alex Baretta 0 siblings, 2 replies; 44+ messages in thread From: brogoff @ 2004-07-28 16:38 UTC (permalink / raw) To: Alex Baretta; +Cc: Ocaml On Wed, 28 Jul 2004, Alex Baretta wrote: > brogoff wrote: > > Sometimes lists are best. If lists are OK for 100, 1_000, or 10_000 items, > > why not 100_000 or 1_000_000? Map and friends shouldn't blow stack. > > > > -- Brian > > Actually, it's not that bad. Well, I'm still on the caml-list, so of course it isn't *that* bad. Also, as I said, if you need a tail recursive map over built in lists, you have at least two options. Unfortunately, my preference is to use Obj, which IMO points to a deficiency in the core language. Most, maybe all, of my issues with OCaml, amount to petty complaints, no showstoppers or dealbreakers. Hey, at least I'm not whining about the license! > Sometimes I design an algorithm in terms of basic data structures, such > as lists, and transformations applied through higher order iterators. > Such "featurs" as the stack overflow in List.map or the Linux > out-of-memory SIGKILL give me a clear indication of the simplemindedness > of such algorithms and force me to rethink them in terms of more > efficient structures and techniques, typically streams and laziness. There are quite a few cases where singly linked lists are the most efficient data structure, and they're just right for the problem. Streams and laziness are not at all efficient in comparison. Stack overflow commonly happens when one of my users (yup, I have users of my code who aren't me ;-) decides to run on data much larger than I'd anticipated. Also, lists are builtin, and have syntactic support in OCaml, by which I mean nice, easy to read syntax. So the language encourages you to use them. -- Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 16:38 ` brogoff @ 2004-07-28 19:40 ` Jon Harrop 2004-07-28 20:18 ` Brandon J. Van Every 2004-07-28 21:22 ` brogoff 2004-07-29 6:28 ` Alex Baretta 1 sibling, 2 replies; 44+ messages in thread From: Jon Harrop @ 2004-07-28 19:40 UTC (permalink / raw) To: Ocaml On Wednesday 28 July 2004 17:38, brogoff wrote: > > brogoff wrote: > > > Sometimes lists are best. If lists are OK for 100, 1_000, or 10_000 > > > items, why not 100_000 or 1_000_000? Map and friends shouldn't blow > > > stack. Creating an int list with 1,000,000 elements and applying List.map using ocamlopt works fine here, and took a meagre 3.6 secs. If you must use bytecode for this then just increase the stack size limit, for example to 8Mb: export OCAMLRUNPARAM='l=8M' Then it runs fine, in 10.7 secs here. Wow, that's quite fast... :-) > Well, I'm still on the caml-list, so of course it isn't *that* bad. Also, > as I said, if you need a tail recursive map over built in lists, you have > at least two options. Unfortunately, my preference is to use Obj, which IMO > points to a deficiency in the core language. No! That points to a severe deficiency in your programming style. OCaml has been developed and used by a great many very clever people, and me. If you're doing things like that then you should immediately stop and think what you might be doing wrong. Perhaps you picked the bad style up at a Seattle OCaml user group meeting? What's wrong with List.rev_map, List.rev (List.rev_map ...), increasing the size of the VM's stack, using native code or even writing your own, tail-recursive map? Cheers, Jon. ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* RE: [Caml-list] looping recursion 2004-07-28 19:40 ` Jon Harrop @ 2004-07-28 20:18 ` Brandon J. Van Every 2004-07-29 6:01 ` Alex Baretta 2004-07-28 21:22 ` brogoff 1 sibling, 1 reply; 44+ messages in thread From: Brandon J. Van Every @ 2004-07-28 20:18 UTC (permalink / raw) To: caml Jon Harrop wrote: > > No! That points to a severe deficiency in your programming > style. OCaml has > been developed and used by a great many very clever people, > and me. If you're > doing things like that then you should immediately stop and > think what you might be doing wrong. Although you might have a point, your need to bluster lends no weight. > Perhaps you picked the bad style up at > a Seattle OCaml user group meeting? Did you learn to debate from a politician, in soundbytes? Cheers, www.indiegamedesign.com Brand*n Van Every S*attle, WA Praise Be to the caml-list Bayesian filter! It blesseth my postings, it is evil crap! evil crap! Bigarray! Unboxed! Overhead! Wondering! chant chant chant... ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 20:18 ` Brandon J. Van Every @ 2004-07-29 6:01 ` Alex Baretta 0 siblings, 0 replies; 44+ messages in thread From: Alex Baretta @ 2004-07-29 6:01 UTC (permalink / raw) To: Brandon J. Van Every, jon; +Cc: caml Brandon J. Van Every wrote: > Jon Harrop wrote: > >>No! That points to a severe deficiency in your programming >>style. OCaml has >>been developed and used by a great many very clever people, >>and me. If you're >>doing things like that then you should immediately stop and >>think what you might be doing wrong. > > > Although you might have a point, your need to bluster lends no weight. > > >>Perhaps you picked the bad style up at >>a Seattle OCaml user group meeting? > > > Did you learn to debate from a politician, in soundbytes? May I respectfully suggest that you guys flame each other to death elsewhere? This place is for technical discussions, not for personal insults. Alex ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 19:40 ` Jon Harrop 2004-07-28 20:18 ` Brandon J. Van Every @ 2004-07-28 21:22 ` brogoff 2004-07-29 9:13 ` Daniel Andor 1 sibling, 1 reply; 44+ messages in thread From: brogoff @ 2004-07-28 21:22 UTC (permalink / raw) To: Ocaml On Wed, 28 Jul 2004, Jon Harrop wrote: > On Wednesday 28 July 2004 17:38, brogoff wrote: > > > brogoff wrote: > > > > Sometimes lists are best. If lists are OK for 100, 1_000, or 10_000 > > > > items, why not 100_000 or 1_000_000? Map and friends shouldn't blow > > > > stack. > > Creating an int list with 1,000,000 elements and applying List.map using > ocamlopt works fine here, and took a meagre 3.6 secs. > > If you must use bytecode for this then just increase the stack size limit, for > example to 8Mb: > > export OCAMLRUNPARAM='l=8M' > > Then it runs fine, in 10.7 secs here. Wow, that's quite fast... :-) I'm going to guess that you don't have much OCaml code running at a customer site. Yes, I'm aware that stack size can be reset. Oh joy. I guess we don't need tail call elimination at all then? > > Well, I'm still on the caml-list, so of course it isn't *that* bad. Also, > > as I said, if you need a tail recursive map over built in lists, you have > > at least two options. Unfortunately, my preference is to use Obj, which IMO > > points to a deficiency in the core language. > > No! That points to a severe deficiency in your programming style. OCaml has > been developed and used by a great many very clever people, and me. If you're > doing things like that then you should immediately stop and think what you > might be doing wrong. I guess all of the authors of ExtLib, who, last time I checked, used a set_cdr approach, are also tyros compared to you? > Perhaps you picked the bad style up at a Seattle OCaml user group meeting? Very classy. :-( > What's wrong with List.rev_map, List.rev (List.rev_map ...), increasing the > size of the VM's stack, using native code or even writing your own, > tail-recursive map? I'm pretty damned well aware that I can reverse a rev mapped list. Does it occur to you that that is not efficient? Have you tried comparing the run times of this versus set_cdr version? Where were you when the "tail recursion modulo cons" discussion came up? Do you understand that optimization? Here's a pointer for you http://caml.inria.fr/archives/200306/msg00254.html By my measurements, even for small lists, the set_cdr version was as fast (a little faster even) than List.map, and it had the nice property of not failing with huge lists. -- Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 21:22 ` brogoff @ 2004-07-29 9:13 ` Daniel Andor 2004-07-29 9:25 ` Keith Wansbrough ` (2 more replies) 0 siblings, 3 replies; 44+ messages in thread From: Daniel Andor @ 2004-07-29 9:13 UTC (permalink / raw) To: Ocaml On Wednesday 28 July 2004 10:22 pm, brogoff wrote: > On Wed, 28 Jul 2004, Jon Harrop wrote: > > On Wednesday 28 July 2004 17:38, brogoff wrote: > > > > brogoff wrote: > > > > > Sometimes lists are best. If lists are OK for 100, 1_000, or 10_000 > > > > > items, why not 100_000 or 1_000_000? Map and friends shouldn't blow > > > > > stack. [...] > > What's wrong with List.rev_map, List.rev (List.rev_map ...), increasing > > the size of the VM's stack, using native code or even writing your own, > > tail-recursive map? > > I'm pretty damned well aware that I can reverse a rev mapped list. If you've got the mutable list version of map to hand, and you are happy with that, then that's clearly the way to go. But the rev version of map is the same linear complexity as all the map functions, and is not much more inefficient than the stack version: CPU or memory-wise (given my limited understanding of the GC). So if you were happy with List.map, except that it blew up on you, then you should definitely be happy with List.rev (List.rev_map ..). Lemme try it out (10^6 elements): ocamlc: rev rev_map version: 2 WALL ( 1.19 usr + 0.02 sys = 1.21 CPU) vanilla map: 7 WALL ( 6.50 usr + 0.09 sys = 6.59 CPU) ocamlopt: rev rev_map version: 1 WALL ( 0.81 usr + 0.03 sys = 0.84 CPU) vanilla map: 2 WALL ( 2.45 usr + 0.02 sys = 2.47 CPU) Wow, that was unexpected! > Does it occur to you that that is not efficient? Not any more! Daniel. ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 9:13 ` Daniel Andor @ 2004-07-29 9:25 ` Keith Wansbrough 2004-07-29 9:41 ` Nicolas Cannasse 2004-07-29 9:57 ` Xavier Leroy 2004-07-29 10:11 ` skaller 2004-07-29 12:41 ` brogoff 2 siblings, 2 replies; 44+ messages in thread From: Keith Wansbrough @ 2004-07-29 9:25 UTC (permalink / raw) To: Daniel Andor; +Cc: Ocaml > Lemme try it out (10^6 elements): > > ocamlc: > rev rev_map version: > 2 WALL ( 1.19 usr + 0.02 sys = 1.21 CPU) > vanilla map: > 7 WALL ( 6.50 usr + 0.09 sys = 6.59 CPU) > > ocamlopt: > rev rev_map version: > 1 WALL ( 0.81 usr + 0.03 sys = 0.84 CPU) > vanilla map: > 2 WALL ( 2.45 usr + 0.02 sys = 2.47 CPU) OK, so why is List.map in the OCaml standard library implemented the vanilla way rather than the rev rev_map way? If it's such a big win, it seems foolish to have a broken implementation for such a crucial function. (BTW, if you want efficient (and pure) mapping and filtering over long streams, you should consider using lazy lists. A good compiler (like GHC) will do the deforestation optimisation, so the list is never even allocated[1].) [1] unless you make use of persistence, of course. --KW 8-) ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 9:25 ` Keith Wansbrough @ 2004-07-29 9:41 ` Nicolas Cannasse 2004-07-29 9:57 ` Xavier Leroy 1 sibling, 0 replies; 44+ messages in thread From: Nicolas Cannasse @ 2004-07-29 9:41 UTC (permalink / raw) To: Daniel Andor, Keith Wansbrough; +Cc: Ocaml > > Lemme try it out (10^6 elements): > > > > ocamlc: > > rev rev_map version: > > 2 WALL ( 1.19 usr + 0.02 sys = 1.21 CPU) > > vanilla map: > > 7 WALL ( 6.50 usr + 0.09 sys = 6.59 CPU) > > > > ocamlopt: > > rev rev_map version: > > 1 WALL ( 0.81 usr + 0.03 sys = 0.84 CPU) > > vanilla map: > > 2 WALL ( 2.45 usr + 0.02 sys = 2.47 CPU) > > OK, so why is List.map in the OCaml standard library implemented the > vanilla way rather than the rev rev_map way? If it's such a big win, > it seems foolish to have a broken implementation for such a crucial > function. > > (BTW, if you want efficient (and pure) mapping and filtering over long > streams, you should consider using lazy lists. A good compiler (like > GHC) will do the deforestation optimisation, so the list is never even > allocated[1].) > > [1] unless you make use of persistence, of course. > > --KW 8-) I think in this thread both "problems" are resolved by ExtLib : - a fully tail-recursive List module - Enums for lazy lists http://ocaml-lib.sf.net So of course you can still get the pro and cons of having ExtLib implementation being the standard one, but anyone actually have the choice, that's important. The choice is up to OCaml INRIA team, and I don't think any thread - as long and flammy as it could be - would change their opinions. Regards, Nicolas Cannasse ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 9:25 ` Keith Wansbrough 2004-07-29 9:41 ` Nicolas Cannasse @ 2004-07-29 9:57 ` Xavier Leroy 2004-07-29 10:44 ` Daniel Andor 1 sibling, 1 reply; 44+ messages in thread From: Xavier Leroy @ 2004-07-29 9:57 UTC (permalink / raw) To: Keith Wansbrough; +Cc: Daniel Andor, Ocaml > > Lemme try it out (10^6 elements): > > > > ocamlc: > > rev rev_map version: > > 2 WALL ( 1.19 usr + 0.02 sys = 1.21 CPU) > > vanilla map: > > 7 WALL ( 6.50 usr + 0.09 sys = 6.59 CPU) > > > > ocamlopt: > > rev rev_map version: > > 1 WALL ( 0.81 usr + 0.03 sys = 0.84 CPU) > > vanilla map: > > 2 WALL ( 2.45 usr + 0.02 sys = 2.47 CPU) > > OK, so why is List.map in the OCaml standard library implemented the > vanilla way rather than the rev rev_map way? If it's such a big win, > it seems foolish to have a broken implementation for such a crucial > function. Because for smaller list the "vanilla way" is more efficient. In the test above, the vanilla way spends significant time resizing the stack. I suspect that when running it a second time on a already resized stack, the timings would improve quite a lot. - Xavier Leroy ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 9:57 ` Xavier Leroy @ 2004-07-29 10:44 ` Daniel Andor 2004-07-29 12:56 ` brogoff 0 siblings, 1 reply; 44+ messages in thread From: Daniel Andor @ 2004-07-29 10:44 UTC (permalink / raw) To: Ocaml On Thursday 29 July 2004 10:57 am, Xavier Leroy wrote: > Because for smaller list the "vanilla way" is more efficient. A little benchmarking with short lists shows that for lists that are near or smaller than my cache size (skaller's point), the stack map performs better; especially in the byte-code case. However, the thread was originally about long lists, and for that it is clear that algorithms other than the vanilla map are better suited. To me, this just proves that there's no such thing as universal optimisation (yet!). One's got to actually think about the problem at hand. Damn. ;) Daniel. ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 10:44 ` Daniel Andor @ 2004-07-29 12:56 ` brogoff 0 siblings, 0 replies; 44+ messages in thread From: brogoff @ 2004-07-29 12:56 UTC (permalink / raw) To: Daniel Andor; +Cc: Ocaml On Thu, 29 Jul 2004, Daniel Andor wrote: > On Thursday 29 July 2004 10:57 am, Xavier Leroy wrote: > > Because for smaller list the "vanilla way" is more efficient. > > A little benchmarking with short lists shows that for lists that are near or > smaller than my cache size (skaller's point), the stack map performs better; > especially in the byte-code case. > > However, the thread was originally about long lists, and for that it is clear > that algorithms other than the vanilla map are better suited. It's not at all clear to me. Better how? Faster? Less coding effort? Clearer to others maintaining the code? > To me, this just proves that there's no such thing as universal optimisation (yet!). I agree with that. And I agree with Xavier's prioritization of a clean fix to this as a "nice to have but not critical". For now, use ExtLib or roll your own. > One's got to actually think about the problem at hand. Damn. ;) Yes. I'm going back to C and assembly code now, thanks to these arguments :-) -- Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 9:13 ` Daniel Andor 2004-07-29 9:25 ` Keith Wansbrough @ 2004-07-29 10:11 ` skaller 2004-07-29 12:41 ` brogoff 2 siblings, 0 replies; 44+ messages in thread From: skaller @ 2004-07-29 10:11 UTC (permalink / raw) To: Daniel Andor; +Cc: Ocaml On Thu, 2004-07-29 at 19:13, Daniel Andor wrote: > Lemme try it out (10^6 elements): > > ocamlc: > rev rev_map version: > 2 WALL ( 1.19 usr + 0.02 sys = 1.21 CPU) > vanilla map: > 7 WALL ( 6.50 usr + 0.09 sys = 6.59 CPU) > > ocamlopt: > rev rev_map version: > 1 WALL ( 0.81 usr + 0.03 sys = 0.84 CPU) > vanilla map: > 2 WALL ( 2.45 usr + 0.02 sys = 2.47 CPU) > > Wow, that was unexpected! The tail-rec version doesn't clobber the cache with growing and shrinking stack -- you have two areas of memory being accessed sequentially instead of three .. (Ocaml allocator is sequential) -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 9:13 ` Daniel Andor 2004-07-29 9:25 ` Keith Wansbrough 2004-07-29 10:11 ` skaller @ 2004-07-29 12:41 ` brogoff 2 siblings, 0 replies; 44+ messages in thread From: brogoff @ 2004-07-29 12:41 UTC (permalink / raw) To: Daniel Andor; +Cc: Ocaml On Thu, 29 Jul 2004, Daniel Andor wrote: > On Wednesday 28 July 2004 10:22 pm, brogoff wrote: > > On Wed, 28 Jul 2004, Jon Harrop wrote: > > > On Wednesday 28 July 2004 17:38, brogoff wrote: > > > > > brogoff wrote: > > > > > > Sometimes lists are best. If lists are OK for 100, 1_000, or 10_000 > > > > > > items, why not 100_000 or 1_000_000? Map and friends shouldn't blow > > > > > > stack. > [...] > > > What's wrong with List.rev_map, List.rev (List.rev_map ...), increasing > > > the size of the VM's stack, using native code or even writing your own, > > > tail-recursive map? > > > > I'm pretty damned well aware that I can reverse a rev mapped list. > > If you've got the mutable list version of map to hand, and you are happy with > that, then that's clearly the way to go. And I said as much. I also said it offends my sense of aesthetics ;-). > Lemme try it out (10^6 elements): > > ocamlc: > rev rev_map version: > 2 WALL ( 1.19 usr + 0.02 sys = 1.21 CPU) > vanilla map: > 7 WALL ( 6.50 usr + 0.09 sys = 6.59 CPU) > > ocamlopt: > rev rev_map version: > 1 WALL ( 0.81 usr + 0.03 sys = 0.84 CPU) > vanilla map: > 2 WALL ( 2.45 usr + 0.02 sys = 2.47 CPU) > > Wow, that was unexpected! > > > Does it occur to you that that is not efficient? > > Not any more! You neglected the "mutable" list version in your quickie benchmark. My own quickie benchmark showed that version had better performance than the doubly reversing implementation on large lists and very slightly better performance than the default on small ones. I would expect that if something like holes or promises (a la Alice SML) or whatever were in OCaml, that under the hood it would be the same as the mutable list version and so would have equivalent performance. -- Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 16:38 ` brogoff 2004-07-28 19:40 ` Jon Harrop @ 2004-07-29 6:28 ` Alex Baretta 2004-07-29 14:58 ` brogoff 1 sibling, 1 reply; 44+ messages in thread From: Alex Baretta @ 2004-07-29 6:28 UTC (permalink / raw) To: brogoff; +Cc: Ocaml brogoff wrote: > On Wed, 28 Jul 2004, Alex Baretta wrote: > >>brogoff wrote: >> >>>Sometimes lists are best. If lists are OK for 100, 1_000, or 10_000 items, >>>why not 100_000 or 1_000_000? Map and friends shouldn't blow stack. >>> >>>-- Brian >> >>Actually, it's not that bad. > > > Well, I'm still on the caml-list, so of course it isn't *that* bad. Also, as > I said, if you need a tail recursive map over built in lists, you have at > least two options. Unfortunately, my preference is to use Obj, which IMO > points to a deficiency in the core language. Most, maybe all, of my issues > with OCaml, amount to petty complaints, no showstoppers or dealbreakers. > Hey, at least I'm not whining about the license! Yes, I guess we simply can live with it. I can't wait to have STL iterators in Ocaml, but meanwhile I can live with this functional nonsense: lists, maps, sets... ;) > There are quite a few cases where singly linked lists are the most efficient > data structure, and they're just right for the problem. Streams and laziness > are not at all efficient in comparison. Stack overflow commonly happens whenm > one of my users (yup, I have users of my code who aren't me ;-) decides to > run on data much larger than I'd anticipated. Huge lists are usually not an efficient data structure. Lists are local structures: at any point you can only observe a fixed number of head elements (usually one at a time) and an unspecified tail. Such locality generally makes caching the entire data structure useless. Long lists (lists of a priori unbounded length) arise from user input: typically the are obtained by reading in a file of a priori unbounded length. In such cases lists are very much inefficient from the point of view of memory complexity: lists cost O(n) where n is the size of the input; streams cost O(1). Both cost O(n) in terms of time complexity. Usually the potential speed benefit of lists is amply counterbalanced by reduction in memory footprint, which usually means no memory swapping in the kernel. Here's an obvious example (not too obvious: I fell for it at first). I have a XML syntax specifying relational databases. Essentially an entire relational database can be represented with it. This XML lexicon is meant for now only for the database initial schema definition and for human-readable backups. I initially wrote the backup algorithm in terms of transformations on a list of PXP trees which were finally serialized. Algebraically, this is perfect. The only problem is that this algorithm stores the entire contents of a database in memory before serializing everything. Ooops, here's a SIGKILL landing in! Now I only use PXP trees to represent the database schema, which I immediately begin to serialize. All the while I pass around continuations which are invoked iteratively on subnodes. If a subnode is an table-data node I declare an SQL cursor in the database and begin walking through the table. Result, where my initial memory footprint bought me a SIGKILL on a 2GB server now I have bounded memory usage (a few megabytes). I don't swap, so the the backup process actually runs an order of magnitude faster. And I actually get my output at the end ;) > Also, lists are builtin, and have syntactic support in OCaml, by which I mean > nice, easy to read syntax. So the language encourages you to use them. They are sooo cool! I actually ask all trainees to reimplement the List module. But then the industrial practice is to use lists only where there is no input or as the result of some kind of slicing or research applied to bigger and more efficient data structures. I actually use a list iteration to schedule tasks in my real-time control kernel, but of course, I don't expect my collegues to write more than a few tasks at a time for my kernel to schedule: safety, liveness and UI. ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 6:28 ` Alex Baretta @ 2004-07-29 14:58 ` brogoff 2004-07-29 16:12 ` Brian Hurt ` (2 more replies) 0 siblings, 3 replies; 44+ messages in thread From: brogoff @ 2004-07-29 14:58 UTC (permalink / raw) To: Alex Baretta; +Cc: Ocaml On Thu, 29 Jul 2004, Alex Baretta wrote: > brogoff wrote: > > Well, I'm still on the caml-list, so of course it isn't *that* bad. Also, as > > I said, if you need a tail recursive map over built in lists, you have at > > least two options. Unfortunately, my preference is to use Obj, which IMO > > points to a deficiency in the core language. Most, maybe all, of my issues > > with OCaml, amount to petty complaints, no showstoppers or dealbreakers. > > Hey, at least I'm not whining about the license! > > Yes, I guess we simply can live with it. I can't wait to have STL > iterators in Ocaml, but meanwhile I can live with this functional > nonsense: lists, maps, sets... ;) That's funny! I used to get chided by the guy who hired me for being a bit of a functional snob, always trying to find a side effect free solution (BTW, the "mutable" lists using Obj are side effect free) instead of solving every problem with a hash table :-) Some problems are just way easier to solve with imperative programming though. Have you ever taken a look at purely functional catenable deques? If you don't need persistence (if your deques are used in a single threaded manner that is) would you use those instead of the obvious doubly linked list implementation? That's not an abstract question either, catenable deques come up quite naturally when implementing some 2D computational geometry algorithms.I took a look at the latest algorithm by Kaplan and Tarjan and quickly realized that it was hugely complicated if persistence is not needed. > > > There are quite a few cases where singly linked lists are the most efficient > > data structure, and they're just right for the problem. Streams and laziness > > are not at all efficient in comparison. Stack overflow commonly happens whenm > > one of my users (yup, I have users of my code who aren't me ;-) decides to > > run on data much larger than I'd anticipated. > > Huge lists are usually not an efficient data structure. Sure, but sometimes you have programs which aren't intended to be used on huge data sets, but pesky users aren't deterred by your warnings. So, even if the extremely common case is not huge, it shouldn't fail in the huge case, unless of course the failure is Out_of_memory. -- Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 14:58 ` brogoff @ 2004-07-29 16:12 ` Brian Hurt 2004-07-29 17:49 ` james woodyatt 2004-07-29 17:44 ` james woodyatt 2004-07-29 22:42 ` Alex Baretta 2 siblings, 1 reply; 44+ messages in thread From: Brian Hurt @ 2004-07-29 16:12 UTC (permalink / raw) To: brogoff; +Cc: Alex Baretta, Ocaml On Thu, 29 Jul 2004, brogoff wrote: > Some problems are just way easier to solve with imperative programming > though. Have you ever taken a look at purely functional catenable deques? Just got added to extlib, for the record. A simplistic version that actually isn't that much more complicated than the imperitive implementation. -- "Usenet is like a herd of performing elephants with diarrhea -- massive, difficult to redirect, awe-inspiring, entertaining, and a source of mind-boggling amounts of excrement when you least expect it." - Gene Spafford Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 16:12 ` Brian Hurt @ 2004-07-29 17:49 ` james woodyatt 2004-07-29 19:25 ` Brian Hurt 0 siblings, 1 reply; 44+ messages in thread From: james woodyatt @ 2004-07-29 17:49 UTC (permalink / raw) To: Brian Hurt; +Cc: Ocaml Trade On 29 Jul 2004, at 09:12, Brian Hurt wrote: > On Thu, 29 Jul 2004, brogoff wrote: >> >> Some problems are just way easier to solve with imperative programming >> though. Have you ever taken a look at purely functional catenable >> deques? > > Just got added to extlib, for the record. A simplistic version that > actually isn't that much more complicated than the imperitive > implementation. This is extraordinary! Do you really mean to say the deques are purely functional, catenable *and* offering the same complexity as the non-persistent implementation? Which algorithm was added to extlib? ― ∴ ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 17:49 ` james woodyatt @ 2004-07-29 19:25 ` Brian Hurt 2004-07-29 20:01 ` brogoff 0 siblings, 1 reply; 44+ messages in thread From: Brian Hurt @ 2004-07-29 19:25 UTC (permalink / raw) To: james woodyatt; +Cc: Ocaml Trade On Thu, 29 Jul 2004, james woodyatt wrote: > On 29 Jul 2004, at 09:12, Brian Hurt wrote: > > On Thu, 29 Jul 2004, brogoff wrote: > >> > >> Some problems are just way easier to solve with imperative programming > >> though. Have you ever taken a look at purely functional catenable > >> deques? > > > > Just got added to extlib, for the record. A simplistic version that > > actually isn't that much more complicated than the imperitive > > implementation. > > This is extraordinary! Do you really mean to say the deques are purely > functional, catenable *and* offering the same complexity as the > non-persistent implementation? Which algorithm was added to extlib? > The version added is O(1) ammoritized. It has the same problem as quicksort and hashtables, in that on average about 1 operation in N has cost O(N), instead of strict O(1). The library added was the simplest double list implementation. Basically, the queue is broken into two parts- a front part and a back part, both kept in lists. The back part list is kept in reverse order- so to add an element to the end of the queue, you prepend it to the back part list. You remove elements from head of the front part queue, and when it's empty, you replace it with the reverse of the back part list. Every element that goes through the queue is touched precisely three times- once when it's added to the end of the queue, once when it's removed from the head of the queue, and once when we reverse the back half to become the new front half. So adding and then removing N elements from the list costs O(N). The core code looks like this: type 'a deque = 'a list * 'a list let empty = ([], []) (* the empty queue *) let add q x = match q with | ([], []) -> (* We add the first element to the front *) ([x], []) | ([], _) -> assert false | (front, back) -> (front, x :: back) let remove q = match q with | ([], []) -> invalid_arg "remove" | ([], _) -> assert false | (h :: [], back) -> h, ((List.rev back), []) | (h :: front, back) -> h, (front, back) I will note that if you use a capability that applicative semantics give us, you can get into trouble. Imagine the following scenario: you create an empty queue and add 1000 new elements. You then remove one element. This does a List.rev on a 999 element list. You then throw the modified queue away, and remove the first element from the original queue again, reversing the same 999 element list. Repeat- every removing reverses the same list. The solution to this is "don't do that!" Note that pushing an element onto the head of the queue is strict O(1): let push x (front, back) = (x :: front, back) Rather than using the applicative semantics to undo the removal, use push to push back the removed elements. This way, you only reverse the back half of the list once. -- "Usenet is like a herd of performing elephants with diarrhea -- massive, difficult to redirect, awe-inspiring, entertaining, and a source of mind-boggling amounts of excrement when you least expect it." - Gene Spafford Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 19:25 ` Brian Hurt @ 2004-07-29 20:01 ` brogoff 2004-07-30 4:42 ` james woodyatt 0 siblings, 1 reply; 44+ messages in thread From: brogoff @ 2004-07-29 20:01 UTC (permalink / raw) To: Brian Hurt; +Cc: james woodyatt, Ocaml Trade On Thu, 29 Jul 2004, Brian Hurt wrote: > On Thu, 29 Jul 2004, james woodyatt wrote: > > On 29 Jul 2004, at 09:12, Brian Hurt wrote: > > > On Thu, 29 Jul 2004, brogoff wrote: > > >> > > >> Some problems are just way easier to solve with imperative programming > > >> though. Have you ever taken a look at purely functional catenable > > >> deques? > > > > > > Just got added to extlib, for the record. A simplistic version that > > > actually isn't that much more complicated than the imperitive > > > implementation. > > > > This is extraordinary! Do you really mean to say the deques are purely > > functional, catenable *and* offering the same complexity as the > > non-persistent implementation? Which algorithm was added to extlib? > > > > The version added is O(1) ammoritized. It has the same problem as > quicksort and hashtables, in that on average about 1 operation in N has > cost O(N), instead of strict O(1). > > The library added was the simplest double list implementation. Basically, > the queue is broken into two parts- a front part and a back part, both > kept in lists. The back part list is kept in reverse order- so to add an > element to the end of the queue, you prepend it to the back part list. > You remove elements from head of the front part queue, and when it's > empty, you replace it with the reverse of the back part list. What you've described so far is a queue. I guess I was really oblique in my message, or I'm just being really obtuse, but what I am basically asking for something that implements this signature (people who don't likw modules, avert your gaze!) module type CatenableDeque = sig type 'a t val empty : 'a t val is_empty : 'a t -> bool val push_first : 'a -> 'a t -> 'a t val first : 'a t -> 'a val rest : 'a t -> 'a t val push_last : 'a -> 'a t -> 'a t val last : 'a t -> 'a val front : 'a t -> 'a t val append : 'a t -> 'a t -> 'a t end and I want to be able to efficiently add at both ends and catenate. I ended up doing this instead module type ImpCatenableDeque = sig type 'a t val make : 'a -> 'a t val of_list : 'a list -> 'a t val to_list : 'a t -> 'a list val first : 'a t -> 'a val last : 'a t -> 'a val prev : 'a t -> 'a t val next : 'a t -> 'a t val insert_first : 'a -> 'a t -> unit val insert_last : 'a -> 'a t -> unit val remove_first : 'a t -> unit val remove_last : 'a t -> unit (** conc_front x y : x is modified so that y is in front, y destroyed *) val conc_front : 'a t -> 'a t -> unit (** conc_back x y : x is modified so that y is in back, y destroyed *) val conc_back : 'a t-> 'a t -> unit val iter : ('a -> unit) -> 'a t -> unit val iteri : (int -> 'a -> unit) -> 'a t -> unit end and yes the implementation is a CS101 style doubly linked list, fraught with peril. It may be mildly interesting to compare performance versus James Woodyatt's version using lazy. If I remember correctly, Kaplan and Tarjan use the term purely functional to exclude lazy in one of their papers, and just allow primitive list ops like car/cdr/cons etc. -- Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 20:01 ` brogoff @ 2004-07-30 4:42 ` james woodyatt 0 siblings, 0 replies; 44+ messages in thread From: james woodyatt @ 2004-07-30 4:42 UTC (permalink / raw) To: brogoff, Ocaml Trade On 29 Jul 2004, at 13:01, brogoff wrote: > > What you've described so far is a queue. I guess I was really oblique > in my > message, or I'm just being really obtuse, but what I am basically > asking for > something that implements this signature (people who don't likw > modules, avert > your gaze!) > > module type CatenableDeque = > sig > type 'a t > val empty : 'a t > val is_empty : 'a t -> bool > > val push_first : 'a -> 'a t -> 'a t > val first : 'a t -> 'a > val rest : 'a t -> 'a t > > val push_last : 'a -> 'a t -> 'a t > val last : 'a t -> 'a > val front : 'a t -> 'a t > > val append : 'a t -> 'a t -> 'a t > end > > and I want to be able to efficiently add at both ends and catenate. > [...] > and yes the implementation is a CS101 style doubly linked list, > fraught with > peril. It may be mildly interesting to compare performance versus James > Woodyatt's version using lazy. If I remember correctly, Kaplan and > Tarjan use > the term purely functional to exclude lazy in one of their papers, and > just > allow primitive list ops like car/cdr/cons etc. They do. Mine would be purely functional if it didn't have to support concatenation. And it does not suffer from the limitations of the implementation Mr. Hurt is talking about in the recent addition to Extlib. I would expect to pay a small price for the persistence. With the imperative double-link list, the insert operation costs one allocation, a reference copy and the update to the links. With my functional deque, the insert operation costs at least one allocation, possibly as many, in the worst-case, as 2/3 log[2] N allocations (where N is the number of elements in the deque), and the link update overhead. The cost of all the other operations is similar in complexity. Here is the module interface file... (*---------------------------------------------------------------------- -----* INTERFACE cf_deque.mli Copyright (c) 2003-2004, James H. Woodyatt All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *----------------------------------------------------------------------- ----*) (** A functional persistent double-ended catenable deque, with O{_ avg}(1) cost for every operation. Internally, this is a recursive data structure with height O(log N). *) (** The abstract type of a deque. *) type +'a t (** The empty deque. *) val nil: 'a t (** Returns [true] if the deque is the empty deque. *) val empty: 'a t -> bool (** Functions for operations on one of the two ends of a deque. *) module type Direction_T = sig (** [push x d] adds the element [x] to the end of the deque [d]. The average cost is constant. Worst-case running time is O(log N), which happens once in every N operations. Not tail-recursive. *) val push: 'a -> 'a t -> 'a t (** [pop d] returns [None] if [d] is the empty deque, otherwise it returns [Some (x, d')] where [x] is the element on the end of the deque, and [d'] is the remainder of [d] with the element [x] removed. The average cost is constant. Worst-case running time is O(log N), which happens once in every N operations. Not tail-recursive. *) val pop: 'a t -> ('a * 'a t) option (** [head d] returns the element at the end of the deque [d]. Raises [Not_found] if the deque is empty. Not tail-recursive. *) val head: 'a t -> 'a (** [tail d] is discards the element at the end of the deque [d]. Raises [Not_found] if the deque is empty. Not tail-recursive. *) val tail: 'a t -> 'a t (** [to_seq d] returns a lazily evaluated sequence of the elements in the deque in the order they would appear by successive calls of [pop d]. *) val to_seq: 'a t -> 'a Cf_seq.t (** [to_seq2 d] returns a lazily evaluated sequence of the pairs [(hd, tl)] obtained by successively calling of [pop d]. *) val to_seq2: 'a t -> ('a * 'a t) Cf_seq.t end module A: Direction_T (** Operations on the left end of a deque. *) module B: Direction_T (** Operations on the right end of a deque. *) (** [iterate f d] applies [f] to every element in [d] in left-to-right order. Not tail recursive. *) val iterate: ('a -> unit) -> 'a t -> unit (** [predicate f d] returns [true] if the result of applying [f] to every element in the deque [d] is [true], or if [d] is the empty deque. The order in which elements are applied is left to right. If [f] returns [false], then no more elements from [d] will be applied and the result will be returned immediately. Not tail recursive. *) val predicate: ('a -> bool) -> 'a t -> bool (** [fold f a0 d] is [f (... (f (f a0 e0) e1) ...) en] when [e0..en] are the elements of the deque [d] in left-to-right order. Not tail recursive. *) val fold: ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b (** [filter f d] returns a new deque composed by applying [f] to every element in [d], including only those elements for which the result is [true]. The function is applied to the elements in the deque in left-to-right order. Not tail recursive. *) val filter: ('a -> bool) -> 'a t -> 'a t (** [map f d] returns a new deque composed by applying [f] to every element in [d] in left-to-right order. Not tail recursive. *) val map: ('a -> 'b) -> 'a t -> 'b t (** [optmap f d] returns a new deque composed by applying [f] to every element in [d] in left-to-right order, including only those elements of [d] for which [f] returns [Some] value. Not tail recursive. *) val optmap: ('a -> 'b option) -> 'a t -> 'b t (** [listmap f d] returns a new deque composed by applying [f] to every element in [d] in left-to-right order, taking all the resulting lists of elements in order. Not tail recursive. *) val listmap: ('a -> 'b list) -> 'a t -> 'b t (** [seqmap f d] returns a new deque composed by applying [f] to every element in [d] in left-to-right order, taking all the resulting sequences of elements in order. Not tail recursive. *) val seqmap: ('a -> 'b Cf_seq.t) -> 'a t -> 'b t (** [partition f s] returns two deques. The first is the deque of elements in [d] for which applying [f] results in [true], and the second is the deque of elements for which applying [f] results in [false]. The elements are applied in left-to-right order. *) val partition: ('a -> bool) -> 'a t -> 'a t * 'a t (** [length d] computes the number elements contained in the deque [d]. Not tail recursive. *) val length: 'a t -> int (** [catenate d1 d2] returns a new deque composed by joining the right end of [d1] to the left end of [d2]. The average cost is constant. Not tail-recursive. *) val catenate: 'a t -> 'a t -> 'a t (*--- End of File [ cf_deque.mli ] ---*) NOTE: it turns out that I will be changing the module in the next release, specifically to make the utility functions [iterate, predicate, filter, map, fold, etc.] apply their iterative function in left-to-right order, rather than an indeterminate order. The right-to-left order will require converting the deque to a sequence and using the [Cf_seq] function of the same name. -- j h woodyatt <jhw@wetware.com> markets are only free to the people who own them. ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 14:58 ` brogoff 2004-07-29 16:12 ` Brian Hurt @ 2004-07-29 17:44 ` james woodyatt 2004-07-29 23:12 ` skaller 2004-07-29 22:42 ` Alex Baretta 2 siblings, 1 reply; 44+ messages in thread From: james woodyatt @ 2004-07-29 17:44 UTC (permalink / raw) To: brogoff; +Cc: Ocaml On 29 Jul 2004, at 07:58, brogoff wrote: > > Some problems are just way easier to solve with imperative programming > though. Have you ever taken a look at purely functional catenable > deques? > If you don't need persistence (if your deques are used in a single > threaded > manner that is) would you use those instead of the obvious doubly > linked > list implementation? That's not an abstract question either, catenable > deques > come up quite naturally when implementing some 2D computational > geometry > algorithms.I took a look at the latest algorithm by Kaplan and Tarjan > and > quickly realized that it was hugely complicated if persistence is not > needed. As it happens, I have some experience with this problem. I have implemented purely functional, catenable deques in Ocaml. (I need the persistence.) Yes, the Kaplan-Okasaki-Tarjan deques are hideously complicated. However. I have found a significantly simpler algorithm that uses lazy evaluation, still offers near real-time performance, and only adds the limitation that persistence is only available in program memory (lazy cells can't be marshalled, you know). I can live with that limitation quite happily, and my deques are about three times as fast as the KOT deques. My algorithm can be reviewed in the [Cf_deque] module of my Cf library. The Ocaml HUMP has a link to it. And the good news is that the algorithm is *not* patented as far as I know (the deadline for me to file an application has lapsed too), and the library is released under a BSD two-clause style license. (I should be releasing an update to the library this week, but the [Cf_deque] module will not be changing.) All that said, I can say that I have found I need persistent data structures to make other functional algorithms work well. In those cases where I feel safe and comfortable mixing imperative and functional styles (fraught with peril!), then I certainly use the standard [Queue] module. I'll be using that tactic in the [Cf_gadget] module when I make the next release (the current version uses [Cf_deque] unnecessarily). [Off topic: there are also cases where I find the standard [Queue] module to be insufficiently well-endowed with utility functions, so I use my [Cf_deque] module instead. It performs almost as well as [Queue], and it allows for more convenient transformation and manipulations of the represented sequence (see the [Cf_poll] module for an example of my thinking there).] ― ∴ ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 17:44 ` james woodyatt @ 2004-07-29 23:12 ` skaller 0 siblings, 0 replies; 44+ messages in thread From: skaller @ 2004-07-29 23:12 UTC (permalink / raw) To: james woodyatt; +Cc: brogoff, Ocaml On Fri, 2004-07-30 at 03:44, james woodyatt wrote: > On 29 Jul 2004, at 07:58, brogoff wrote: > And the good news is that the > algorithm is *not* patented as far as I know Australia does not allow algorithms to be patented anyhow .. :) -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 14:58 ` brogoff 2004-07-29 16:12 ` Brian Hurt 2004-07-29 17:44 ` james woodyatt @ 2004-07-29 22:42 ` Alex Baretta 2004-07-30 2:38 ` Corey O'Connor ` (2 more replies) 2 siblings, 3 replies; 44+ messages in thread From: Alex Baretta @ 2004-07-29 22:42 UTC (permalink / raw) To: brogoff; +Cc: Ocaml brogoff wrote: > On Thu, 29 Jul 2004, Alex Baretta wrote: > >>brogoff wrote: > > Some problems are just way easier to solve with imperative programming > though. Have you ever taken a look at purely functional catenable deques? > If you don't need persistence (if your deques are used in a single threaded > manner that is) would you use those instead of the obvious doubly linked > list implementation? That's not an abstract question either, catenable deques > come up quite naturally when implementing some 2D computational geometry > algorithms.I took a look at the latest algorithm by Kaplan and Tarjan and > quickly realized that it was hugely complicated if persistence is not needed. s list: http://groups.yahoo.com/group/ocaml_beginners > I didn't understand the connection between multithreading and persistence, but it's probably too late and I've been working far too much to follow you entirely. Let me just give you a couple eurocents of industrial experience: side-effects are utterly bad. My Xcaml application server was designed two years ago with one major flaw: one global state variable. I'm still fighting with the execution engine to take it away, but that won't happen before a major rewrite. I won't by imperative programming for anything at all. And, yes, in my company the mandated standard for deques is batched queues à la Okasaki. Alex ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 22:42 ` Alex Baretta @ 2004-07-30 2:38 ` Corey O'Connor [not found] ` <200407300136.14042.jon@jdh30.plus.com> 2004-07-30 17:07 ` brogoff 2 siblings, 0 replies; 44+ messages in thread From: Corey O'Connor @ 2004-07-30 2:38 UTC (permalink / raw) To: Ocaml Is the connection due to persistance? So given a data structure is persistant across operations, let's say a stack. Suppose if two threads are working with the same instance of a data structure. One thread pushes a value onto the stack, the other does not. Due to persistance the other thread's instance of the stack is unaffected? -Corey On Fri, 30 Jul 2004 00:42:27 +0200, Alex Baretta <alex@baretta.com> wrote: > > I didn't understand the connection between multithreading and > persistence, but it's probably too late and I've been working far too > much to follow you entirely. Let me just give you a couple eurocents of > industrial experience: side-effects are utterly bad. My Xcaml > application server was designed two years ago with one major flaw: one > global state variable. I'm still fighting with the execution engine to > take it away, but that won't happen before a major rewrite. I won't by > imperative programming for anything at all. And, yes, in my company the > mandated standard for deques is batched queues à la Okasaki. > > Alex > > > > ------------------- > To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr > Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > -- -Corey O'Connor ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
[parent not found: <200407300136.14042.jon@jdh30.plus.com>]
* Re: [Caml-list] looping recursion [not found] ` <200407300136.14042.jon@jdh30.plus.com> @ 2004-07-30 12:45 ` Alex Baretta 0 siblings, 0 replies; 44+ messages in thread From: Alex Baretta @ 2004-07-30 12:45 UTC (permalink / raw) To: Jon Harrop, Ocaml Jon Harrop wrote: > On Thursday 29 July 2004 23:42, you wrote: > >>...I won't by >>imperative programming for anything at all... > > > What would you do if you wanted a data structure with O(1) lookup? Such structures do not exists. Remember that complexity theory deals with *unbounded* input sizes. As you can well imagine, there exists no such thing as an indefinitely large datastructure with O(1) time complexity. Arrays are O(1) only if you preallocate them to the maximum size allowed by the application. But, in this case, even a binary search in Map.t is O(1): that is, bounded by a constant. Alex ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-29 22:42 ` Alex Baretta 2004-07-30 2:38 ` Corey O'Connor [not found] ` <200407300136.14042.jon@jdh30.plus.com> @ 2004-07-30 17:07 ` brogoff 2004-07-30 18:25 ` [Caml-list] kaplan-okasaki-tarjan deque (was "looping recursion") james woodyatt 2 siblings, 1 reply; 44+ messages in thread From: brogoff @ 2004-07-30 17:07 UTC (permalink / raw) To: Alex Baretta; +Cc: Ocaml On Fri, 30 Jul 2004, Alex Baretta wrote: > brogoff wrote: > > On Thu, 29 Jul 2004, Alex Baretta wrote: > > > >>brogoff wrote: > > > > Some problems are just way easier to solve with imperative programming > > though. Have you ever taken a look at purely functional catenable deques? > > If you don't need persistence (if your deques are used in a single threaded > > manner that is) would you use those instead of the obvious doubly linked > > list implementation? That's not an abstract question either, catenable deques > > come up quite naturally when implementing some 2D computational geometry > > algorithms.I took a look at the latest algorithm by Kaplan and Tarjan and > > quickly realized that it was hugely complicated if persistence is not needed. > s list: http://groups.yahoo.com/group/ocaml_beginners > > > > I didn't understand the connection between multithreading and > persistence, but it's probably too late and I've been working far too > much to follow you entirely. I'm not using the terminology with regard to concurrent programming, but simply saying only I don't need persistence. Maybe I picked up the terminology from some discussion about versioned arrays (implementing functional arrays on top of imperative ones) or from Clean. > Let me just give you a couple eurocents of industrial experience: side-effects > are utterly bad. My two U.S. cents of industrial experience is that statement is utterly false. Side effects are to be controlled and dealt with, but they are unavoidable. But I don't feel like having a flamewar about it, no doubt we've all seen the arguments and no one will offer anything new... > My Xcaml > application server was designed two years ago with one major flaw: one > global state variable. Sure, Dante's lowest level of Hell is reserved for those who abuse global state. Ever look at the purely functional red black tree implementation in the SML/NJ utils library? Last time I looked, the author used a local reference to store some state in one of the calculation, rather than adding an argument to a bunch of nested functions. Still purely functional to the observer though. What level of Hell should he go to? > I'm still fighting with the execution engine to > take it away, but that won't happen before a major rewrite. I won't by > imperative programming for anything at all. Even monadic IO strikes me as imperative, so if you have IO, there's really no way to avoid it. > And, yes, in my company the > mandated standard for deques is batched queues à la Okasaki. I think of the respondents to my point about catenable deques, only James Woodyatt seems to get what I mean. And given the fact that persistence is not necessary in my application, I am willing to exercise extra care to get that annoying imperative code right. I would like to see an implementation of the catenable deques only using simple list ops (not laziness) described by Kaplan and Tarjan, in OCaml. If I remember, the algorithm was described using nonuniform data types, so that's yet another plug for supporting this more directly in OCaml, meaning, without having to use recursive modules, record field polymorphism, or polymorphic records to work around the ability to directly write such functions. -- Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* [Caml-list] kaplan-okasaki-tarjan deque (was "looping recursion") 2004-07-30 17:07 ` brogoff @ 2004-07-30 18:25 ` james woodyatt 2004-07-30 21:20 ` brogoff 0 siblings, 1 reply; 44+ messages in thread From: james woodyatt @ 2004-07-30 18:25 UTC (permalink / raw) To: brogoff, Ocaml Trade [-- Attachment #1: Type: text/plain, Size: 274 bytes --] On 30 Jul 2004, at 10:07, brogoff wrote: > > I would like to see an implementation of the catenable deques only > using simple list ops (not laziness) described by Kaplan and Tarjan, > in OCaml. Sure. Here is the basic implementation I did for performance comparisons. [-- Attachment #2: cf_kotdeque.ml --] [-- Type: application/octet-stream, Size: 18773 bytes --] (*---------------------------------------------------------------------------* IMPLEMENTATION cf_kotdeque.ml Copyright (c) 2003, James H. Woodyatt All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *---------------------------------------------------------------------------*) type 'a buffer = | B1 of 'a | B2 of 'a * 'a | B3 of 'a * 'a * 'a | B4 of 'a * 'a * 'a * 'a | B5 of 'a * 'a * 'a * 'a * 'a | B6 of 'a * 'a * 'a * 'a * 'a * 'a | B7 of 'a * 'a * 'a * 'a * 'a * 'a * 'a | B8 of 'a * 'a * 'a * 'a * 'a * 'a * 'a * 'a let pushBufferL x = function | B1 a -> B2 (x,a) | B2 (a,b) -> B3 (x,a,b) | B3 (a,b,c) -> B4 (x,a,b,c) | B4 (a,b,c,d) -> B5 (x,a,b,c,d) | B5 (a,b,c,d,e) -> B6 (x,a,b,c,d,e) | B6 (a,b,c,d,e,f) -> B7 (x,a,b,c,d,e,f) | B7 (a,b,c,d,e,f,g) -> B8 (x,a,b,c,d,e,f,g) | B8 _ as y -> assert (not true); y let pushBufferR x = function | B1 a -> B2 (a,x) | B2 (a,b) -> B3 (a,b,x) | B3 (a,b,c) -> B4 (a,b,c,x) | B4 (a,b,c,d) -> B5 (a,b,c,d,x) | B5 (a,b,c,d,e) -> B6 (a,b,c,d,e,x) | B6 (a,b,c,d,e,f) -> B7 (a,b,c,d,e,f,x) | B7 (a,b,c,d,e,f,g) -> B8 (a,b,c,d,e,f,g,x) | B8 _ as y -> assert (not true); y let popBufferL = function | B1 _ -> assert false | B2 (x,a) -> x, B1 a | B3 (x,a,b) -> x, B2 (a,b) | B4 (x,a,b,c) -> x, B3 (a,b,c) | B5 (x,a,b,c,d) -> x, B4 (a,b,c,d) | B6 (x,a,b,c,d,e) -> x, B5 (a,b,c,d,e) | B7 (x,a,b,c,d,e,f) -> x, B6 (a,b,c,d,e,f) | B8 (x,a,b,c,d,e,f,g) -> x, B7 (a,b,c,d,e,f,g) let popBufferR = function | B1 _ -> assert false | B2 (a,x) -> x, B1 a | B3 (a,b,x) -> x, B2 (a,b) | B4 (a,b,c,x) -> x, B3 (a,b,c) | B5 (a,b,c,d,x) -> x, B4 (a,b,c,d) | B6 (a,b,c,d,e,x) -> x, B5 (a,b,c,d,e) | B7 (a,b,c,d,e,f,x) -> x, B6 (a,b,c,d,e,f) | B8 (a,b,c,d,e,f,g,x) -> x, B7 (a,b,c,d,e,f,g) type 'a t = | Z | Y of 'a buffer | U of 'a buffer * 'a triple t * ('a * 'a) * 'a triple t * 'a buffer and 'a triple = | T1 of 'a buffer | T2 of 'a buffer * 'a buffer | T3 of 'a buffer * 'a triple t * 'a buffer let popLeftNaive = function | Z -> assert false | Y (B1 x) -> x, Z | Y b -> let x, b = popBufferL b in x, Y b | U (pr, ld, mid, rd, sf) -> let x, pr = popBufferL pr in x, U (pr, ld, mid, rd, sf) let popRightNaive = function | Z -> assert false | Y (B1 x) -> x, Z | Y b -> let x, b = popBufferR b in x, Y b | U (pr, ld, mid, rd, sf) -> let x, sf = popBufferR sf in x, U (pr, ld, mid, rd, sf) let rec pushLeft x = function | Z -> Y (B1 x) | Y (B8 (a,b,c,d,e,f,g,h)) -> U (B4 (x,a,b,c), Z, (d,e), Z, B3 (f,g,h)) | Y b -> Y (pushBufferL x b) | U (pr, ld, mid, rd, sf) as d -> match pr with | B6 _ -> let pr, ld, mid, rd, sf = greenLeft pr ld mid rd sf in U (pushBufferL x pr, ld, mid, rd, sf) | _ -> U (pushBufferL x pr, ld, mid, rd, sf) and pushRight x = function | Z -> Y (B1 x) | Y (B8 (a,b,c,d,e,f,g,h)) -> U (B3 (a,b,c), Z, (d,e), Z, B4 (f,g,h,x)) | Y b -> Y (pushBufferR x b) | U (pr, ld, mid, rd, sf) as d -> match sf with | B6 _ -> let pr, ld, mid, rd, sf = greenRight pr ld mid rd sf in U (pr, ld, mid, rd, pushBufferR x sf) | _ -> U (pr, ld, mid, rd, pushBufferR x sf) and greenLeft pr ld mid rd sf = match pr with | B6 (e1,e2,e3,e4,e5,e6) -> let ld = (Obj.magic pushLeft) (T1 (B2 (e5,e6))) ld in B4 (e1,e2,e3,e4), ld, mid, rd, sf | B3 _ -> begin match ld, rd with | Y _, _ | U _, _ -> let t, l = let t, _ = popLeftNaive ld in match t with | T1 (B3 _) | T2 (B3 _, _) | T3 _ -> popLeftNaive ld | _ -> match (Obj.magic popLeft) ld with | Some (x,y) -> x, y | None -> assert false in let (T1 x | T2 (x,_) | T3 (x,_,_)) = t in begin match x with | B3 (e1,e2,e3) -> let p = pushBufferR e1 pr in let x' = B2 (e2,e3) in let l0 = match t with | T3 (_, d', y) -> T3 (x', d', y) | T2 (_, y) -> T2 (x', y) | T1 _ -> T1 x' in let ld = (Obj.magic pushLeft) l0 l in p, ld, mid, rd, sf | B2 (e1,e2) -> let p = pushBufferR e2 (pushBufferR e1 pr) in let l' = match t with | T3 (_, d', y) -> let l = (Obj.magic pushLeft) (T1 y) l in (Obj.magic catenate) d' l | T1 _ -> l | _ -> assert false in p, l', mid, rd, sf | _ -> assert false end | Z, Y _ | Z, U _ -> let t, r = let t, _ = popLeftNaive rd in match t with | T1 (B3 _) | T2 (B3 _, _) | T3 _ -> popLeftNaive rd | _ -> match (Obj.magic popLeft) rd with | Some (x,y) -> x, y | None -> assert false in let m1, m2 = mid in let (T1 x | T2 (x,_) | T3 (x,_,_)) = t in begin match x with | B3 (e1,e2,e3) -> let p = pushBufferR m1 pr in let mid = m2, e1 in let x' = B2 (e2,e3) in let r0 = match t with | T3 (_, d', y) -> T3 (x', d', y) | T2 (_, y) -> T2 (x', y) | T1 _ -> T1 x' in let rd = (Obj.magic pushLeft) r0 r in p, ld, mid, rd, sf | B2 (e1,e2) -> let p = pushBufferR m2 (pushBufferR m1 pr) in let mid = e1, e2 in let r' = match t with | T3 (_, d', y) -> let r = (Obj.magic pushLeft) (T1 y) r in (Obj.magic catenate) d' r | T1 _ -> r | _ -> assert false in p, Z, mid, r', sf | _ -> assert false end | _ -> assert false end | _ -> assert false and greenRight pr ld mid rd sf = match sf with | B6 (e6,e5,e4,e3,e2,e1) -> let rd = (Obj.magic pushRight) (T1 (B2 (e6,e5))) rd in pr, ld, mid, rd, B4 (e4,e3,e2,e1) | B3 _ -> begin match ld, rd with | _, Y _ | _, U _ -> let t, r = let t, _ = popRightNaive rd in match t with | T1 (B3 _) | T2 (_, B3 _) | T3 _ -> popRightNaive rd | _ -> match (Obj.magic popRight) rd with | Some (x,y) -> x, y | None -> assert false in let (T1 x | T2 (_,x) | T3 (_,_,x)) = t in begin match x with | B3 (e3,e2,e1) -> let p = pushBufferL e1 sf in let x' = B2 (e3,e2) in let r0 = match t with | T3 (y, d', _) -> T3 (y, d', x') | T2 (y, _) -> T2 (y, x') | T1 _ -> T1 x' in let rd = (Obj.magic pushRight) r0 r in pr, ld, mid, rd, p | B2 (e2,e1) -> let p = pushBufferL e2 (pushBufferL e1 sf) in let r' = match t with | T3 (y, d', _) -> let r = (Obj.magic pushRight) (T1 y) r in (Obj.magic catenate) r d' | T1 _ -> r | _ -> assert false in pr, ld, mid, r', p | _ -> assert false end | Y _, Z | U _, Z -> let t, l = let t, _ = popRightNaive ld in match t with | T1 (B3 _) | T2 (_, B3 _) | T3 _ -> popRightNaive ld | _ -> match (Obj.magic popRight) ld with | Some (x,y) -> x, y | None -> assert false in let m2, m1 = mid in let (T1 x | T2 (_,x) | T3 (_,_,x)) = t in begin match x with | B3 (e3,e2,e1) -> let p = pushBufferL m1 sf in let mid = e1, m2 in let x' = B2 (e3,e2) in let l0 = match t with | T3 (y, d', _) -> T3 (y, d', x') | T2 (y, _) -> T2 (y, x') | T1 _ -> T1 x' in let ld = (Obj.magic pushRight) l0 l in pr, ld, mid, rd, p | B2 (e2,e1) -> let p = pushBufferL m2 (pushBufferL m1 sf) in let mid = e2, e1 in let l' = match t with | T3 (y, d', _) -> let l = (Obj.magic pushRight) (T1 y) l in (Obj.magic catenate) l d' | T1 _ -> l | _ -> assert false in pr, l', mid, Z, p | _ -> assert false end | _ -> assert false end | _ -> assert false and popLeft = function | Z -> None | Y (B1 x) -> Some (x, Z) | Y b -> let x, b = popBufferL b in Some (x, Y b) | U ((B4 _ | B5 _ | B6 _ as pr), ld, mid, rd, sf) -> let x, pr = popBufferL pr in Some (x, U (pr, ld, mid, rd, sf)) | U ((B3 (x,e1,e2) as pr), Z, (m1,m2), Z, sf) -> let y = match sf with | B3 (f1,f2,f3) -> Y (B7 (e1,e2,m1,m2,f1,f2,f3)) | _ -> let f', sf = popBufferL sf in U (B3 (e1,e2,m1), Z, (m2,f'), Z, sf) in Some (x, y) | U ((B3 _ as pr), ld, mid, rd, sf) -> let pr, ld, mid, rd, sf = greenLeft pr ld mid rd sf in Some (popLeftNaive (U (pr, ld, mid, rd, sf))) | _ -> assert false and popRight = function | Z -> None | Y (B1 x) -> Some (x, Z) | Y b -> let x, b = popBufferR b in Some (x, Y b) | U (pr, ld, mid, rd, (B4 _ | B5 _ | B6 _ as sf)) -> let x, sf = popBufferR sf in Some (x, U (pr, ld, mid, rd, sf)) | U (pr, Z, (m2,m1), Z, (B3 (e2,e1,x) as sf)) -> let y = match pr with | B3 (p3,p2,p1) -> Y (B7 (p3,p2,p1,m2,m1,e2,e1)) | _ -> let f', pr = popBufferR pr in U (pr, Z, (f',m2), Z, B3 (m1,e2,e1)) in Some (x, y) | U (pr, ld, mid, rd, (B3 _ as sf)) -> let pr, ld, mid, rd, sf = greenRight pr ld mid rd sf in Some (popRightNaive (U (pr, ld, mid, rd, sf))) | _ -> assert false and catenate d1 d2 = match d1, d2 with | Z, _ -> d2 | _, Z -> d1 | Y b, _ -> begin match b with | B1 x -> pushLeft x d2 | _ -> let x, b = popBufferR b in catenate (Y b) (pushLeft x d2) end | _, Y b -> begin match b with | B1 x -> pushRight x d1 | _ -> let x, b = popBufferL b in catenate (pushRight x d1) (Y b) end | U (pr1, ld1, mid1, rd1, sf1), U (pr2, ld2, mid2, rd2, sf2) -> let y, pr2 = popBufferL pr2 and x, sf1 = popBufferR sf1 in let mid = x, y in let ld = let m1,m2 = mid1 in let pRight = Obj.magic pushRight in match sf1 with | B2 _ | B3 _ -> pRight (T3 (B2 (m1,m2), rd1, sf1)) ld1 | B4 (e1,e2,e3,e4) -> let ld'1 = pRight (T3 (B2 (m1,m2), rd1, B2 (e1,e2))) ld1 in pRight (T1 (B2 (e3,e4))) ld'1 | B5 (e1,e2,e3,e4,e5) -> let s'1 = B3 (e1,e2,e3) in let ld'1 = pRight (T3 (B2 (m1,m2), rd1, s'1)) ld1 in pRight (T1 (B2 (e4,e5))) ld'1 | _ -> assert false in let rd = let m2,m1 = mid2 in let pLeft = Obj.magic pushLeft in match pr2 with | B2 _ | B3 _ -> pLeft (T3 (pr2, ld2, B2 (m2,m1))) rd2 | B4 (e4,e3,e2,e1) -> let rd'1 = pLeft (T3 (B2 (e2,e1), ld2, B2 (m2,m1))) rd2 in pLeft (T1 (B2 (e4,e3))) rd'1 | B5 (e5,e4,e3,e2,e1) -> let s'1 = B3 (e3,e2,e1) in let rd'1 = pLeft (T3 (s'1, ld2, B2 (m2,m1))) rd2 in pLeft (T1 (B2 (e5,e4))) rd'1 | _ -> assert false in U (pr1, ld, mid, rd, sf2) let nil = Z module type Direction_T = sig val push: 'a -> 'a t -> 'a t val pop: 'a t -> ('a * 'a t) option end module A = struct let push = pushLeft let pop = popLeft end module B = struct let push = pushRight let pop = popRight end let sprintB ff = function | B1 a -> let a = ff a in Printf.sprintf "%s" a | B2 (a,b) -> let a = ff a in let b = ff b in Printf.sprintf "%s,%s" a b | B3 (a,b,c) -> let a = ff a in let b = ff b in let c = ff c in Printf.sprintf "%s,%s,%s" a b c | B4 (a,b,c,d) -> let a = ff a in let b = ff b in let c = ff c in let d = ff d in Printf.sprintf "%s,%s,%s,%s" a b c d | B5 (a,b,c,d,e) -> let a = ff a in let b = ff b in let c = ff c in let d = ff d in let e = ff e in Printf.sprintf "%s,%s,%s,%s,%s" a b c d e | B6 (a,b,c,d,e,f) -> let a = ff a in let b = ff b in let c = ff c in let d = ff d in let e = ff e in let f = ff f in Printf.sprintf "%s,%s,%s,%s,%s,%s" a b c d e f | B7 (a,b,c,d,e,f,g) -> let a = ff a in let b = ff b in let c = ff c in let d = ff d in let e = ff e in let f = ff f in let g = ff g in Printf.sprintf "%s,%s,%s,%s,%s,%s,%s" a b c d e f g | B8 (a,b,c,d,e,f,g,h) -> let a = ff a in let b = ff b in let c = ff c in let d = ff d in let e = ff e in let f = ff f in let g = ff g in let h = ff h in Printf.sprintf "%s,%s,%s,%s,%s,%s,%s,%s" a b c d e f g h let rec sprint f = function | Z -> "{}" | Y b -> Printf.sprintf "{%s}" (sprintB f b) | U (pr, ld, (m1,m2), rd, sf) -> let g = sprintT f in let pr = sprintB f pr in let ld = (Obj.magic sprint) g ld in let mid = sprintB f (B2 (m1,m2)) in let rd = (Obj.magic sprint) g rd in let sf = sprintB f sf in Printf.sprintf "{%s;%s;%s;%s;%s}" pr ld mid rd sf and sprintT f = function | T1 b -> Printf.sprintf "<%s>" (sprintB f b) | T2 (b1,b2) -> Printf.sprintf "(%s/%s)" (sprintB f b1) (sprintB f b2) | T3 (b1,q,b2) -> let b1 = sprintB f b1 in let q = (Obj.magic sprint) (fun x -> sprintT f x) q in let b2 = sprintB f b2 in Printf.sprintf "[%s|%s|%s]" b1 q b2 (*--- End of File [ cf_kotdeque.ml ] ---*) [-- Attachment #3: cf_kotdeque.mli --] [-- Type: application/octet-stream, Size: 1845 bytes --] (*---------------------------------------------------------------------------* INTERFACE cf_kotdeque.mli Copyright (c) 2003, James H. Woodyatt All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *---------------------------------------------------------------------------*) type 'a t val nil: 'a t module type Direction_T = sig val push: 'a -> 'a t -> 'a t val pop: 'a t -> ('a * 'a t) option end module A: Direction_T module B: Direction_T val catenate: 'a t -> 'a t -> 'a t val sprint: ('a -> string) -> 'a t -> string (*--- End of File [ cf_kotdeque.mli ] ---*) [-- Attachment #4: Type: text/plain, Size: 1593 bytes --] > If I remember, the algorithm was described using nonuniform > data types, so that's yet another plug for supporting this more > directly in > OCaml, meaning, without having to use recursive modules, record field > polymorphism, or polymorphic records to work around the ability to > directly write such functions. The structure uses "bootstrapping" and so does my [Cf_deque] structure. I have handled bootstrapping in [Cf] with judicious use of [Obj.magic] to cast functions into the appropriate type at the recursion. Note: My [Cf] library is loaded with calls to [Obj.magic]. There are lots of places where the only way I could solve a problem was by hammering something deeply weird into a shape that would fit into the Ocaml type system. Mostly, that happens where I have an interface to C library stuff and I'm using shadow type annotations. But the bootstrapped data structures are another place. And the [Cf_gadget] module needs to do something similar (which was well known in the original Haskell code where that idea started). Every time the Ocaml team updates the compiler, I'm worried they're going to change something that invalidates my assumptions about what [Obj.magic] can and cannot do for me. I'm *not* saying they didn't warn me. I knew the job was dangerous. I'd be more exercised about the 'looping recursion' problem with List.map exploding on large data sets if I were not in the habit of using my [Cf] structures to avoid all that. -- j h woodyatt <jhw@wetware.com> that's my village calling... no doubt, they want their idiot back. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] kaplan-okasaki-tarjan deque (was "looping recursion") 2004-07-30 18:25 ` [Caml-list] kaplan-okasaki-tarjan deque (was "looping recursion") james woodyatt @ 2004-07-30 21:20 ` brogoff 2004-07-31 5:37 ` james woodyatt 0 siblings, 1 reply; 44+ messages in thread From: brogoff @ 2004-07-30 21:20 UTC (permalink / raw) To: james woodyatt; +Cc: Ocaml Trade On Fri, 30 Jul 2004, james woodyatt wrote: > On 30 Jul 2004, at 10:07, brogoff wrote: > > > > I would like to see an implementation of the catenable deques only > > using simple list ops (not laziness) described by Kaplan and Tarjan, > > in OCaml. > > Sure. Here is the basic implementation I did for performance > comparisons. Thanks. I emailed you back a version with the magic gone, using the recursive module extension to bulldozer over your abuse of the type system ;-). It's the least offensive (IMO of course) of the current workarounds. I wonder if the implementors can tell us if there is any hope that we'll see some better solutions in the near future? BTW, what did your comparisons tell you? -- Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] kaplan-okasaki-tarjan deque (was "looping recursion") 2004-07-30 21:20 ` brogoff @ 2004-07-31 5:37 ` james woodyatt 0 siblings, 0 replies; 44+ messages in thread From: james woodyatt @ 2004-07-31 5:37 UTC (permalink / raw) To: brogoff; +Cc: Ocaml Trade On 30 Jul 2004, at 14:20, brogoff wrote: > > BTW, what did your comparisons tell you? It told me that my implementation was enough faster that it was worth the price of having to convert between deques and lists in order to marshall them to disk or on the wire, with noticeable savings even after that. I can't remember the numbers right now. At one time, I thought it would be worth the effort of writing an academic paper on the subject, but I never learned how to write an academic paper so it will get published. So I haven't tried. Sigh. One of the questions that remains unanswered is how much time my implementation of Kaplan-Okasaki-Tarjan wastes being inefficient about handling the fixed size buffers. My gut tells me there's room for improvement there, but not enough to overcome the advantage my implementation has over it. I need to measure that. ― ∴ ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 1:43 ` Brian Hurt 2004-07-28 2:49 ` briand @ 2004-07-28 7:27 ` skaller 2004-07-28 14:36 ` Brian Hurt 1 sibling, 1 reply; 44+ messages in thread From: skaller @ 2004-07-28 7:27 UTC (permalink / raw) To: Brian Hurt; +Cc: briand, John Prevost, Ocaml Mailing List On Wed, 2004-07-28 at 11:43, Brian Hurt wrote: > On Tue, 27 Jul 2004 briand@aracnet.com wrote: > Very long lists are a sign that you're using the wrong data structure. What would you recommend for a sequence of tokens? Streams are slow and hard to match on.. bucket lists have lower storage overhead but hard to match on. -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 7:27 ` [Caml-list] looping recursion skaller @ 2004-07-28 14:36 ` Brian Hurt 2004-07-28 22:05 ` skaller 0 siblings, 1 reply; 44+ messages in thread From: Brian Hurt @ 2004-07-28 14:36 UTC (permalink / raw) To: skaller; +Cc: briand, John Prevost, Ocaml Mailing List On 28 Jul 2004, skaller wrote: > On Wed, 2004-07-28 at 11:43, Brian Hurt wrote: > > On Tue, 27 Jul 2004 briand@aracnet.com wrote: > > > Very long lists are a sign that you're using the wrong data structure. > > What would you recommend for a sequence of tokens? > Streams are slow and hard to match on.. bucket lists > have lower storage overhead but hard to match on. Extlib Enumerations. For short lists, yeah they're slower than lists. But for long lists, I could see them being a lot faster. Don't forget cache effects- streaming processing can have much better cache behavior than repeatedly walking a long list (too large to fit into cache). -- "Usenet is like a herd of performing elephants with diarrhea -- massive, difficult to redirect, awe-inspiring, entertaining, and a source of mind-boggling amounts of excrement when you least expect it." - Gene Spafford Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-28 14:36 ` Brian Hurt @ 2004-07-28 22:05 ` skaller 0 siblings, 0 replies; 44+ messages in thread From: skaller @ 2004-07-28 22:05 UTC (permalink / raw) To: Brian Hurt; +Cc: briand, John Prevost, Ocaml Mailing List On Thu, 2004-07-29 at 00:36, Brian Hurt wrote: > On 28 Jul 2004, skaller wrote: > > > On Wed, 2004-07-28 at 11:43, Brian Hurt wrote: > > > On Tue, 27 Jul 2004 briand@aracnet.com wrote: > > > > > Very long lists are a sign that you're using the wrong data structure. > > > > What would you recommend for a sequence of tokens? > > Streams are slow and hard to match on.. bucket lists > > have lower storage overhead but hard to match on. > > Extlib Enumerations. For short lists, yeah they're slower than lists. That doesn't matter -- the lists are long by specification. > But for long lists, I could see them being a lot faster. Don't forget > cache effects- streaming processing can have much better cache behavior > than repeatedly walking a long list (too large to fit into cache). Can't pattern match on them. One reason for building a list is I filter it, for example, in Felix I strip out white space tokens, in Vyper (Python interpreter written in Ocaml) I did something like 13 separate passes to handle the indentation and other quirks to precondition the input to the parser so it became LALR(1). So, I'd have to use a list as a buffer for the head of the stream anyhow.. Also, there is a serious design problem with ExtLib Enums. Although the data structure appears functional, it doesn't specify when things happen precisely. In particular if the input is a stream, that is, uses mutators to extract elements, then instead of using the persistence and laziness so you can use the Enums as forward iterators -- for example in a backtracking parser -- the Enums actually degrade to uncopyable input iterators. Since Ocamllex uses a mutable lex buffer, the Enums based on them are also non-functional input iterators .. [I can get around that by calling 'force()' but that totally defeats the purpose of using Enums .. :] Whereas, a plain old list is a purely functional forward iterator, and unquestionably works with a backtracking parser. As an example of a simple modification I could do that won't work easily with uncontrolled control inversion: suppose I cache the token stream on disk, and in particular Marshal file 'fred.flx' out as 'fred.tokens'. [Now you *have* to force() all the iterators, or each one inside the #include will write the file to disk at the end of the sub-file .. but that should only be done once -- its quite slow writing a file to disk .. forcing all the enums makes separate copies of the tokens .. argggg .. ] The problem goes away when I manually build lists and preprocess them because I have explicit control. Bottom line is that Enums work fine to integrate purely functional data structures together but they're not very useful mixing coupled streams together. Crudely -- if you have a hierarchy of streams you may need to read them in a particular order due to the coupling .. with STL input iterators you can do that, with hand written Ocaml you can do that -- with Enums you can't. -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] looping recursion 2004-07-27 23:43 [Caml-list] looping recursion briand 2004-07-28 0:27 ` John Prevost @ 2004-07-28 0:37 ` skaller 1 sibling, 0 replies; 44+ messages in thread From: skaller @ 2004-07-28 0:37 UTC (permalink / raw) To: briand; +Cc: caml-list On Wed, 2004-07-28 at 09:43, briand@aracnet.com wrote: > And since I'm not actually accumulating anything, I'm most > confused as to how the stack space is disappearing. The recursion is stacking up the exception handlers. -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 44+ messages in thread
end of thread, other threads:[~2004-07-31 5:37 UTC | newest] Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2004-07-27 23:43 [Caml-list] looping recursion briand 2004-07-28 0:27 ` John Prevost 2004-07-28 0:38 ` John Prevost 2004-07-28 1:17 ` skaller 2004-07-28 1:05 ` briand 2004-07-28 1:43 ` Brian Hurt 2004-07-28 2:49 ` briand 2004-07-28 3:12 ` Brian Hurt 2004-07-28 3:20 ` Brian Hurt 2004-07-28 5:54 ` brogoff 2004-07-28 7:22 ` Alex Baretta 2004-07-28 16:38 ` brogoff 2004-07-28 19:40 ` Jon Harrop 2004-07-28 20:18 ` Brandon J. Van Every 2004-07-29 6:01 ` Alex Baretta 2004-07-28 21:22 ` brogoff 2004-07-29 9:13 ` Daniel Andor 2004-07-29 9:25 ` Keith Wansbrough 2004-07-29 9:41 ` Nicolas Cannasse 2004-07-29 9:57 ` Xavier Leroy 2004-07-29 10:44 ` Daniel Andor 2004-07-29 12:56 ` brogoff 2004-07-29 10:11 ` skaller 2004-07-29 12:41 ` brogoff 2004-07-29 6:28 ` Alex Baretta 2004-07-29 14:58 ` brogoff 2004-07-29 16:12 ` Brian Hurt 2004-07-29 17:49 ` james woodyatt 2004-07-29 19:25 ` Brian Hurt 2004-07-29 20:01 ` brogoff 2004-07-30 4:42 ` james woodyatt 2004-07-29 17:44 ` james woodyatt 2004-07-29 23:12 ` skaller 2004-07-29 22:42 ` Alex Baretta 2004-07-30 2:38 ` Corey O'Connor [not found] ` <200407300136.14042.jon@jdh30.plus.com> 2004-07-30 12:45 ` Alex Baretta 2004-07-30 17:07 ` brogoff 2004-07-30 18:25 ` [Caml-list] kaplan-okasaki-tarjan deque (was "looping recursion") james woodyatt 2004-07-30 21:20 ` brogoff 2004-07-31 5:37 ` james woodyatt 2004-07-28 7:27 ` [Caml-list] looping recursion skaller 2004-07-28 14:36 ` Brian Hurt 2004-07-28 22:05 ` skaller 2004-07-28 0:37 ` skaller
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox