Code Snippet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
module BottomUpMergeSort =
type 'a Sortable =
{ less : 'a * 'a -> bool; size : int; segments : 'a list list Lazy}
// Don't use as it gives stack-overflow
let merge less xs ys =
let rec mrg = function
| [ ], zs | zs, [ ] -> zs
| x :: xs, y :: ys ->
if less (x,y) then
x :: mrg (xs, y :: ys)
else
y :: mrg (x :: xs, ys)
mrg (xs,ys)
// Use this instead as it is tail-recursive
let merge' less xs ys =
let rec append = function
| xs, [ ] -> xs
| xs, y :: ys -> append (y :: xs, ys)
let rec mrg acc = function
| [ ], zs | zs, [ ] -> append (zs,acc)
| x :: xs, y :: ys ->
if less (x,y) then
mrg (x :: acc) (xs, y :: ys)
else
mrg (y :: acc) (x :: xs, ys)
mrg [ ] (xs,ys)
let add x { less = less; size = n; segments = segs} =
let rec add' seg segs n =
if n % 2 = 0 then
lazy (seg :: segs)
else
add' (merge' less seg (List.head segs)) (List.tail segs) (n / 2)
{ less = less; size = n + 1; segments = add' [x] (segs.Force()) n; }
let sort { less = less; segments = segs} =
let rec sort' = function
| xs, [ ] -> xs
| xs, seg :: segs -> sort' (merge' less xs seg, segs)
sort' ([],segs.Force())
let sort' { less = less; segments = segs} =
let rec foldl = function
| f, c, [ ] -> c
| f, c, x :: xs -> foldl(f,f c x,xs)
foldl(merge' less,[ ],segs.Force())
module ScheduledBottomUpMergeSort =
type 'a Stream = Cons of 'a * 'a Stream Lazy | Nil
type 'a Schedule = 'a Stream list
type 'a Sortable =
{ less : 'a * 'a -> bool;
size : int;
segments : ('a Stream * 'a Schedule) list }
let merge less xs ys =
let rec mrg = function
| Nil,zs | zs, Nil -> zs
| (Cons(x,xs) as xs'), (Cons(y,ys) as ys') ->
if less (x,y) then
Cons(x, lazy mrg (xs.Force(),ys'))
else
Cons(y, lazy mrg (xs',ys.Force()))
mrg (xs,ys)
let rec exec1 = function
| [ ] -> [ ]
| Nil :: sched -> exec1 sched
| Cons(x,xs) :: sched -> (xs.Force()) :: sched
// Don't use as it gives stack-overflow
let rec exec2PerSeg = function
| [ ] -> [ ]
| (xs,sched) :: segs -> (xs, exec1 (exec1 sched)) :: exec2PerSeg segs
// Use this instead as it is tail-recursive
let rec exec2PerSeg' acc = function
| [ ] -> acc
| (xs,sched) :: segs -> exec2PerSeg' ((xs, exec1 (exec1 sched)) :: acc) segs
let add x { less = less; size = n; segments = segs} =
let rec add' xs segs n rsched =
if n % 2 = 0 then
(xs,xs :: rsched |> List.rev) :: segs
else
match segs with
| [ ] -> [ ]
| (xs',_) :: segs' ->
add' (merge less xs xs') segs' (n / 2) (xs :: rsched)
let segs' = add' (Cons(x,lazy Nil)) segs n [ ]
{ less = less; size = n + 1; segments = exec2PerSeg' [ ] segs'; }
let sort { less = less; segments = segs } =
let rec sort' = function
| xs, [ ] -> xs
| xs, (xs',_) :: segs -> sort' (merge less xs xs', segs)
// Don't use as it gives stack-overflow
let rec stream2list = function
| Nil -> [ ]
| Cons(x,xs) -> x :: stream2list (xs.Force())
// Use this instead as it is tail-recursive
let rec stream2list' acc = function
| Nil -> acc |> List.rev
| Cons(x,xs) -> stream2list' (x :: acc) (xs.Force())
(Nil,segs) |> sort' |> stream2list' [ ]
module List =
type 'a S = 'a BottomUpMergeSort.Sortable
type 'a S' = 'a ScheduledBottomUpMergeSort.Sortable
let puresort xs =
({ S.less = (fun (x,y) -> x < y); S.size = 0; S.segments = lazy [] },xs)
||> List.fold(fun a x -> a |> BottomUpMergeSort.add x)
|> BottomUpMergeSort.sort
let puresort' xs =
({ S.less = (fun (x,y) -> x < y); S.size = 0; S.segments = lazy [] },xs)
||> List.fold(fun a x -> a |> BottomUpMergeSort.add x)
|> BottomUpMergeSort.sort'
let puresort'' xs =
({ S'.less = (fun (x,y) -> x < y); S'.size = 0; S'.segments = [] },xs)
||> List.fold(fun a x -> a |> ScheduledBottomUpMergeSort.add x)
|> ScheduledBottomUpMergeSort.sort
Code output:
>
module BottomUpMergeSort = begin
type 'a Sortable =
{less: 'a * 'a -> bool;
size: int;
segments: Lazy<'a list list>;}
val merge : less:('a * 'a -> bool) -> xs:'a list -> ys:'a list -> 'a list
val merge' : less:('a * 'a -> bool) -> xs:'a list -> ys:'a list -> 'a list
val add : x:'a -> 'a Sortable -> 'a Sortable
val sort : 'a Sortable -> 'a list
val sort' : 'a Sortable -> 'a list
end
module ScheduledBottomUpMergeSort = begin
type 'a Stream =
| Cons of 'a * Lazy<'a Stream>
| Nil
type 'a Schedule = 'a Stream list
type 'a Sortable =
{less: 'a * 'a -> bool;
size: int;
segments: ('a Stream * 'a Schedule) list;}
val merge :
less:('a * 'a -> bool) -> xs:'a Stream -> ys:'a Stream -> 'a Stream
val exec1 : _arg1:'a Stream list -> 'a Stream list
val exec2PerSeg :
_arg1:('a * 'b Stream list) list -> ('a * 'b Stream list) list
val exec2PerSeg' :
acc:('a * 'b Stream list) list ->
_arg1:('a * 'b Stream list) list -> ('a * 'b Stream list) list
val add : x:'a -> 'a Sortable -> 'a Sortable
val sort : 'a Sortable -> 'a list
end
module List = begin
type 'a S = 'a BottomUpMergeSort.Sortable
type 'a S' = 'a ScheduledBottomUpMergeSort.Sortable
val puresort : xs:'a list -> 'a list when 'a : comparison
val puresort' : xs:'a list -> 'a list when 'a : comparison
val puresort'' : xs:'a list -> 'a list when 'a : comparison
end
Verification Code Snippet:
1
2
3
4
5
6
7
8
9
10
11
[100*1000 .. -1 .. 1]
|> List.puresort
|> List.fold(fun acc x -> (x >= snd acc) && (fst acc),x) (true,0)
[100*1000 .. -1 .. 1]
|> List.puresort'
|> List.fold(fun acc x -> (x >= snd acc) && (fst acc),x) (true,0)
[100*1000 .. -1 .. 1]
|> List.puresort''
|> List.fold(fun acc x -> (x >= snd acc) && (fst acc),x) (true,0)
Verification Code output:
> val it : bool * int = (true, 100000)
> val it : bool * int = (true, 100000)
> val it : bool * int = (true, 100000)
Performance Code Snippet:
1
2
3
4
5
6
7
8
9
10
#time
[1000*1000 .. -1 .. 1]
|> List.puresort
[1000*1000 .. -1 .. 1]
|> List.puresort'
[1000*1000 .. -1 .. 1]
|> List.puresort''
Performance Code output:
--> Timing now on
> Real: 00:00:12.407, CPU: 00:00:12.723, GC gen0: 265, gen1: 13
val it : int list =
[1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21;
22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39; 40;
41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57; 58; 59;
60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75; 76; 77; 78;
79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93; 94; 95; 96; 97;
98; 99; 100; ...]
> Real: 00:00:12.069, CPU: 00:00:12.335, GC gen0: 269, gen1: 10
val it : int list =
[1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21;
22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39; 40;
41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57; 58; 59;
60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75; 76; 77; 78;
79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93; 94; 95; 96; 97;
98; 99; 100; ...]
> Real: 00:01:10.638, CPU: 00:01:12.356, GC gen0: 428, gen1: 16
val it : int list =
[1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21;
22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39; 40;
41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57; 58; 59;
60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75; 76; 77; 78;
79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93; 94; 95; 96; 97;
98; 99; 100; ...]
References:
- Okasaki thesis: Purely Functional Data Structures