diff options
-rw-r--r-- | hash.c | 42 | ||||
-rw-r--r-- | hash.h | 1 | ||||
-rw-r--r-- | tests/010/hash.tl | 5 | ||||
-rw-r--r-- | txr.1 | 42 |
4 files changed, 90 insertions, 0 deletions
@@ -1939,6 +1939,47 @@ val hash_uni(val hash1, val hash2, val joinfun, val map1fun, val map2fun) } } +val hash_join(val hash1, val hash2, val joinfun, val h1dfl, val h2dfl) +{ + val self = lit("hash-join"); + struct hash *h1 = coerce(struct hash *, cobj_handle(self, hash1, hash_cls)); + struct hash *h2 = coerce(struct hash *, cobj_handle(self, hash2, hash_cls)); + + if (h1->hops != h2->hops) + uw_throwf(error_s, lit("~a: ~s and ~s are incompatible hashes"), + self, hash1, hash2, nao); + + { + val hout = make_similar_hash(hash1); + val h1ent, h2ent; + struct hash_iter hi; + + hash_iter_init(&hi, hash1, self); + + for (h1ent = hash_iter_next(&hi); h1ent; h1ent = hash_iter_next(&hi)) { + val h1val = us_cdr(h1ent); + val key = us_car(h1ent); + val h2ent = gethash_e(self, hash2, key); + val h2val = if3(h2ent, us_cdr(h2ent), h2dfl); + + sethash(hout, key, funcall2(joinfun, h1val, h2val)); + } + + hash_iter_init(&hi, hash2, self); + + for (h2ent = hash_iter_next(&hi); h2ent; h2ent = hash_iter_next(&hi)) { + val h2val = us_cdr(h2ent); + val key = us_car(h2ent); + val h1ent = gethash_e(self, hash1, us_car(h2ent)); + val h1val = if3(h1ent, us_cdr(h1ent), h1dfl); + + sethash(hout, key, funcall2(joinfun, h1val, h2val)); + } + + return hout; + } +} + val hash_diff(val hash1, val hash2) { val self = lit("hash-diff"); @@ -2246,6 +2287,7 @@ void hash_init(void) reg_fun(intern(lit("hash-pairs"), user_package), func_n1(hash_pairs)); reg_fun(intern(lit("hash-alist"), user_package), func_n1(hash_alist)); reg_fun(intern(lit("hash-uni"), user_package), func_n5o(hash_uni, 2)); + reg_fun(intern(lit("hash-join"), user_package), func_n5o(hash_join, 3)); reg_fun(intern(lit("hash-diff"), user_package), func_n2(hash_diff)); reg_fun(intern(lit("hash-symdiff"), user_package), func_n2(hash_symdiff)); reg_fun(intern(lit("hash-isec"), user_package), func_n3o(hash_isec, 2)); @@ -97,6 +97,7 @@ val hash_values(val hash); val hash_pairs(val hash); val hash_alist(val hash); val hash_uni(val hash1, val hash2, val joinfun, val map1fun, val map2fun); +val hash_join(val hash1, val hash2, val joinfun, val h1dfl, val h2dfl); val hash_diff(val hash1, val hash2); val hash_symdiff(val hash1, val hash2); val hash_isec(val hash1, val hash2, val joinfun); diff --git a/tests/010/hash.tl b/tests/010/hash.tl index 415dd223..b2e8d891 100644 --- a/tests/010/hash.tl +++ b/tests/010/hash.tl @@ -73,6 +73,11 @@ [hash-uni h1 h2 + : -] #H(() (a 1) (b 4) (c 6) (d 8) (e 5)) [hash-uni h1 h2 + - -] #H(() (a -1) (b 0) (c 0) (d 0) (e 5))) (mtest + [hash-join h1 h2 +] :error + [hash-join h1 h2 + 0] :error + [hash-join h1 h2 + : 0] :error + [hash-join h1 h2 + 0 0] #H(() (a 1) (b 0) (c 0) (d 0) (e -5))) + (mtest (hash-diff h1 h2) #H(() (a 1)) (hash-diff h2 h1) #H(() (e -5))) (mtest @@ -57672,6 +57672,7 @@ or .coNP Functions @, hash-uni @, hash-diff @ hash-symdiff and @ hash-isec .synb .mets (hash-uni < hash1 < hash2 >> [ joinfun >> [ map1fun <> [ map2fun ]]]) +.mets (hash-join < hash1 < hash2 < joinfun >> [ hash1dfl <> [ hash2dfl ]]) .mets (hash-diff < hash1 << hash2 ) .mets (hash-symdiff < hash1 << hash2 ) .mets (hash-isec < hash1 < hash2 <> [ joinfun ]) @@ -57742,6 +57743,47 @@ they are applied to values without regard for whether their keys exist in both hashes or just one. The +.code hash-join +function performs a union operation similar to, but usefully different from +.codn hash-uni . +The +.meta joinfun +argument is mandatory in +.codn hash-join , +and is applied to all items, regardless of whether they are present +in just one hash or both hashes. +The arguments +.meta hash1dfl +and +.meta hash2dfl +specify default values used in invocations of +.meta joinfun +for keys that are present only in one hash. These values default to +.codn nil . +For every key that is present only in +.metn hash1 , +.meta joinfun +is invoked with that key's value as its left argument, and the +.meta hash2dfl +value as the right argument. +Conversely, for every key that is present only in +.metn hash2 , +.meta joinfun +is invoked with the +.meta hash1dfl +value as the left argument, +and that key's value as its right argument. +For every key that is present in both hashes, +.meta joinfun +is invoked with the values, respectively, from +.meta hash1 +and +.metn hash2 . +The returned hash contains all the keys from both hashes, +associated with the values returned by +.metn joinfun . + +The .code hash-diff function performs a set difference. First, a copy of .meta hash1 |