From 2498ff42a1fe78ff8130e4242203f4d673e5bc2f Mon Sep 17 00:00:00 2001 From: Gregory Burd Date: Mon, 25 Feb 2013 10:46:48 -0500 Subject: [PATCH] A few fixes here and there. --- README | 19 ++++++++++--------- src/hamt.erl | 34 ++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/README b/README index 39373a2..a0ce058 100644 --- a/README +++ b/README @@ -1,15 +1,16 @@ Ideal Hash Array Mapped Tries: an Erlang functional datatype -The Hash Array Mapped Trie (HAMT) is based on the simple notion of hashing a -key and storing the key in a trie based on this hash value. The AMT is used to -implement the required structure e#ciently. The Array Mapped Trie (AMT) is a -versatile data structure and yields attractive alternative to contemporary -algorithms in many applications. Here I describe how it is used to develop Hash -Trees with near ideal characteristics that avoid the traditional problem, -setting the size of the initial root hash table or incurring the high cost of -dynamic resizing to achieve an acceptable performance. +"The Hash Array Mapped Trie (HAMT) is based on the simple notion of hashing +a key and storing the key in a trie based on this hash value. The AMT is +used to implement the required structure efficiently. The Array Mapped Trie +(AMT) is a versatile data structure and yields attractive alternative to +contemporary algo- rithms in many applications. Here I describe how it is +used to develop Hash Trees with near ideal characteristics that avoid the +traditional problem, setting the size of the initial root hash table or +incurring the high cost of dynamic resizing to achieve an acceptable +performance." -Based on the paper "Ideal Hash Tries" by Phil Bagwell [2000]. +The quote above is from the paper "Ideal Hash Tries" by Phil Bagwell [2000]: @ARTICLE{Bagwell01idealhash, author = {Phil Bagwell}, title = {Ideal Hash Trees}, diff --git a/src/hamt.erl b/src/hamt.erl index e2d7d94..eaeb91e 100644 --- a/src/hamt.erl +++ b/src/hamt.erl @@ -53,7 +53,7 @@ %% - is_empty(T): returns 'true' if T is an empty hamt, and 'false' %% otherwise. %% -%% - get(K, T): retreives the value stored with key K in hamt T or +%% - get(K, T): retrieves the value stored with key K in hamt T or %% `not_found' if the key is not present in the hamt. %% %% - put(K, V, T): inserts key K with value V into hamt T; if the key @@ -79,8 +79,8 @@ -ifdef(TEST). -ifdef(EQC). -%include_lib("eqc/include/eqc.hrl"). -%include_lib("eqc/include/eqc_fsm.hrl"). +-include_lib("eqc/include/eqc.hrl"). +-include_lib("eqc/include/eqc_fsm.hrl"). -endif. -compile(export_all). -include_lib("eunit/include/eunit.hrl"). @@ -92,32 +92,34 @@ from_list/1, from_list/2, to_list/1]). %% The Hamt data structure consists of: -%% - {hamt, nil | {SNode, CNode, LNode} -%% - {hamt, nil | {hamt, {SNode, CNode, LNode}} +%% - {hamt, nil | {SNode, CNode, LNode}} %% - {snode, Key::binary(), Value::binary()} %% - {cnode, Bitmap, Branch} %% - {lnode, [snode]} -export_type([hamt/0, hamt_hash_fn/0]). +-type hamt_hashsize() :: 32 | 64 | 128 | 160. -type hamt_hash_fn() :: {non_neg_integer(), fun((any()) -> binary())}. -type hamt_snode() :: {snode, any(), any()}. -type hamt_lnode() :: {lnode, [hamt_snode()]}. -type hamt_cnode() :: {cnode, non_neg_integer(), [hamt_snode() | hamt_cnode() | hamt_lnode()]}. --opaque hamt() :: {hamt, nil | hamt_hash_fn(), nil | hamt_cnode()}. +-opaque hamt() :: {hamt, hamt_hashsize(), nil} | + {hamt, hamt_hashsize(), hamt_hash_fn(), nil} | + {hamt, hamt_hashsize(), hamt_cnode()}. -%% @doc Returns a new, empty trie that uses phash2 to generate -%% 32bit hash codes for keys. +%% @doc Returns a new, empty trie that uses +%% 32-bit hash codes for keys. -spec new() -> hamt(). new() -> {hamt, 32, nil}. %% @doc Returns a new, empty trie that uses the specified %% number of bits when hashing keys. --spec new(32|64|128|160) -> hamt(). +-spec new(hamt_hashsize()) -> hamt(). new(HashSize) -> {hamt, HashSize, nil}. --spec hash(32|64|128|160, any()) -> non_neg_integer(). +-spec hash(hamt_hashsize(), any()) -> non_neg_integer(). hash(HashSize, X) when not is_binary(X) -> hash(HashSize, term_to_binary(X)); @@ -191,7 +193,7 @@ get_2(Key, [{_DifferentKey, _Value} | Rest]) -> from_list(List) -> put(List, hamt:new()). --spec from_list([{any(), any()}], 32|64|128|160) -> hamt(). +-spec from_list([{any(), any()}], hamt_hashsize()) -> hamt(). from_list(List, HashSize) -> put(List, hamt:new(HashSize)). @@ -299,9 +301,9 @@ delete_1(_Hash, Key, {lnode, List}, _L, _M) -> -spec map(Function, Hamt1) -> Hamt2 when % TODO Function :: fun((K :: term(), V1 :: term()) -> V2 :: term()), Hamt1 :: hamt(), Hamt2 :: hamt(). -map(F, {hamt, HashSize, _}=T) +map(F, {hamt, HashSize, Node}) when is_function(F, 2) -> - {hamt, HashSize, map_1(F, T)}. + {hamt, HashSize, map_1(F, Node)}. map_1(_, nil) -> nil; map_1(F, {K, V, Smaller, Larger}) -> @@ -311,9 +313,9 @@ map_1(F, {K, V, Smaller, Larger}) -> %% extra argument Acc (short for accumulator). Fun must return a new %% accumulator which is passed to the next call. Acc0 is returned if %% the list is empty. The evaluation order is undefined. --spec fold(Fun, Hamt, Acc) -> Hamt when - Fun :: fun((K :: term(), V :: term(), Acc :: any()) -> Acc2 :: any()), - Hamt :: hamt(), Acc :: any(). +-spec fold(Fun, Hamt, Acc1) -> Acc2 when + Fun :: fun((K :: term(), V :: term(), Acc0 :: any()) -> Acc :: any()), + Hamt :: hamt(), Acc1 :: any(), Acc2 :: any(). fold(Fun, {hamt, _, Node}, Acc) -> fold_1(Fun, Acc, Node).