* Ant: Re: [Caml-list] Avoiding shared data [not found] <Pine.LNX.4.63.0509251653340.9226@localhost.localdomain> @ 2005-09-26 21:29 ` Martin Chabr 0 siblings, 0 replies; 10+ messages in thread From: Martin Chabr @ 2005-09-26 21:29 UTC (permalink / raw) To: Brian Hurt; +Cc: caml-list Hello Brian, thank you for the whole lot of useful tips and nice explanations. It is immediately clear to me how to copy a record and a tuple, but I will need some time to think about your suggestions using a map. Intuitively I feel that it needs a method to create non-shared data structures in the first place, so that no copying whatsoever is needed. Regards, Martin --- Brian Hurt <bhurt@spnz.org> schrieb: > > > On Sun, 25 Sep 2005, Martin Chabr wrote: > > > Working with non-shared data structures in OCaml > > Deep copy of OCaml structures, marshaling > > ================================================ > > > > Dear group members, > > > > I need to process arrays of pairs of integers and > > records ((int * record) array) in which all > elements > > must be updated individually, which means that > > unshared data structures must be used. For arrays > of > > arrays I can produce unshared data by using the > > library functions Array.copy and Array.append to > > append the individual arrays into the embedding > array. > > It works, the low level arrays can be updated > > individually. But I cannot use the same scheme to > the > > array of the (int * record) structures, because I > do > > not know how to copy these structures to dissolve > the > > sharing. I do not even know how to copy records. > It > > seems to me that this problem occurs always when I > > want to produce an array of data with a fixed > > structure automatically (rather than entering the > > array [| ... |] by hand at the top level > interpreter > > using constants only). How can I produce > completely > > unshared structures? > > First of all, you can copy a structure easily enough > using the with key > word. Say you have: > > type my_struct = { > foo: int; > bar: float; > baz: string; > } > > You can write a copy function just like: > > let copy_my_struct x = { x with foo = x.foo };; > > In this case, the "with" construct says "create a > new structure with the > same elements as the old structure, except give the > foo member this new > value (which in this case just happens to be the > same value as the old > value)". Unfortunately you have to specify at least > one new value to use > the with construct, even if it doesn't get a new > value. This is > especially usefull if there are other value you want > to copy, for example, > you probably want to write: > > let copy_my_struct x = { x with baz = String.copy > x.baz } > > As this doesn't just create a new copy of the > structure, it also creates a > new copy of the string as well (foo and bar get > whatever values the > original structure had). > > I can create a new tuple just by breaking the old > tuple apart and putting > it back together again. For all two element tuples, > I can create a new > copy of them just by going: > > let copy_tuple (a,b) = a,b > > This function, given a tuple of two elements, > creates a new tuple with the > same two elements. > > The easiest way to create a new list from old values > is to just use the > map function. The output list type of List.map can > be the same type as > the input list. This is really usefull- imagine > that I want to add 1 each > to a list of floats, I can just write: > > List.map (fun i -> i + 1) lst > > So, to answer you specific question, say I have an > (int * my_struct) list > (where t is the structure I defined above). The > function to create a > whole new copy of this list (assuming I've written > copy_t like I did > above) is just: > > let copy_list lst = List.map (fun (i,s) -> i, > (copy_my_struct s)) lst > > Notice that I made copy_tuple an anonymous (unnamed) > function there. With > a couple of more tricks I can fit the whole thing on > one line: > > let cplst = List.map (fun (i,s) -> i, {s with baz = > String.copy s.baz}) > > Which is nice for bragging rights, but I kind of > like the more unpacked > version. > > Now, I'm going to jump from the question you asked > to the question you > didn't ask, and make a big assumption along the way. > I'd bet dollars to > donuts that you really don't want to be using either > lists or array- you > really want to be using a map. Let me guess: you've > written a function > like: > > let rec find i lst = (* find element i in list lst) > match lst with > | (j, s) :: t -> > if i == j then > s > else > find i t > | [] -> raise Not_found > > and are calling it a lot (or rewritting it a lot). > If this is the case, > then what you really want to be using is a map, not > a list or an array. > You can do this with the following code: > > module Int = struct > type t = int > let compare (x: int) y = > if x < y then > -1 > else if x > y then > 1 > else > 0 > end;; > > module IntMap = Map.Make(Int);; > > Now you can just keep your structures in a my_struct > IntMap.t. The find > function is now: > let find i lst = (* lst is a IntMap, not a list *) > IntMap.find i lst > ;; > > The big difference here is that IntMap.find is O(log > N) cost, while the > list-based find I wrote above is O(N) cost. What > this means is that if > the list is 1000 elements long, the list-based find > will have to do (on > aveage) about 500 steps of processing to find the > answer- it has to walk > halfway down the list. The IntMap.find function, > however, only has to do > about 10 steps on average to find the answer- it's > literally 50 times > faster to do the IntMap.find in this case than the > list-based find. If > we're dealing with a million elements, the > list-based find will take > 500,000 steps to find the answer on average, the > IntMap.find only 20, for > an incredible speed up of 25,000 times. > > Note that IntMap has a map function as well- so I > can steal the exact same > trick to copy it (but note that I don't have to > allocate the new tuples): > > let copy_list lst = (* list is an IntMap, not a list > *) > IntMap.map copy_my_struct lst > ;; > > If you actually want the associated integers as > well, use mapi instead of > map. > > Note that O(log N) vr.s O(N) thing applies to > updates as well. Let's say > === message truncated === ___________________________________________________________ Was denken Sie über E-Mail? Wir hören auf Ihre Meinung: http://surveylink.yahoo.com/wix/p0379378.aspx ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Avoiding shared data
@ 2005-09-26 8:17 William Lovas
2005-09-26 21:07 ` Ant: " Martin Chabr
0 siblings, 1 reply; 10+ messages in thread
From: William Lovas @ 2005-09-26 8:17 UTC (permalink / raw)
To: caml-list; +Cc: Martin Chabr
Hi Martin,
On Sun, Sep 25, 2005 at 11:32:02PM +0200, Martin Chabr wrote:
> [...] But I cannot use the same scheme to the
> array of the (int * record) structures, because I do
> not know how to copy these structures to dissolve the
> sharing. I do not even know how to copy records.
> [...] How can I produce completely
> unshared structures?
Maybe i'm missing something, but if these are unmutable records, then why
do you need to concern yourself with any potential sharing? As long as the
array cells are not "shared" -- which they can't be, as far as i know --
you can update each one individually no matter what the sharing status of
their contents is.
If the records *are* mutable, then the suggestion to use Array.init should
be sufficient.
Hoping i might save you some work :)
William
^ permalink raw reply [flat|nested] 10+ messages in thread
* Ant: Re: [Caml-list] Avoiding shared data 2005-09-26 8:17 William Lovas @ 2005-09-26 21:07 ` Martin Chabr 2005-09-26 22:08 ` Jon Harrop 2005-09-30 22:57 ` Oliver Bandel 0 siblings, 2 replies; 10+ messages in thread From: Martin Chabr @ 2005-09-26 21:07 UTC (permalink / raw) To: William Lovas; +Cc: caml-list Hello William, I am using a mutable record. I am programming this 90% in the imperative (non-functional) style, so that I can rewrite critical parts into Fortran easily. Another reason is, I am an intermediate user and finding out whether the recursion is a tail-one or not is difficult for me. This is a kind of number crunching problem and the data structures will be huge. Blessed are the creators of OCaml for the inclusion of all imperative constructs. Martin --- William Lovas <wlovas@stwing.upenn.edu> schrieb: > Hi Martin, > > On Sun, Sep 25, 2005 at 11:32:02PM +0200, Martin > Chabr wrote: > > [...] But I cannot use the same scheme to the > > array of the (int * record) structures, because I > do > > not know how to copy these structures to dissolve > the > > sharing. I do not even know how to copy records. > > [...] How can I produce completely > > unshared structures? > > Maybe i'm missing something, but if these are > unmutable records, then why > do you need to concern yourself with any potential > sharing? As long as the > array cells are not "shared" -- which they can't be, > as far as i know -- > you can update each one individually no matter what > the sharing status of > their contents is. > > If the records *are* mutable, then the suggestion to > use Array.init should > be sufficient. > > Hoping i might save you some work :) > > William > Martin Chabr Hochstrasse 28 8044 Zürich Schweiz / Switzerland Tel.P.: 01-261 17 24 ___________________________________________________________ Gesendet von Yahoo! Mail - Jetzt mit 1GB Speicher kostenlos - Hier anmelden: http://mail.yahoo.de ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-09-26 21:07 ` Ant: " Martin Chabr @ 2005-09-26 22:08 ` Jon Harrop 2005-09-30 22:57 ` Oliver Bandel 1 sibling, 0 replies; 10+ messages in thread From: Jon Harrop @ 2005-09-26 22:08 UTC (permalink / raw) To: caml-list On Monday 26 September 2005 22:07, Martin Chabr wrote: > I am using a mutable record. I am programming this 90% > in the imperative (non-functional) style, so that I > can rewrite critical parts into Fortran easily. In case you haven't already - I'd advise you to write a simple working version first. -- Dr Jon D Harrop, Flying Frog Consultancy Ltd. Objective CAML for Scientists http://www.ffconsultancy.com/products/ocaml_for_scientists ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-09-26 21:07 ` Ant: " Martin Chabr 2005-09-26 22:08 ` Jon Harrop @ 2005-09-30 22:57 ` Oliver Bandel 2005-10-01 0:07 ` Pal-Kristian Engstad 1 sibling, 1 reply; 10+ messages in thread From: Oliver Bandel @ 2005-09-30 22:57 UTC (permalink / raw) To: caml-list On Mon, Sep 26, 2005 at 11:07:30PM +0200, Martin Chabr wrote: > Hello William, > > I am using a mutable record. I am programming this 90% > in the imperative (non-functional) style, so that I > can rewrite critical parts into Fortran easily. > Another reason is, I am an intermediate user and > finding out whether the recursion is a tail-one or not > is difficult for me. When you 90% of your code are writing in imperative style and do not go deeper into the functional/recursive world, you will never be able to distinguish between tail-rec and non-tail-rec style. But: It is not really hard to find the distinction betwen the two styles, but often the explanations are not made well. Sometimes it's only one or two words in an explanation about tail-rec/non-tail-rec that must be substituted by other words, and the distinction can be made visible very easy. On the other hand: writing mor funtional/recursive code will make you more used to to this... Ciao, Oliver ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-09-30 22:57 ` Oliver Bandel @ 2005-10-01 0:07 ` Pal-Kristian Engstad 2005-10-01 5:46 ` Bill Wood ` (2 more replies) 0 siblings, 3 replies; 10+ messages in thread From: Pal-Kristian Engstad @ 2005-10-01 0:07 UTC (permalink / raw) To: caml-list; +Cc: Oliver Bandel On Friday 30 September 2005 03:57 pm, Oliver Bandel wrote: > On the other hand: writing mor funtional/recursive code will > make you more used to to this... I've always thought that this was a really bad argument from the ML camp. The logic of complicated control-paths is very easily made a zillion times worse by writing in a tail-recursive style. It is *not* a good programming practice to make hard-to-read code! I encourage people to read the paper by Olin Shivers: "The Anatomy of a Loop - A story of scope and control", which was presented at ICFP 2005, and can be found at http://www.cc.gatech.edu/~shivers/papers/loop.pdf. The author argues that "Writing loops with tail-recursive function calls is the equivalent of writing them with goto’s." and gives an example that I've rewritten from Scheme-ish into OCaml-ish: let myfunc l = let rec loop rest result = match rest with | [] -> List.rev result | x::xs -> if xpred x then let y = verbose_code_using_x x in if ypred y then let z = verbose_code_using_y y in loop xs (z_expression :: result) else loop xs result else loop xs result in loop l [] ;; Obviously, one would like to refactor this into HOF, but in this situation it is hard to see how one should do it. The author instead proposes to use loops in a monadic style, which I've again rewritten: let myfunc l = loop [ for x in l ; when xpred x ; let y = verbose_code_using_x x ; when ypred y ; let z = verbose_code_using_y y ; save z ] The point being (syntax aside) that this code is much more readible, easier to change and easier to verify than the code given above. [Of course, Haskell has the really cool list-comprehension syntax that alleviates some of ML's problems, but still.] Thanks, PKE. -- _ \`. Pål-Kristian Engstad, Lead Programmer, \ `| Naughty Dog, Inc., 1601 Cloverfield Blvd, 6000 North, __\ |`. Santa Monica, CA 90404, USA. (310) 633-9112. / /o mailto:engstad@naughtydog.com http://www.naughtydog.com / '~ mailto:mrengstad@yahoo.com http://www.engstad.com / ,' Hang-gliding Rulez! ~' ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 0:07 ` Pal-Kristian Engstad @ 2005-10-01 5:46 ` Bill Wood 2005-10-01 8:27 ` Wolfgang Lux 2005-10-01 12:34 ` Oliver Bandel 2 siblings, 0 replies; 10+ messages in thread From: Bill Wood @ 2005-10-01 5:46 UTC (permalink / raw) To: caml-list . . . > I've always thought that this was a really bad argument from the ML camp. The > logic of complicated control-paths is very easily made a zillion times worse > by writing in a tail-recursive style. It is *not* a good programming practice > to make hard-to-read code! > > I encourage people to read the paper by Olin Shivers: "The Anatomy of a Loop - > A story of scope and control", which was presented at ICFP 2005, and can be > found at http://www.cc.gatech.edu/~shivers/papers/loop.pdf. > > The author argues that "Writing loops with tail-recursive function calls is > the equivalent of writing them with goto’s." and gives an example that I've > rewritten from Scheme-ish into OCaml-ish: > > let myfunc l = > let rec loop rest result = > match rest with > | [] -> List.rev result > | x::xs -> > if xpred x then > let y = verbose_code_using_x x in > if ypred y then > let z = verbose_code_using_y y in > loop xs (z_expression :: result) > else > loop xs result > else > loop xs result > in > loop l [] > ;; > > Obviously, one would like to refactor this into HOF, but in this situation it > is hard to see how one should do it. The author instead proposes to use loops > in a monadic style, which I've again rewritten: > > let myfunc l = > loop [ for x in l > ; when xpred x > ; let y = verbose_code_using_x x > ; when ypred y > ; let z = verbose_code_using_y y > ; save z > ] > > The point being (syntax aside) that this code is much more readible, easier to > change and easier to verify than the code given above. [Of course, Haskell > has the really cool list-comprehension syntax that alleviates some of ML's > problems, but still.] I about half agree with Mr. Engstad's observation. I have often used the fact that the accumulator passed around in tail-recursive functions acts like a state (some people call these *state threads*), and I've used techniques like pushing Dijkstra's weakest-precondition predicate transformers over accumulators to reason about tail-recursive functional code almost as if it were imperative (the only difference with Mr. Shivers is that my programming is goto-less :-). However, the example is a little unfortunate in that transforming it to an arguably cleaner HOF form is fairly easy. If we first define function composition as an operator (shouldn't this be an OCaml Pervasive?), say let ($@) fyz fxy x = fyz (fxy x);; then the tail-recursive version of myfunc transforms into my_hof_func: let my_hof_func l = let fumble = let save = rev $@ (fold_left (fun la i -> i :: la) []) in save $@ (map verbose_code_using_y) $@ (filter ypred) $@ (map verbose_code_using_x) $@ (filter xpred) in fumble l;; where packaging and return of the result has been encapsulated in the local function 'save' (I'm also assuming z_expression was supposed to be z). A minor problem is that the textual order of the predicates and functions is reversed. Even this can be handled by a sleazy trick -- reverse the order of the operands to the compose operator like so: let ($@) fxy fyz x = fyz (fxy x);; We can then rewrite my_hof_func as let my_hof_func l = let fumble = let save = (fold_left (fun la i -> i :: la) []) $@ rev in (filter xpred) $@ (map verbose_code_using_x) $@ (filter ypred) $@ (map verbose_code_using_y) $@ save in fumble l;; The actions/functions are read in the order they are performed/applied, and trivial reformatting even makes it look sorta like imperative code. A very similar transformation could be done on the "Scheme-ish" original, where a variable-arity form (compose f1 ... fn) is used to equally good effect. I think the ease-of-reading issue can now be revisited on a slightly more even playing field. The transformed, HOF, code is a couple of lines longer, but the meaning is fairly transparent because native OCaml facilities are used throughout. The monadic form above is a trifle shorter, but has the liability that the new syntax has complex semantics -- what *exactly* is going on with "when" and "save"?. I note also that the monadic form looks very similar to the infamous Common Lisp "loop" facility (the one towards the back of The Book, with all the keywords). Many lispers love it, and many loathe it. Does anybody know if MLers have looked at either the Series or the Generators-and-Gatherers described in Appendices A and B of Common Lisp the Language, 2nd ed.? Some of the ideas there look interesting. Interesting topic, -- Bill Wood bill.wood@acm.org ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 0:07 ` Pal-Kristian Engstad 2005-10-01 5:46 ` Bill Wood @ 2005-10-01 8:27 ` Wolfgang Lux 2005-10-01 18:02 ` Wolfgang Lux 2005-10-01 12:34 ` Oliver Bandel 2 siblings, 1 reply; 10+ messages in thread From: Wolfgang Lux @ 2005-10-01 8:27 UTC (permalink / raw) To: Pal-Kristian Engstad; +Cc: caml-list, Oliver Bandel Pal-Kristian Engstad wrote: > The author argues that "Writing loops with tail-recursive function > calls is > the equivalent of writing them with goto’s." and gives an example that > I've > rewritten from Scheme-ish into OCaml-ish: > > let myfunc l = > let rec loop rest result = > match rest with > | [] -> List.rev result > | x::xs -> > if xpred x then > let y = verbose_code_using_x x in > if ypred y then > let z = verbose_code_using_y y in > loop xs (z_expression :: result) > else > loop xs result > else > loop xs result > in > loop l [] > ;; > > Obviously, one would like to refactor this into HOF, but in this > situation it > is hard to see how one should do it. Oh come on! IMHO, most loops can easily be transformed by using map, filter, fold_left, and fold_right. The example given is no exception. The loop essentially applies some complex code to every element of the list and includes the result in the output list only if a condition is satisfied that is computed along with result. This is always the structure of a fold. (If the condition were independent from the result one could combine a filter and a map.) Thus, a straight forward rewrite of the loop is: let myfunc l = let f x result = if xpred then let y = verbose_code_using_x x in if ypred y then let z = verbose_code_using_y y in z_expression :: result else result else result in fold_right f l [] Furthermore, I would turn the two if expressions into local functions let myfunc l = let g y result = if ypred y then let z = verbose_code_using_y y in z_expression :: result else result in let f x result = if xpred x then let y = verbose_code_using_x x in g y result else result in fold_right f l [] Regards Wolfgang ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 8:27 ` Wolfgang Lux @ 2005-10-01 18:02 ` Wolfgang Lux 0 siblings, 0 replies; 10+ messages in thread From: Wolfgang Lux @ 2005-10-01 18:02 UTC (permalink / raw) To: Wolfgang Lux; +Cc: Pal-Kristian Engstad, caml-list, Oliver Bandel Wolfgang Lux wrote: > let myfunc l = > let g y result = > if ypred y then > let z = verbose_code_using_y y in z_expression :: result > else > result > in let f x result = > if xpred x then > let y = verbose_code_using_x x in g y result > else > result > in fold_right f l [] Just correcting myself. The code should use fold_left rather than fold_right (and thus switch the parameters in the call as well as that of the local functions f and g) because the former is tail recursive and therefore should be preferred in a strict language (have been programming too much Haskell lately where a right fold is better). Wolfghang ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 0:07 ` Pal-Kristian Engstad 2005-10-01 5:46 ` Bill Wood 2005-10-01 8:27 ` Wolfgang Lux @ 2005-10-01 12:34 ` Oliver Bandel 2005-10-01 13:58 ` Bill Wood 2 siblings, 1 reply; 10+ messages in thread From: Oliver Bandel @ 2005-10-01 12:34 UTC (permalink / raw) To: caml-list On Fri, Sep 30, 2005 at 05:07:00PM -0700, Pal-Kristian Engstad wrote: > On Friday 30 September 2005 03:57 pm, Oliver Bandel wrote: > > On the other hand: writing mor funtional/recursive code will > > make you more used to to this... > > I've always thought that this was a really bad argument from the ML camp. It is not a bad argument from the ML camp. It's always so, that if you have more practice you will be more used to something and you learn it better. If you don't practise, learning is abandoned. This has nothing to do with programming languages. [...] > The > logic of complicated control-paths is very easily made a zillion times worse > by writing in a tail-recursive style. It is *not* a good programming practice > to make hard-to-read code! Some things are better wriiten down functionally, others are better suited for using impoerative code. Since I get more and more used to using functional and recursive code writing, I can better decide, which way is better. And more and more often I decide to use the recursive style. In OCaml you are not restricted to it, but if you only use one style of programming, and never practise the other programming styles, you can't see, when which programming style/paradigm is better, and the original poster said something about the distinction of tail-rec vs. non tail-rec. (It was not about if that style makes sense or not.) If you practise more of that stuff, and reading some good explanations, then it's obvious, which solution is tail-rec and which is not. > > I encourage people to read the paper by Olin Shivers: "The Anatomy of a Loop - > A story of scope and control", which was presented at ICFP 2005, and can be > found at http://www.cc.gatech.edu/~shivers/papers/loop.pdf. Thanks for the link. > > The author argues that "Writing loops with tail-recursive function calls is > the equivalent of writing them with goto???s." I doubt that the author writes that. You mean for/while instead of goto's as a substitute for recursive functions...?! Ciao, Oliver ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 12:34 ` Oliver Bandel @ 2005-10-01 13:58 ` Bill Wood 0 siblings, 0 replies; 10+ messages in thread From: Bill Wood @ 2005-10-01 13:58 UTC (permalink / raw) To: caml-list . . . > > The author argues that "Writing loops with tail-recursive function calls is > > the equivalent of writing them with goto???s." > > I doubt that the author writes that. > You mean for/while instead of goto's as a substitute for > recursive functions...?! Actually, the author does say that. More precisely, Shivers refers to Steele's characterization of lambda as "gotos with arguments"; I think the source is the paper "Lambda: the Ultimate GOTO". -- Bill Wood bill.wood@acm.org ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2005-10-01 18:02 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- [not found] <Pine.LNX.4.63.0509251653340.9226@localhost.localdomain> 2005-09-26 21:29 ` Ant: Re: [Caml-list] Avoiding shared data Martin Chabr 2005-09-26 8:17 William Lovas 2005-09-26 21:07 ` Ant: " Martin Chabr 2005-09-26 22:08 ` Jon Harrop 2005-09-30 22:57 ` Oliver Bandel 2005-10-01 0:07 ` Pal-Kristian Engstad 2005-10-01 5:46 ` Bill Wood 2005-10-01 8:27 ` Wolfgang Lux 2005-10-01 18:02 ` Wolfgang Lux 2005-10-01 12:34 ` Oliver Bandel 2005-10-01 13:58 ` Bill Wood
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox