-
Notifications
You must be signed in to change notification settings - Fork 4
/
string_builder.ml
259 lines (216 loc) · 6.45 KB
/
string_builder.ml
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
module type S = sig
type t
val empty : t
val ( @ ) : t -> t -> t
val of_string : string -> t
val to_string : t -> string
end
module Build_string = struct
type t = string
let empty = ""
let ( @ ) a b = a ^ b
let of_string s = s
let to_string s = s
end
module type FOLD = sig
type 'a t
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
end
module Fold_to_string (F : FOLD) = struct
let to_string ss =
let n = F.fold_left (fun acc s -> acc + String.length s) 0 ss in
let buf = Bytes.create n in
let _ =
F.fold_left
(fun i s ->
let n = String.length s in
Bytes.blit_string s 0 buf i n ;
i + n)
0
ss
in
Bytes.to_string buf
end
module Build_list = struct
type t = string list
let empty = []
let ( @ ) a b = List.append a b
let of_string s = [s]
include Fold_to_string (List)
end
module Build_list_safe = struct
include Build_list
let ( @ ) a b = List.rev_append (List.rev a) b
end
module Build_dlist = struct
type t = string list -> string list
let empty = fun xs -> xs
let ( @ ) a b = fun xs -> a (b xs)
let of_string s = fun xs -> s :: xs
let to_string ss = Build_list.to_string (ss [])
end
module Build_tree = struct
module F = struct
type 'a t = Empty | Single of 'a | Concat of 'a t * 'a t
let rec fold_left f z = function
| Empty -> z
| Single s -> f z s
| Concat (a, b) -> fold_left f (fold_left f z a) b
end
type t = string F.t
let empty = F.Empty
let ( @ ) a b = match a, b with
| F.Empty, t | t, F.Empty -> t
| _ -> F.Concat (a, b)
let of_string s = F.Single s
include Fold_to_string (F)
end
module Build_deque = struct
type t = string Deque.t
let empty = Deque.empty
let ( @ ) a b = Deque.append a b
let of_string s = Deque.cons s Deque.empty
include Fold_to_string (Deque)
end
module Build_steque = struct
module Deque = Deque.Steque
type t = string Deque.t
let empty = Deque.empty
let ( @ ) a b = Deque.append a b
let of_string s = Deque.cons s Deque.empty
include Fold_to_string (Deque)
end
let bench name f =
let t0 = Unix.gettimeofday () in
let ok, str = try true, f () with e -> false, Printexc.to_string e in
let t1 = Unix.gettimeofday () in
Printf.printf "%16s: %.3f s -- %s\n%!"
name
(t1 -. t0)
(if ok
then "length " ^ string_of_int (String.length str)
else str) ;
if ok
then Some str
else None
module Test (Builder : S) = struct
open Builder
let popen = of_string " begin "
let pclose = of_string " end "
let parens x = popen @ x @ pclose
let parens_in =
bench "parens" @@ fun () ->
let rec go acc n =
if n = 0
then acc
else go (parens acc) (n - 1)
in
to_string (go empty 20000)
let () = Gc.full_major ()
let fibonacci =
bench "fibonacci" @@ fun () ->
let rec go a b n =
if n = 0
then a
else go (parens (a @ b)) (parens a) (n - 1)
in
to_string (go (of_string "1") (of_string "0") 32)
let () = Gc.full_major ()
end
let header name =
Printf.printf "-- %s %s\n%!"
name
(String.make (70 - String.length name - 4) '-')
let () = header "String"
module Test_string = Test (Build_string)
let () = Printf.printf "\n%!"
module Test_check (Builder : S) = struct
module T = Test (Builder)
let () =
assert (T.parens_in = Test_string.parens_in) ;
assert (T.fibonacci = None || T.fibonacci = Test_string.fibonacci)
end
let () = header "List"
module Test_list = Test_check (Build_list)
let () = Printf.printf "\n%!"
let () = header "List_safe (no Stack_overflow on append)"
module Test_list_safe = Test_check (Build_list_safe)
let () = Printf.printf "\n%!"
let () = header "Diff list"
module Test_dlist = Test_check (Build_dlist)
let () = Printf.printf "\n%!"
let () = header "Custom tree"
module Test_tree = Test_check (Build_tree)
let () = Printf.printf "\n%!"
let () = header "Deque"
module Test_deque = Test_check (Build_deque)
let () = Printf.printf "\n%!"
let () = header "Steque"
module Test_steque = Test_check (Build_steque)
let () = Printf.printf "\n%!"
let () = header "Buffer"
module Test_buffer = struct
let popen = " begin "
let pclose = " end "
let parens_in =
bench "parens" @@ fun () ->
let buf = Buffer.create 0 in
let rec go n =
if n = 0
then ()
else begin
Buffer.add_string buf popen ;
go (n - 1) ;
Buffer.add_string buf pclose
end
in
go 20000 ;
Buffer.contents buf
let () = Gc.full_major ()
let fibonacci =
bench "fibonacci" @@ fun () ->
let buf = Buffer.create 0 in
let rec go = function
| 0 -> Buffer.add_string buf "0"
| 1 -> Buffer.add_string buf "1"
| n ->
Buffer.add_string buf popen ;
go (n - 1) ;
if n > 2 then Buffer.add_string buf popen ;
go (n - 2) ;
if n > 2 then Buffer.add_string buf pclose ;
Buffer.add_string buf pclose
in
go (32 + 1) ;
Buffer.contents buf
let () = Gc.full_major ()
let () =
assert (parens_in = Test_string.parens_in) ;
assert (fibonacci = Test_string.fibonacci)
end
(* $ dune exec examples/string_builder.exe
-- String ------------------------------------------------------------
parens: 1.478 s -- length 240000
fibonacci: 0.430 s -- length 116432443
-- List --------------------------------------------------------------
parens: 7.131 s -- length 240000
fibonacci: 0.232 s -- Stack overflow
-- List_safe (no Stack_overflow on append) ---------------------------
parens: 7.856 s -- length 240000
fibonacci: 16.243 s -- length 116432443
-- Diff list ---------------------------------------------------------
parens: 0.003 s -- length 240000
fibonacci: 1.963 s -- length 116432443
-- Custom tree -------------------------------------------------------
parens: 0.001 s -- length 240000
fibonacci: 0.448 s -- length 116432443
-- Deque -------------------------------------------------------------
parens: 0.013 s -- length 240000
fibonacci: 0.581 s -- length 116432443
-- Steque ------------------------------------------------------------
parens: 0.006 s -- length 240000
fibonacci: 0.559 s -- length 116432443
-- Buffer ------------------------------------------------------------
parens: 0.001 s -- length 240000
fibonacci: 0.284 s -- length 116432443
*)