Last active
October 29, 2017 06:56
-
-
Save t-katsushima/a1c2d2ad36865f17605aaadfb2565e5a to your computer and use it in GitHub Desktop.
VList の実装(コメント欄に説明付き)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module type VLIST = sig | |
type 'a t | |
val nth: 'a t -> int -> 'a | |
val length: 'a t -> int | |
val cons: 'a -> 'a t -> 'a t | |
val head: 'a t -> 'a | |
val tail: 'a t -> 'a t | |
val vlist_of_list: 'a list -> 'a t | |
val list_of_vlist: 'a t -> 'a list | |
end | |
module VList: VLIST = struct | |
(* memory block *) | |
type 'a mb = { mutable base: 'a mb option; offset: int; | |
size: int; mutable last_used: int; | |
mBlock: 'a option array } | |
type 'a t = { mutable base: 'a mb; offset: int } | |
let ratio = 2 | |
let get: 'a option -> 'a = function | |
| Some x -> x | |
| None -> failwith "Tried to get from None" | |
let rec nth_sup: int -> 'a mb -> 'a = fun n mb -> | |
if n <= mb.offset then get (get mb.base).mBlock.(mb.offset - n) | |
else nth_sup (n - (mb.offset + 1)) (get mb.base) | |
let nth vl n = | |
if n >= 0 then | |
if n <= vl.offset then get vl.base.mBlock.(vl.offset - n) | |
else nth_sup (n - (vl.offset + 1)) vl.base | |
else | |
failwith "index-number mustn't be negative-number" | |
let rec length_sup: 'a mb -> int -> int = fun mb res -> | |
match mb.base with | |
| None -> res | |
| Some pre_mb -> length_sup pre_mb (res + (mb.offset + 1)) | |
let length vl = | |
let mb = vl.base in | |
match mb.base with | |
| None -> vl.offset + 1 | |
| Some _ -> length_sup mb (vl.offset + 1) | |
let cons x vl = | |
let mb = vl.base in | |
if vl.offset == mb.last_used | |
then (* simply add to next index case *) | |
if mb.last_used + 1 < mb.size | |
then | |
begin | |
mb.mBlock.(vl.offset + 1) <- Some x; | |
mb.last_used <- mb.last_used + 1; | |
{ base = vl.base; offset = vl.offset + 1 } | |
end | |
else | |
let next_size = mb.size * ratio in | |
let next_mb = { base = Some mb; offset = vl.offset; | |
size = next_size; last_used = 0; | |
mBlock = Array.make next_size None } in | |
next_mb.mBlock.(0) <- Some x; | |
{ base = next_mb; offset = 0 } | |
else (* update case *) | |
let next_mb = { base = Some mb; offset = vl.offset; | |
size = 1; last_used = 0; | |
mBlock = Array.make 1 None } in | |
begin | |
next_mb.mBlock.(0) <- Some x; | |
{ base = next_mb; offset = 0 } | |
end | |
let head vl = get vl.base.mBlock.(vl.offset) | |
let tail vl = | |
let mb = vl.base in | |
match vl.offset with | |
| 0 -> ( | |
match vl.base.base with | |
| Some pre_mb -> { base = pre_mb; offset = mb.offset } | |
| None -> { vl with offset = -1 }) | |
| i when i > 0 -> { vl with offset = vl.offset - 1 } | |
| _ -> failwith "This list is empty" | |
let vlist_of_list l = | |
let fst_mb = { base = None; offset = 0; | |
size = 1; last_used = -1; | |
mBlock = Array.make 1 None } in | |
let fst_pointer = { base = fst_mb; offset = -1 } in | |
let rec make res = function | |
| [] -> res | |
| hd :: tl -> | |
let mb = res.base in | |
if res.offset + 1 < mb.size then | |
begin | |
mb.mBlock.(res.offset + 1) <- Some hd; | |
mb.last_used <- mb.last_used + 1; | |
make { res with offset = res.offset + 1 } tl | |
end | |
else | |
let next_size = mb.size * ratio in | |
let next_mb = { base = Some mb; offset = res.offset; | |
size = next_size; last_used = 0; | |
mBlock = Array.make next_size None } in | |
begin | |
next_mb.mBlock.(0) <- Some hd; | |
make { base = next_mb; offset = 0 } tl | |
end | |
in make fst_pointer (List.rev l) | |
let list_of_vlist vl = | |
let rec make acc vl = | |
let mb = vl.base in | |
match (mb.base, vl.offset) with | |
| (None, -1) -> List.rev acc | |
| (None, _) -> List.rev @@ (head vl) :: acc | |
| _ -> make (head vl :: acc) @@ tail vl | |
in make [] vl | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
README
Overview
Fast Functional Lists, Hash-Lists, Deques and Variable Length Arrays, Phil Bagwell の VList の実装
Example
OCaml REPL にファイルを読み込み、
VList
をオープンします.vlist_of_list
を使うことで、リストから VList を生成できます.諸々コマンドを試してみましょう.
ベンチマークっぽいやつ
要素数 n の Linked List と VList それぞれに対して全要素へのアクセスを行い, 速度を比べました.
実際に動かしてみると違いが良く分かると思います.
まず要素数25,000の Linked List と VList を作ります.
Linked List の計測:
-- 結果:
3.896
秒VList の計測:
-- 結果:
0.004
秒Linked List が
O(n^2)
掛かるのに対して, VList はO(n)
で済むためはるかに高速です.