caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Re: [Caml-list] Preferred Way to Split a List
  2007-10-30  7:58     ` Alain Frisch
@ 2007-10-29  9:34       ` Christophe Raffalli
  2007-10-30 11:31         ` skaller
  2007-10-30 12:30         ` David Allsopp
  2007-10-30 11:15       ` skaller
  1 sibling, 2 replies; 16+ messages in thread
From: Christophe Raffalli @ 2007-10-29  9:34 UTC (permalink / raw)
  To: Alain Frisch; +Cc: skaller, caml-list


[-- Attachment #1.1: Type: text/plain, Size: 1043 bytes --]

Or

let split l n =
  let rec aux acc l n =
     if n = 0 then List.rev acc, l
     else match l with
        [] -> List.rev acc, []
     | hd::l -> aux (hd::acc) l (n-1)
  in aux [] l n

This is tail recursive, it does not use magic nor  references
and keed the original ordering of the list.

Finally, I did in the past some testing  comparing a tail-rec append or map
using magic and the composition of rev and rev-append or rev-map ...
There was no noticable difference, because the extra cost  of
mutability (regarding the managment of the grey val list) compensate
the extra call to reverse.

So I think I would write split the way shown above if ordering matters.

Cheers,
Christophe
>
> Alain
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs

[-- Attachment #1.2: Christophe.Raffalli.vcf --]
[-- Type: text/x-vcard, Size: 380 bytes --]

begin:vcard
fn:Christophe Raffalli
n:Raffalli;Christophe
org:LAMA (UMR 5127)
email;internet:Christophe.Raffalli@univ-savoie.fr
title;quoted-printable:Ma=C3=AEtre de conf=C3=A9rences
tel;work:+33 4 79 75 81 03
note;quoted-printable:Message sign=C3=A9 cryptographiquement avec pgp, la clef publique est sur=
	 subkeys.pgp.net
x-mozilla-html:TRUE
version:2.1
end:vcard


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 252 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Preferred Way to Split a List
@ 2007-10-29 23:33 Robert Fischer
  2007-10-30  0:45 ` [Caml-list] " Matthew William Cox
                   ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Robert Fischer @ 2007-10-29 23:33 UTC (permalink / raw)
  To: caml-list

What is the preferred way to split a list into two, at an arbitrary 
point?  There's lots of ways you could do it, but I'm not sure if 
there's a standard best practice for this.

~~ Robert.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-29 23:33 Preferred Way to Split a List Robert Fischer
@ 2007-10-30  0:45 ` Matthew William Cox
  2007-10-30 13:18   ` Robert Fischer
  2007-10-30  1:20 ` Julien Moutinho
  2007-10-30  7:50 ` Jon Harrop
  2 siblings, 1 reply; 16+ messages in thread
From: Matthew William Cox @ 2007-10-30  0:45 UTC (permalink / raw)
  To: caml-list

[-- Attachment #1: Type: text/plain, Size: 550 bytes --]

On Mon, Oct 29, 2007 at 06:33:11PM -0500, Robert Fischer wrote:
> What is the preferred way to split a list into two, at an arbitrary point?  
> There's lots of ways you could do it, but I'm not sure if there's a 
> standard best practice for this.
That's the thing, there are lots of best ways. Which is the best depends
on what properties your list-splitter needs.

Do you need to preserve ordering in the sublists? Do they need to be
roughly equivalent lengths? Elaborating on these requirements will guide
your search.

Cheers,
Matt

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 827 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-29 23:33 Preferred Way to Split a List Robert Fischer
  2007-10-30  0:45 ` [Caml-list] " Matthew William Cox
@ 2007-10-30  1:20 ` Julien Moutinho
  2007-10-30  1:38   ` Karl Zilles
  2007-10-30  5:46   ` skaller
  2007-10-30  7:50 ` Jon Harrop
  2 siblings, 2 replies; 16+ messages in thread
From: Julien Moutinho @ 2007-10-30  1:20 UTC (permalink / raw)
  To: caml-list

On Mon, Oct 29, 2007 at 06:33:11PM -0500, Robert Fischer wrote:
> What is the preferred way to split a list into two, at an arbitrary point?  
> There's lots of ways you could do it, but I'm not sure if there's a 
> standard best practice for this.

Two answers for what they're worth:

# let split l f =
	let rec aux =
		function
		| h::t as l ->
			if f h then
			let a,b = aux t in h::a,b
			else [],l
		| [] -> [],[]
	in aux l;;
val split : 'a list -> ('a -> bool) -> 'a list * 'a list = <fun>
# split [1;2;3;4;5;6] ((>=) 4);;
- : int list * int list = ([1; 2; 3; 4], [5; 6])

(* may trash the given list but tail-recursive *)
# let magic_split l f =
	let rec aux p =
		function
		| [] -> l,[]
		| h::t as b ->
			if f h then aux (Some b) t
			else match p with None -> [],l
			| Some p -> (Obj.magic p).(1) <- None; l,b
	in aux None l;;
val magic_split : 'a list -> ('a -> bool) -> 'a list * 'a list = <fun>
# let l = [1;2;3;4;5;6];;
val l : int list = [1; 2; 3; 4; 5; 6]
# magic_split l ((>=) 4);;
- : int list * int list = ([1; 2; 3; 4], [5; 6])
# l;;
- : int list = [1; 2; 3; 4]

HTH,
  Julien.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-30  1:20 ` Julien Moutinho
@ 2007-10-30  1:38   ` Karl Zilles
  2007-10-30  5:46   ` skaller
  1 sibling, 0 replies; 16+ messages in thread
From: Karl Zilles @ 2007-10-30  1:38 UTC (permalink / raw)
  To: Julien Moutinho; +Cc: caml-list

Julien Moutinho wrote:
 > (* may trash the given list but tail-recursive *)
 > # let magic_split l f =
 >     let rec aux p =
 >         function
 >         | [] -> l,[]
 >         | h::t as b ->
 >             if f h then aux (Some b) t
 >             else match p with None -> [],l
 >             | Some p -> (Obj.magic p).(1) <- None; l,b
 >     in aux None l;;
 > val magic_split : 'a list -> ('a -> bool) -> 'a list * 'a list = <fun>
 > # let l = [1;2;3;4;5;6];;
 > val l : int list = [1; 2; 3; 4; 5; 6]
 > # magic_split l ((>=) 4);;
 > - : int list * int list = ([1; 2; 3; 4], [5; 6])
 > # l;;
 > - : int list = [1; 2; 3; 4]

Wow, this looks like a horrible idea!  You're modifying a non-mutable 
data structure and using object magic.  This leads to madness:

# let test () = magic_split [1; 2; 3; 4; 5; 6] ((>=) 4);;
val test : unit -> int list * int list = <fun>
# test ();;
- : int list * int list = ([1; 2; 3; 4], [5; 6])
# test ();;
- : int list * int list = ([1; 2; 3; 4], [])

I'm sure you're aware of this, but to suggest this as an answer to his 
question...


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-30  1:20 ` Julien Moutinho
  2007-10-30  1:38   ` Karl Zilles
@ 2007-10-30  5:46   ` skaller
  2007-10-30  7:58     ` Alain Frisch
  1 sibling, 1 reply; 16+ messages in thread
From: skaller @ 2007-10-30  5:46 UTC (permalink / raw)
  To: Julien Moutinho; +Cc: caml-list


On Tue, 2007-10-30 at 02:20 +0100, Julien Moutinho wrote:
> On Mon, Oct 29, 2007 at 06:33:11PM -0500, Robert Fischer wrote:
> > What is the preferred way to split a list into two, at an arbitrary point?  
> > There's lots of ways you could do it, but I'm not sure if there's a 
> > standard best practice for this.
> 
> Two answers for what they're worth:

Ouch. A simpler way is:

	let split ls n = let rec aux inp out n =
		if n = 0 then unsafe_rev_cat out inp
		else match inp with
		| [] -> unsafe_rev_cat out []
		| h :: t -> aux t (h::out) (n-1)
	in aux ls [] n

Here 'unsafe_rev_cat' uses magic to reverse a list in place
and tack the tail onto the end. 

However this isn't the fastest way. The fastest way is:

	let split ls n = 
		let out = magic_list() in
		let rec aux inp n =
			if n = 0 then magic_cat out inp
			else match inp with
			| [] -> ()
			| h :: t -> magic_append h out; aux t (n-1)
	in 
		aux ls n;
		list_of out

Here the magic list is the usual list data structure PLUS a pointer
to the last element allowing O(1) append. This is of course a mutable
data structure. All the operations here can be implemented without
Obj.magic, however it's much faster WITH Obj.magic: by using a 
magic list structure identical to an actual list, the 'list_of'
function becomes a typecast.

A similar code uses a reversed ordinary list for the temporary,
and then reverses it inplace (safe because it is a temporary)
and tacks the tail on. Felix uses that algorithm in its standard
library, however the one actually coded above is optimal (it isn't
possible to do it any faster).

Note the function above is purely functional (mutations are
isolated to the implementation) and does not itself use any
unsafe features (but the magic implementation is unsafe,
so it should be put in a library to limit bugs and 
implementation dependencies).

-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-29 23:33 Preferred Way to Split a List Robert Fischer
  2007-10-30  0:45 ` [Caml-list] " Matthew William Cox
  2007-10-30  1:20 ` Julien Moutinho
@ 2007-10-30  7:50 ` Jon Harrop
  2007-10-30 13:20   ` Robert Fischer
  2 siblings, 1 reply; 16+ messages in thread
From: Jon Harrop @ 2007-10-30  7:50 UTC (permalink / raw)
  To: caml-list

On Monday 29 October 2007 23:33, Robert Fischer wrote:
> What is the preferred way to split a list into two, at an arbitrary
> point?  There's lots of ways you could do it, but I'm not sure if
> there's a standard best practice for this.

I tend to write a combinator that nests an arbitrary number of function 
applications:

# let rec nest n f x =
    if n=0 then x else nest (n-1) f (f x);;
val nest : int -> ('a -> 'a) -> 'a -> 'a = <fun>

and then apply this to a function that moves head elements:

# let aux = function
    | front, h::back -> h::front, back
    | _ -> invalid_arg "aux";;
val aux : 'a list * 'a list -> 'a list * 'a list = <fun>

Then I write a "chop" function in terms of those two:

# let chop n list =
    nest n aux ([], list);;
val chop : int -> 'a list -> 'a list * 'a list = <fun>

For example, splitting after the fourth element:

# chop 4 [1;2;3;4;5;6;7;8;9];;
- : int list * int list = ([4; 3; 2; 1], [5; 6; 7; 8; 9])

Note that the front list is reversed.

PS: Ignore any responses that even mention Obj.
-- 
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/products/?e


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-30  5:46   ` skaller
@ 2007-10-30  7:58     ` Alain Frisch
  2007-10-29  9:34       ` Christophe Raffalli
  2007-10-30 11:15       ` skaller
  0 siblings, 2 replies; 16+ messages in thread
From: Alain Frisch @ 2007-10-30  7:58 UTC (permalink / raw)
  To: skaller; +Cc: Julien Moutinho, caml-list

skaller wrote:
> The fastest way is:

How can you be so sure? Do you have a proof or some intuitive argument?
Or maybe you tried all the possible other implementations? The following 
should not be that much slower (I did not try it, though, and of course, 
it is not tail-rec):

let split l n =
   let l2 = ref [] in
   let rec aux l n =
     if n = 0 then (l2 := l; [])
     else match l with
       | [] -> []
       | hd :: tl -> hd :: (aux tl (n - 1))
   in
   let l1 = aux l n in
   (l1, !l2)


Alain


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-30  7:58     ` Alain Frisch
  2007-10-29  9:34       ` Christophe Raffalli
@ 2007-10-30 11:15       ` skaller
  2007-10-30 13:05         ` Brian Hurt
  1 sibling, 1 reply; 16+ messages in thread
From: skaller @ 2007-10-30 11:15 UTC (permalink / raw)
  To: Alain Frisch; +Cc: Julien Moutinho, caml-list


On Tue, 2007-10-30 at 08:58 +0100, Alain Frisch wrote:
> skaller wrote:
> > The fastest way is:
> 
> How can you be so sure?

Why do I need to be something naturally impossible?
Certainty isn't even possible in the presence of a proof.

The fact here is I made a complete mess!

My algorithm doesn't meet the specification. LOL!
It doesn't split a list in two dang it! It actually
splits it in two then recombines the result and
returns the original list! :)


-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-29  9:34       ` Christophe Raffalli
@ 2007-10-30 11:31         ` skaller
  2007-10-30 12:30         ` David Allsopp
  1 sibling, 0 replies; 16+ messages in thread
From: skaller @ 2007-10-30 11:31 UTC (permalink / raw)
  To: Christophe Raffalli; +Cc: Alain Frisch, caml-list


On Mon, 2007-10-29 at 10:34 +0100, Christophe Raffalli wrote:
> Or
> 
> let split l n =
>   let rec aux acc l n =
>      if n = 0 then List.rev acc, l
>      else match l with
>         [] -> List.rev acc, []
>      | hd::l -> aux (hd::acc) l (n-1)
>   in aux [] l n
> 
> This is tail recursive, it does not use magic nor  references
> and keed the original ordering of the list.

Yes, but it double handles the head needlessly. Ignoring
the interesting observation you make below, this would
be expected to transfer head memory twice into the cache,
when once would suffice.

> Finally, I did in the past some testing  comparing a tail-rec append or map
> using magic and the composition of rev and rev-append or rev-map ...
> There was no noticable difference, because the extra cost  of
> mutability (regarding the managment of the grey val list) compensate
> the extra call to reverse.

However, you raise here an important issue not covered well anywhere:
when you have a garbage collection, the effect of some operation
should include not just the local algorithmic considerations
but also the cost of the garbage collection.

In the equivalent Felix code, there is no issue with write
barriers because Felix collector doesn't use them, so mutations
don't incur any cost. OTOH allocations are expensive.

Now, back to collectors .. the append is a special.
The mutations in the append are not mutations, but deferred
initialisations, that is, the write operation is guaranteed
to be replacing a NULL pointer.

Overwriting of NULL pointers is already possible in functional
coding without it implying mutation.

So one might imagine a collector with a minor and major heap,
in which the major heap requires special handling for mutations.
However the rule for moving an object out of the minor heap
is that the object must be fully initialised. (all pointers
in the block are non-null). 

In that design (I have no idea if it is viable!) the performance
might be different for the 'append' and other mutations.

The bottom line is that a good point is made that the 
usual fine detail performance analysis doesn't apply to
a garbage collected language.. thanks for pointing this out!


-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [Caml-list] Preferred Way to Split a List
  2007-10-29  9:34       ` Christophe Raffalli
  2007-10-30 11:31         ` skaller
@ 2007-10-30 12:30         ` David Allsopp
  2007-10-30 15:03           ` Christophe Raffalli
  1 sibling, 1 reply; 16+ messages in thread
From: David Allsopp @ 2007-10-30 12:30 UTC (permalink / raw)
  To: caml-list

Christophe Raffalli wrote:
> let split l n =
>   let rec aux acc l n =
>      if n = 0 then List.rev acc, l
>      else match l with
>         [] -> List.rev acc, []
>      | hd::l -> aux (hd::acc) l (n-1)
>   in aux [] l n

I'd go one stage further and use Obj.magic to eliminate the List.rev calls
(cf. ExtList.List in ExtLib). I'm sensibly wary of using Obj.magic in
general but find it acceptable here because its use is hidden within the
function (you are constructing a new list anyway, you just use Obj.magic to
allow tail-consing instead) - and there is no risk of "entertaining" side
effects (cf. John's earlier email!). I've got tail-consing wrapped in a few
extra functions in List so that this would become:

let split l n =
  let acc = List.tlempty ()
  in
    let rec aux l n =
       if n = 0 then (List.tlresult acc, l)
       else match l with
              []    -> (List.tlresult acc, [])
            | hd::l -> List.tlcons hd acc; aux l (n-1)
    in
      aux l n


David


PS List.tlempty:        returns a new, empty tail-consing list
   List.tlcons x tlist: adds x to the end of the given tlist and returns
                        the same tlist pointer
   List.tlresult tlist: casts an 'a tlist to an 'a list - and marks the
                        tlist as "exported", preventing future tail-consing
                        (which would violate the semantics of O'Caml 'a
                        lists)


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-30 11:15       ` skaller
@ 2007-10-30 13:05         ` Brian Hurt
  0 siblings, 0 replies; 16+ messages in thread
From: Brian Hurt @ 2007-10-30 13:05 UTC (permalink / raw)
  To: skaller; +Cc: Alain Frisch, caml-list

[-- Attachment #1: Type: text/plain, Size: 1543 bytes --]

skaller wrote:

>On Tue, 2007-10-30 at 08:58 +0100, Alain Frisch wrote:
>  
>
>>skaller wrote:
>>    
>>
>>>The fastest way is:
>>>      
>>>
>>How can you be so sure?
>>    
>>
>
>Why do I need to be something naturally impossible?
>Certainty isn't even possible in the presence of a proof.
>
>The fact here is I made a complete mess!
>
>My algorithm doesn't meet the specification. LOL!
>It doesn't split a list in two dang it! It actually
>splits it in two then recombines the result and
>returns the original list! :)
>
>
>  
>
Just so people know, Obj.magic is not necessary here:

let take n lst =
   let rec loop accum n lst =
      if n == 0 then
         List.rev accum
      else match lst with
         | x :: xs -> loop (x :: accum) (n - 1) xs
         | [] -> List.rev accum
   in
   if n < 0 then
      invalid_arg "Negative argument to List.take"
   else
      loop [] n lst
;;

let rec drop n lst =
   if n < 0 then
      invalid_arg "Negative argument to List.drop"
   else if n == 0 then
      lst
   else match lst with
      | _ :: xs -> drop (n - 1) xs
      | [] -> []
;;

I'd also take a look at the standard library functions List.filter and 
List.partition.  With their standard, Obj.magic-less, implementations.

If the list is long enough that the extra overhead of calling List.rev 
is a problem, I'd recommend using a better data structure than a list.  
I'm fond of tree-based "functional arrays", which allow for splitting 
them in O(log N), instead of O(N).  We're clock cycle tuning bubble sort 
here.

Brian


[-- Attachment #2: Type: text/html, Size: 2603 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-30  0:45 ` [Caml-list] " Matthew William Cox
@ 2007-10-30 13:18   ` Robert Fischer
  2007-11-03  3:06     ` Nathaniel Gray
  0 siblings, 1 reply; 16+ messages in thread
From: Robert Fischer @ 2007-10-30 13:18 UTC (permalink / raw)
  To: caml-list


> Do you need to preserve ordering in the sublists? 
>   
Yes.
> Do they need to be roughly equivalent lengths?
No -- the head list will probably be shorter than the tail list.

~~ Robert.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-30  7:50 ` Jon Harrop
@ 2007-10-30 13:20   ` Robert Fischer
  0 siblings, 0 replies; 16+ messages in thread
From: Robert Fischer @ 2007-10-30 13:20 UTC (permalink / raw)
  To: caml-list

[-- Attachment #1: Type: text/html, Size: 1591 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-30 12:30         ` David Allsopp
@ 2007-10-30 15:03           ` Christophe Raffalli
  0 siblings, 0 replies; 16+ messages in thread
From: Christophe Raffalli @ 2007-10-30 15:03 UTC (permalink / raw)
  To: David Allsopp; +Cc: caml-list

[-- Attachment #1: Type: text/plain, Size: 3859 bytes --]

David Allsopp a écrit :
> Christophe Raffalli wrote:
>> let split l n =
>>   let rec aux acc l n =
>>      if n = 0 then List.rev acc, l
>>      else match l with
>>         [] -> List.rev acc, []
>>      | hd::l -> aux (hd::acc) l (n-1)
>>   in aux [] l n
> 
> let split l n =
>   let acc = List.tlempty ()
>   in
>     let rec aux l n =
>        if n = 0 then (List.tlresult acc, l)
>        else match l with
>               []    -> (List.tlresult acc, [])
>             | hd::l -> List.tlcons hd acc; aux l (n-1)
>     in
>       aux l n
> 
> 

My point in my second last post was that in practice, for OCaml, the
second code is not much faster than the first ...

All this forced me to redo the testing : the details are bellow, but
list_split with rev wins for small list, the dirty thing with set_field
wins for big lists and the transition is between length 10000 and 100000.
Overall the difference is small.

Remark: the result might differ a lot for map if the mapped function does allocate memory ...
then there should almost no difference ...

So I would advice not to use Obj.set_field in this case ...

(* module for lists that can be constructed by both end *)
module Tlist = struct
  type 'a tlist = 'a list * 'a list

  (* a fake value for the tail of a list *)
  let rec dummy_tlist = Obj.magic 0::dummy_tlist

  let empty = [], []

  let tailcons tl a =
    match tl with
      [], [] -> let tl = a :: dummy_tlist in (tl, tl)
    | (_::_ as s), (_::dl as e) when dl == dummy_tlist ->
	let e' = a::dummy_tlist in
	Obj.set_field (Obj.repr e) 1 (Obj.repr e');
	s, e'
    | _ -> raise (Invalid_argument "Tlist.tail_cons")

  let cons a tl =
    match tl with
      [], [] -> let tl = a :: dummy_tlist in (tl, tl)
    | (_::_ as s), (_::dl as e) when dl ==  dummy_tlist ->
	a::s, e
    | _ -> raise (Invalid_argument "Tlist.cons")

  (* this must be the final call to transform a 'a tlist into a list *)
  let append tl l =
    match tl with
      [], [] -> l
    | (_::_ as s), (_::dl as e) when dl == dummy_tlist ->
	Obj.set_field (Obj.repr e) 1 (Obj.repr l);
	s
    | _ -> raise (Invalid_argument "Tlist.append")
end

(* the two versions of split *)
let split_with_rev l n =
   let rec aux acc l n =
      if n = 0 then List.rev acc, l
      else match l with
         [] -> List.rev acc, []
      | hd::l -> aux (hd::acc) l (n-1)
   in aux [] l n

let split_with_tlist l n =
  let rec aux acc l n =
    if n = 0 then (Tlist.append acc [], l)
    else match l with
      [] -> Tlist.append acc [], []
    | hd::l -> aux (Tlist.tailcons acc hd) l (n-1)
  in
  aux Tlist.empty l n

(* code for testing *)
let rec build_list n =
  let rec aux acc n =
    if n = 0 then acc else aux (n::acc) (n-1)
  in
  aux [] n

let test_tlist n p =
  for i = 1 to n do
    ignore (split_with_tlist (build_list (2*p)) p)
  done

let test_rev n p =
  for i = 1 to n do
    ignore (split_with_rev (build_list (2*p)) p)
  done

let _ = test_tlist 10 1000000

(*
result:

  test_tlist 10000000 10 : 3.1s
  test_rev 10000000 10 : 2.6s

  test_tlist 100000 1000 : 8.8s
  test_rev 100000 1000 : 6.9s

  test_tlist 100 100000 : 5.9s
  test_rev 100 100000 : 7.4s

  test_tlist 10 1000000 : 6.2s
  test_rev 10 1000000 : 7.9s
*)

-- 
Christophe Raffalli
Universite de Savoie
Batiment Le Chablais, bureau 21
73376 Le Bourget-du-Lac Cedex

tel: (33) 4 79 75 81 03
fax: (33) 4 79 75 87 42
mail: Christophe.Raffalli@univ-savoie.fr
www: http://www.lama.univ-savoie.fr/~RAFFALLI
---------------------------------------------
IMPORTANT: this mail is signed using PGP/MIME
At least Enigmail/Mozilla, mutt or evolution
can check this signature. The public key is
stored on www.keyserver.net
---------------------------------------------


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 252 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Caml-list] Preferred Way to Split a List
  2007-10-30 13:18   ` Robert Fischer
@ 2007-11-03  3:06     ` Nathaniel Gray
  0 siblings, 0 replies; 16+ messages in thread
From: Nathaniel Gray @ 2007-11-03  3:06 UTC (permalink / raw)
  To: Robert Fischer; +Cc: caml-list

On Oct 30, 2007 6:18 AM, Robert Fischer <robert@fischerventure.com> wrote:
>
> > Do you need to preserve ordering in the sublists?
> >
> Yes.
> > Do they need to be roughly equivalent lengths?
> No -- the head list will probably be shorter than the tail list.

Hmm...

let fast_list_splitter = function
   | h :: t -> [h], t
   | [] -> [], []  (* Or maybe raise an exception? *)

Or if you're really shooting for speed:

let golly_it's_fast_list_splitter lst = [], lst

I win.  Seriously, you must have *some* further constraints!

Cheers,
-n8

-- 
>>>-- Nathaniel Gray -- Caltech Computer Science ------>
>>>-- Mojave Project -- http://mojave.cs.caltech.edu -->


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2007-11-03  3:07 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-10-29 23:33 Preferred Way to Split a List Robert Fischer
2007-10-30  0:45 ` [Caml-list] " Matthew William Cox
2007-10-30 13:18   ` Robert Fischer
2007-11-03  3:06     ` Nathaniel Gray
2007-10-30  1:20 ` Julien Moutinho
2007-10-30  1:38   ` Karl Zilles
2007-10-30  5:46   ` skaller
2007-10-30  7:58     ` Alain Frisch
2007-10-29  9:34       ` Christophe Raffalli
2007-10-30 11:31         ` skaller
2007-10-30 12:30         ` David Allsopp
2007-10-30 15:03           ` Christophe Raffalli
2007-10-30 11:15       ` skaller
2007-10-30 13:05         ` Brian Hurt
2007-10-30  7:50 ` Jon Harrop
2007-10-30 13:20   ` Robert Fischer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).