318 lines
8.2 KiB
Prolog
318 lines
8.2 KiB
Prolog
%% -*- Prolog -*-
|
|
|
|
/*
|
|
|
|
@defgroup YapHash Backtrackable Hash Tables
|
|
@ingroup YapLibrary
|
|
@{
|
|
|
|
This code implements hash-arrays.
|
|
It requires the hash key to be a ground term.
|
|
|
|
It relies on dynamic array code.
|
|
|
|
*/
|
|
:- source.
|
|
:- yap_flag(unknown,error).
|
|
:- style_check(all).
|
|
|
|
:- module(b_hash, [ b_hash_new/1,
|
|
b_hash_new/2,
|
|
b_hash_new/4,
|
|
b_hash_lookup/3,
|
|
b_hash_update/3,
|
|
b_hash_update/4,
|
|
b_hash_insert_new/4,
|
|
b_hash_insert/4,
|
|
b_hash_size/2,
|
|
b_hash_code/2,
|
|
is_b_hash/1,
|
|
b_hash_to_list/2,
|
|
b_hash_values_to_list/2,
|
|
b_hash_keys_to_list/2
|
|
]).
|
|
|
|
:- use_module(library(terms), [ term_hash/4 ]).
|
|
|
|
|
|
:- meta_predicate(b_hash_new(-,+,3,2)).
|
|
|
|
array_default_size(2048).
|
|
|
|
/** @pred is_b_hash( +Hash )
|
|
|
|
Term _Hash_ is a hash table.
|
|
*/
|
|
is_b_hash(V) :- var(V), !, fail.
|
|
is_b_hash(hash(_,_,_,_,_)).
|
|
|
|
/** @pred b_hash_new( -NewHash )
|
|
|
|
Create a empty hash table _NewHash_, with size 2048 entries.
|
|
*/
|
|
b_hash_new(hash(Keys, Vals, Size, N, _, _)) :-
|
|
array_default_size(Size),
|
|
array(Keys, Size),
|
|
array(Vals, Size),
|
|
create_mutable(0, N).
|
|
|
|
/** @pred b_hash_new( -_NewHash_, +_Size_ )
|
|
|
|
Create a empty hash table, with size _Size_ entries.
|
|
*/
|
|
b_hash_new(hash(Keys, Vals, Size, N, _, _), Size) :-
|
|
array(Keys, Size),
|
|
array(Vals, Size),
|
|
create_mutable(0, N).
|
|
|
|
/** @pred b_hash_new( -_NewHash_, +_Size_, :_Hash_, :_Cmp_ )
|
|
|
|
Create a empty hash table, with size _Size_ entries.
|
|
_Hash_ defines a partition function, and _Cmp_ defined a comparison function.
|
|
*/
|
|
b_hash_new(hash(Keys,Vals, Size, N, HashF, CmpF), Size, HashF, CmpF) :-
|
|
array(Keys, Size),
|
|
array(Vals, Size),
|
|
create_mutable(0, N).
|
|
|
|
/**
|
|
@pred b_hash_size( +_Hash_, -_Size_ )
|
|
|
|
_Size_ unifies with the size of the hash table _Hash_.
|
|
*/
|
|
b_hash_size(hash(_, _, Size, _, _, _), Size).
|
|
|
|
/**
|
|
@pred b_hash_lookup( +_Key_, ?_Val_, +_Hash_ )
|
|
|
|
Search the ground term _Key_ in table _Hash_ and unify _Val_ with the associated entry.
|
|
*/
|
|
b_hash_lookup(Key, Val, hash(Keys, Vals, Size, _, F, CmpF)):-
|
|
hash_f(Key, Size, Index, F),
|
|
fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex),
|
|
array_element(Vals, ActualIndex, Mutable),
|
|
get_mutable(Val, Mutable).
|
|
|
|
fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex) :-
|
|
array_element(Keys, Index, El),
|
|
nonvar(El),
|
|
(
|
|
cmp_f(CmpF, El, Key)
|
|
->
|
|
Index = ActualIndex
|
|
;
|
|
I1 is (Index+1) mod Size,
|
|
fetch_key(Keys, I1, Size, Key, CmpF, ActualIndex)
|
|
).
|
|
|
|
/**
|
|
@pred b_hash_update( +_Key_, +_Hash_, +NewVal )
|
|
|
|
Update to the value associated with the ground term _Key_ in table _Hash_ to _NewVal_.
|
|
*/
|
|
b_hash_update(Hash, Key, NewVal):-
|
|
Hash = hash(Keys, Vals, Size, _, F, CmpF),
|
|
hash_f(Key,Size,Index,F),
|
|
fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex),
|
|
array_element(Vals, ActualIndex, Mutable),
|
|
update_mutable(NewVal, Mutable).
|
|
|
|
/**
|
|
@pred b_hash_update( +_Key_, -_OldVal_, +_Hash_, +NewVal )
|
|
|
|
Update to the value associated with the ground term _Key_ in table _Hash_ to _NewVal_, and unify _OldVal_ with the current value.
|
|
*/
|
|
b_hash_update(Hash, Key, OldVal, NewVal):-
|
|
Hash = hash(Keys, Vals, Size, _, F, CmpF),
|
|
hash_f(Key,Size,Index,F),
|
|
fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex),
|
|
array_element(Vals, ActualIndex, Mutable),
|
|
get_mutable(OldVal, Mutable),
|
|
update_mutable(NewVal, Mutable).
|
|
|
|
/** b_hash_insert(+_Hash_, +_Key_, _Val_, +_NewHash_ )
|
|
|
|
Insert the term _Key_-_Val_ in table _Hash_ and unify _NewHash_ with the result. If ground term _Key_ exists, update the dictionary.
|
|
*/
|
|
b_hash_insert(Hash, Key, NewVal, NewHash):-
|
|
Hash = hash(Keys, Vals, Size, N, F, CmpF),
|
|
hash_f(Key,Size,Index,F),
|
|
find_or_insert(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash).
|
|
|
|
find_or_insert(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash) :-
|
|
array_element(Keys, Index, El),
|
|
(
|
|
var(El)
|
|
->
|
|
add_element(Keys, Index, Size, N, Vals, Key, NewVal, Hash, NewHash)
|
|
;
|
|
cmp_f(CmpF, El, Key)
|
|
->
|
|
% do rb_update
|
|
array_element(Vals, Index, Mutable),
|
|
update_mutable(NewVal, Mutable),
|
|
Hash = NewHash
|
|
;
|
|
I1 is (Index+1) mod Size,
|
|
find_or_insert(Keys, I1, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash)
|
|
).
|
|
|
|
/**
|
|
@pred b_hash_insert_new(+_Hash_, +_Key_, _Val_, +_NewHash_ )
|
|
|
|
Insert the term _Key_-_Val_ in table _Hash_ and unify _NewHash_ with the result. If ground term _Key_ exists, fail.
|
|
*/
|
|
b_hash_insert_new(Hash, Key, NewVal, NewHash):-
|
|
Hash = hash(Keys, Vals, Size, N, F, CmpF),
|
|
hash_f(Key,Size,Index,F),
|
|
find_or_insert_new(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash).
|
|
|
|
find_or_insert_new(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash) :-
|
|
array_element(Keys, Index, El),
|
|
(
|
|
var(El)
|
|
->
|
|
add_element(Keys, Index, Size, N, Vals, Key, NewVal, Hash, NewHash)
|
|
;
|
|
cmp_f(CmpF, El, Key)
|
|
->
|
|
fail
|
|
;
|
|
I1 is (Index+1) mod Size,
|
|
find_or_insert_new(Keys, I1, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash)
|
|
).
|
|
|
|
add_element(Keys, Index, Size, N, Vals, Key, NewVal, Hash, NewHash) :-
|
|
get_mutable(NEls, N),
|
|
NN is NEls+1,
|
|
update_mutable(NN, N),
|
|
array_element(Keys, Index, Key),
|
|
update_mutable(NN, N),
|
|
array_element(Vals, Index, Mutable),
|
|
create_mutable(NewVal, Mutable),
|
|
(
|
|
NN > Size/3
|
|
->
|
|
expand_array(Hash, NewHash)
|
|
;
|
|
Hash = NewHash
|
|
).
|
|
|
|
expand_array(Hash, NewHash) :-
|
|
Hash == NewHash, !,
|
|
Hash = hash(Keys, Vals, Size, _X, F, _CmpF),
|
|
new_size(Size, NewSize),
|
|
array(NewKeys, NewSize),
|
|
array(NewVals, NewSize),
|
|
copy_hash_table(Size, Keys, Vals, F, NewSize, NewKeys, NewVals),
|
|
/* overwrite in place */
|
|
setarg(1, Hash, NewKeys),
|
|
setarg(2, Hash, NewVals),
|
|
setarg(3, Hash, NewSize).
|
|
|
|
expand_array(Hash, hash(NewKeys, NewVals, NewSize, X, F, CmpF)) :-
|
|
Hash = hash(Keys, Vals, Size, X, F, CmpF),
|
|
new_size(Size, NewSize),
|
|
array(NewKeys, NewSize),
|
|
array(NewVals, NewSize),
|
|
copy_hash_table(Size, Keys, Vals, F, NewSize, NewKeys, NewVals).
|
|
|
|
new_size(Size, NewSize) :-
|
|
Size > 1048576, !,
|
|
NewSize is Size+1048576.
|
|
new_size(Size, NewSize) :-
|
|
NewSize is Size*2.
|
|
|
|
copy_hash_table(0, _, _, _, _, _, _) :- !.
|
|
copy_hash_table(I1, Keys, Vals, F, Size, NewKeys, NewVals) :-
|
|
I is I1-1,
|
|
array_element(Keys, I, Key),
|
|
nonvar(Key), !,
|
|
array_element(Vals, I, Val),
|
|
insert_el(Key, Val, Size, F, NewKeys, NewVals),
|
|
copy_hash_table(I, Keys, Vals, F, Size, NewKeys, NewVals).
|
|
copy_hash_table(I1, Keys, Vals, F, Size, NewKeys, NewVals) :-
|
|
I is I1-1,
|
|
copy_hash_table(I, Keys, Vals, F, Size, NewKeys, NewVals).
|
|
|
|
insert_el(Key, Val, Size, F, NewKeys, NewVals) :-
|
|
hash_f(Key,Size,Index, F),
|
|
find_free(Index, Size, NewKeys, TrueIndex),
|
|
array_element(NewKeys, TrueIndex, Key),
|
|
array_element(NewVals, TrueIndex, Val).
|
|
|
|
find_free(Index, Size, Keys, NewIndex) :-
|
|
array_element(Keys, Index, El),
|
|
(
|
|
var(El)
|
|
->
|
|
NewIndex = Index
|
|
;
|
|
I1 is (Index+1) mod Size,
|
|
find_free(I1, Size, Keys, NewIndex)
|
|
).
|
|
|
|
hash_f(Key, Size, Index, F) :-
|
|
var(F), !,
|
|
term_hash(Key,-1,Size,Index).
|
|
hash_f(Key, Size, Index, F) :-
|
|
call(F, Key, Size, Index).
|
|
|
|
cmp_f(F, A, B) :-
|
|
var(F), !,
|
|
A == B.
|
|
cmp_f(F, A, B) :-
|
|
call(F, A, B).
|
|
|
|
/**
|
|
@pred b_hash_to_list(+_Hash_, -_KeyValList_ )
|
|
|
|
The term _KeyValList_ unifies with a list containing all terms _Key_-_Val_ in the hash table.
|
|
*/
|
|
b_hash_to_list(hash(Keys, Vals, _, _, _, _), LKeyVals) :-
|
|
Keys =.. (_.LKs),
|
|
Vals =.. (_.LVs),
|
|
mklistpairs(LKs, LVs, LKeyVals).
|
|
|
|
/**
|
|
@pred b_key_to_list(+_Hash_, -_KeyList_ )
|
|
|
|
The term _KeyList_ unifies with a list containing all keys in the hash table.
|
|
*/
|
|
b_hash_keys_to_list(hash(Keys, _, _, _, _, _), LKeys) :-
|
|
Keys =.. (_.LKs),
|
|
mklistels(LKs, LKeys).
|
|
|
|
/**
|
|
@pred b_key_to_list(+_Hash_, -_ValList_ )
|
|
|
|
The term _`valList_ unifies with a list containing all values in the hash table.
|
|
*/
|
|
b_hash_values_to_list(hash(_, Vals, _, _, _, _), LVals) :-
|
|
Vals =.. (_.LVs),
|
|
mklistvals(LVs, LVals).
|
|
|
|
mklistpairs([], [], []).
|
|
mklistpairs(V.LKs, _.LVs, KeyVals) :- var(V), !,
|
|
mklistpairs(LKs, LVs, KeyVals).
|
|
mklistpairs(K.LKs, V.LVs, (K-VV).KeyVals) :-
|
|
get_mutable(VV, V),
|
|
mklistpairs(LKs, LVs, KeyVals).
|
|
|
|
mklistels([], []).
|
|
mklistels(V.Els, NEls) :- var(V), !,
|
|
mklistels(Els, NEls).
|
|
mklistels(K.Els, K.NEls) :-
|
|
mklistels(Els, NEls).
|
|
|
|
mklistvals([], []).
|
|
mklistvals(V.Vals, NVals) :- var(V), !,
|
|
mklistvals(Vals, NVals).
|
|
mklistvals(K.Vals, KK.NVals) :-
|
|
get_mutable(KK, K),
|
|
mklistvals(Vals, NVals).
|
|
|
|
/**
|
|
@}
|
|
*/
|