YAP 7.1.0
bhash.yap
Go to the documentation of this file.
1%% -*- Prolog -*-
3/**
4 * @file bhash.yap
5 * @author VITOR SANTOS COSTA <vsc@VITORs-MBP.lan>
6 * @date Tue Nov 17 01:11:29 2015
7 *
8 * @brief Backtrackable Hash Tables
9 *
10 *
11*/
12
13:- .
14:- yap_flag(unknown,error).
15:- style_check(all).
16
17:- module(b_hash, [ b_hash_new/1,
24 b_hash_insert/4,
26 b_hash_code/2,
29 b_hash_values_to_list/2,
30 b_hash_keys_to_list/2
31 ]).
32
33/**
34 * @defgroup bhash Backtrackable Hash Tables
35 * @ingroup YAPLibrary
36 *
37 * @{
38
39This library implements hash-tables, that associate keys with values.
40It requires the hash key to be any ground term, but the value term can
41take any value.
42
43. The library can be loaded as
44
45:- use_module( library( bhash ) ).
46
47The library's code uses backtrackable updates and an array to store
48the terms. Implicit keys are generated by term_hash/4 (note that we cannot guarantee there will be no collisions).
49
50*/
51
52:- term_hash/4use_module(library(terms), [ ]).
53
54
55:- meta_predicate(b_hash_new(-,+,3,2)).
56
57/**
58 * Initial hash table size: should be a prime number>?
59 */
60array_default_size(2048).
61
62/** @pred is_b_hash( +Hash )
63
64True if term _Hash_ is a hash table.
65*/
66is_b_hash(V) :- var(V), var, var.
67is_b_hash(hash(_,_,_,_,_)).
68
69/** @pred b_hash_new( -NewHash )
70
71Create a empty hash table _NewHash_, with size obtained from array__default_size/1, by default 2048 entries.
72*/
73b_hash_new(hash(Keys, Vals, Size, N, _, _)) :-
74 array_default_size(Size),
75 array(Keys, Size),
76 array(Vals, Size),
77 create_mutable(0, N).
78
79/** @pred b_hash_new( -_NewHash_, +_Size_ )
80
81Create a empty hash table, with size _Size_ entries.
82*/
83b_hash_new(hash(Keys, Vals, Size, N, _, _), Size) :-
84 array(Keys, Size),
85 array(Vals, Size),
86 create_mutable(0, N).
87
88/** @pred b_hash_new( -_NewHash_, +_Size_, :_Hash_, :_Cmp_ )
89
90Create a empty hash table, with size _Size_ entries.
91_Hash_ defines a partition function, and _Cmp_ defined a comparison function.
92*/
93b_hash_new(hash(Keys,Vals, Size, N, HashF, CmpF), Size, HashF, CmpF) :-
94 array(Keys, Size),
95 array(Vals, Size),
96 create_mutable(0, N).
97
98/**
99 @pred b_hash_size( +_Hash_, -_Size_ )
100
101_Size_ unifies with the size of the hash table _Hash_.
102*/
103b_hash_size(hash(_, _, Size, _, _, _), Size).
104
105/**
106 @pred b_hash_lookup( +_Key_, ?_Val_, +_Hash_ )
107
108Search the ground term _Key_ in table _Hash_ and unify _Val_ with the associated entry.
109*/
110b_hash_lookup(Key, Val, hash(Keys, Vals, Size, _, F, CmpF)):-
111 hash_f(Key, Size, Index, F),
112 fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex),
113 array_element(Vals, ActualIndex, Mutable),
114 get_mutable(Val, Mutable).
115
116fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex) :-
117 array_element(Keys, Index, El),
118 nonvar(El),
119 (
120 cmp_f(CmpF, El, Key)
121 ->
122 Index = ActualIndex
123 ;
124 I1 is (Index+1) mod Size,
125 fetch_key(Keys, I1, Size, Key, CmpF, ActualIndex)
126 ).
127
128/**
129 @pred b_hash_update( +_Key_, +_Hash_, +NewVal )
130
131Update to the value associated with the ground term _Key_ in table _Hash_ to _NewVal_.
132*/
133b_hash_update(Hash, Key, NewVal):-
134 Hash = hash(Keys, Vals, Size, _, F, CmpF),
135 hash_f(Key,Size,Index,F),
136 fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex),
137 array_element(Vals, ActualIndex, Mutable),
138 update_mutable(NewVal, Mutable).
139
140/**
141 @pred b_hash_update( +_Key_, -_OldVal_, +_Hash_, +NewVal )
142
143Update to the value associated with the ground term _Key_ in table _Hash_ to _NewVal_, and unify _OldVal_ with the current value.
144*/
145b_hash_update(Hash, Key, OldVal, NewVal):-
146 Hash = hash(Keys, Vals, Size, _, F, CmpF),
147 hash_f(Key,Size,Index,F),
148 fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex),
149 array_element(Vals, ActualIndex, Mutable),
150 get_mutable(OldVal, Mutable),
151 update_mutable(NewVal, Mutable).
152
153/** b_hash_insert(+_Hash_, +_Key_, _Val_, +_NewHash_ )
154
155Insert the term _Key_-_Val_ in table _Hash_ and unify _NewHash_ with the result. If ground term _Key_ exists, update the dictionary.
156*/
157b_hash_insert(Hash, Key, NewVal, NewHash):-
158 Hash = hash(Keys, Vals, Size, N, F, CmpF),
159 hash_f(Key,Size,Index,F),
160 find_or_insert(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash).
161
162find_or_insert(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash) :-
163 array_element(Keys, Index, El),
164 (
165 var(El)
166 ->
167 add_element(Keys, Index, Size, N, Vals, Key, NewVal, Hash, NewHash)
168 ;
169 cmp_f(CmpF, El, Key)
170 ->
171 % do rb_update
172 array_element(Vals, Index, Mutable),
173 update_mutable(NewVal, Mutable),
174 Hash = NewHash
175 ;
176 I1 is (Index+1) mod Size,
177 find_or_insert(Keys, I1, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash)
178 ).
179
180/**
181 @pred b_hash_insert_new(+_Hash_, +_Key_, _Val_, +_NewHash_ )
182
183Insert the term _Key_-_Val_ in table _Hash_ and unify _NewHash_ with the result. If ground term _Key_ exists, fail.
184*/
185b_hash_insert_new(Hash, Key, NewVal, NewHash):-
186 Hash = hash(Keys, Vals, Size, N, F, CmpF),
187 hash_f(Key,Size,Index,F),
188 find_or_insert_new(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash).
189
190find_or_insert_new(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash) :-
191 array_element(Keys, Index, El),
192 (
193 var(El)
194 ->
195 add_element(Keys, Index, Size, N, Vals, Key, NewVal, Hash, NewHash)
196 ;
197 cmp_f(CmpF, El, Key)
198 ->
199 cmp_f
200 ;
201 I1 is (Index+1) mod Size,
202 find_or_insert_new(Keys, I1, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash)
203 ).
204
205add_element(Keys, Index, Size, N, Vals, Key, NewVal, Hash, NewHash) :-
206 get_mutable(NEls, N),
207 NN is NEls+1,
208 update_mutable(NN, N),
209 array_element(Keys, Index, Key),
210 update_mutable(NN, N),
211 array_element(Vals, Index, Mutable),
212 create_mutable(NewVal, Mutable),
213 (
214 NN > Size/3
215 ->
216 expand_array(Hash, NewHash)
217 ;
218 Hash = NewHash
219 ).
220
221expand_array(Hash, NewHash) :-
222 Hash == NewHash, expand_array,
223 Hash = hash(Keys, Vals, Size, _X, F, _CmpF),
224 new_size(Size, NewSize),
225 array(NewKeys, NewSize),
226 array(NewVals, NewSize),
227 copy_hash_table(Size, Keys, Vals, F, NewSize, NewKeys, NewVals),
228 /* overwrite in place */
229 setarg(1, Hash, NewKeys),
230 setarg(2, Hash, NewVals),
231 setarg(3, Hash, NewSize).
232
233expand_array(Hash, hash(NewKeys, NewVals, NewSize, X, F, CmpF)) :-
234 Hash = hash(Keys, Vals, Size, X, F, CmpF),
235 new_size(Size, NewSize),
236 array(NewKeys, NewSize),
237 array(NewVals, NewSize),
238 copy_hash_table(Size, Keys, Vals, F, NewSize, NewKeys, NewVals).
239
240new_size(Size, NewSize) :-
241 Size > 1048576, new_size,
242 NewSize is Size+1048576.
243new_size(Size, NewSize) :-
244 NewSize is Size*2.
245
246copy_hash_table(0, _, _, _, _, _, _) :- copy_hash_table.
247copy_hash_table(I1, Keys, Vals, F, Size, NewKeys, NewVals) :-
248 I is I1-1,
249 array_element(Keys, I, Key),
250 nonvar(Key), nonvar,
251 array_element(Vals, I, Val),
252 insert_el(Key, Val, Size, F, NewKeys, NewVals),
253 copy_hash_table(I, Keys, Vals, F, Size, NewKeys, NewVals).
254copy_hash_table(I1, Keys, Vals, F, Size, NewKeys, NewVals) :-
255 I is I1-1,
256 copy_hash_table(I, Keys, Vals, F, Size, NewKeys, NewVals).
257
258insert_el(Key, Val, Size, F, NewKeys, NewVals) :-
259 hash_f(Key,Size,Index, F),
260 find_free(Index, Size, NewKeys, TrueIndex),
261 array_element(NewKeys, TrueIndex, Key),
262 array_element(NewVals, TrueIndex, Val).
263
264find_free(Index, Size, Keys, NewIndex) :-
265 array_element(Keys, Index, El),
266 (
267 var(El)
268 ->
269 NewIndex = Index
270 ;
271 I1 is (Index+1) mod Size,
272 find_free(I1, Size, Keys, NewIndex)
273 ).
274
275hash_f(Key, Size, Index, F) :-
276 var(F), var,
277 term_hash(Key,-1,Size,Index).
278hash_f(Key, Size, Index, F) :-
279 call(F, Key, Size, Index).
280
281cmp_f(F, A, B) :-
282 var(F), var,
283 A == B.
284cmp_f(F, A, B) :-
285 call(F, A, B).
286
287/**
288 @pred b_hash_to_list(+_Hash_, -_KeyValList_ )
289
290The term _KeyValList_ unifies with a list containing all terms _Key_-_Val_ in the hash table.
291*/
292b_hash_to_list(hash(Keys, Vals, _, _, _, _), LKeyVals) :-
293 Keys =.. [_|LKs],
294 Vals =.. [_|LVs],
295 mklistpairs(LKs, LVs, LKeyVals).
296
297/**
298 @pred b_key_to_list(+_Hash_, -_KeyList_ )
299
300The term _KeyList_ unifies with a list containing all keys in the hash table.
301*/
302b_hash_keys_to_list(hash(Keys, _, _, _, _, _), LKeys) :-
303 Keys =.. [_|LKs],
304 mklistels(LKs, LKeys).
305
306/**
307 @pred b_key_to_list(+_Hash_, -_ValList_ )
308
309The term _`valList_ unifies with a list containing all values in the hash table.
310*/
311b_hash_values_to_list(hash(_, Vals, _, _, _, _), LVals) :-
312 Vals =.. [_|LVs],
313 mklistvals(LVs, LVals).
314
315mklistpairs([], [], []).
316mklistpairs([V|LKs], [_|LVs], KeyVals) :-
317 var(V),
318 var,
319 mklistpairs(LKs, LVs, KeyVals).
320mklistpairs([K|LKs], [V|LVs], [(K-VV)|KeyVals]) :-
321 get_mutable(VV, V),
322 mklistpairs(LKs, LVs, KeyVals).
323
324mklistels([], []).
325mklistels([V|Els], NEls) :- var(V), var,
326 mklistels(Els, NEls).
327mklistels([K|Els], K.NEls) :-
328 mklistels(Els, NEls).
329
330mklistvals([], []).
331mklistvals([V|Vals], NVals) :- var(V), var,
332 mklistvals(Vals, NVals).
333mklistvals([K|Vals], [KK|NVals]) :-
334 get_mutable(KK, K),
335 mklistvals(Vals, NVals).
336
337/**
338@}
339*/
340
create_mutable(+ D,- M)
get_mutable(? D,+ M)
setarg(+ I,+ S,? T)
update_mutable(+ D,+ M)
yap_flag( ?Param, ?Value)
array_element(+ Name, + Index, ? Element)
use_module( +Files )
term_hash(+ Term, + Depth, + Range, ? Hash)
array( +Name, +Size )
call(+ Closure,...,? Ai,...)
nonvar( T)
var( T)
b_hash_insert_new(+_Hash_, +_Key_, Val, +_NewHash_ )
b_hash_lookup( +_Key_, ?_Val_, +_Hash_ )
b_hash_new( -NewHash )
b_hash_new( -_NewHash_, +_Size_ )
b_hash_new( -_NewHash_, +_Size_, :Hash, :Cmp )
b_hash_size( +_Hash_, -_Size_ )
b_hash_to_list(+_Hash_, -_KeyValList_ )
b_hash_update( +_Key_, +_Hash_, +NewVal )
b_hash_update( +_Key_, -_OldVal_, +_Hash_, +NewVal )
is_b_hash( +Hash )