summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hash.c42
-rw-r--r--hash.h1
-rw-r--r--tests/010/hash.tl5
-rw-r--r--txr.142
4 files changed, 90 insertions, 0 deletions
diff --git a/hash.c b/hash.c
index 74ec8b44..cdf8e453 100644
--- a/hash.c
+++ b/hash.c
@@ -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));
diff --git a/hash.h b/hash.h
index 3334bf93..2d2ef37e 100644
--- a/hash.h
+++ b/hash.h
@@ -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
diff --git a/txr.1 b/txr.1
index 8703cc49..1c9e799a 100644
--- a/txr.1
+++ b/txr.1
@@ -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