hash-map equality: bash, c, coffee, cs, es6, ...
authorJoel Martin <github@martintribe.org>
Tue, 17 Nov 2015 05:28:58 +0000 (23:28 -0600)
committerJoel Martin <github@martintribe.org>
Tue, 17 Nov 2015 05:28:58 +0000 (23:28 -0600)
- hash-map equality support for bash, c, coffee, cs, es6, java, js,
  julia, make, php.
- also, add another test to catch another hash-map in-equality: same
  keys, different values

12 files changed:
bash/types.sh
c/types.c
coffee/types.coffee
cs/types.cs
es6/types.js
java/Makefile
java/src/main/java/mal/types.java
js/types.js
julia/types.jl
make/types.mk
php/types.php
tests/step9_try.mal

index 02108a7..f171b17 100644 (file)
@@ -81,7 +81,7 @@ _equal? () {
     case "${ot1}" in
     string|symbol|keyword|number)
         [[ "${ANON["${1}"]}" == "${ANON["${2}"]}" ]] ;;
-    list|vector|hash_map)
+    list|vector)
         _count "${1}"; local sz1="${r}"
         _count "${2}"; local sz2="${r}"
         [[ "${sz1}" == "${sz2}" ]] || return 1
@@ -91,6 +91,20 @@ _equal? () {
             _equal? "${a1[${i}]}" "${a2[${i}]}" || return 1
         done
         ;;
+    hash_map)
+        local hm1="${ANON["${1}"]}"
+        eval local ks1="\${!${hm1}[@]}"
+        local hm2="${ANON["${2}"]}"
+        eval local ks2="\${!${hm2}[@]}"
+        [[ "${#ks1}" == "${#ks2}" ]] || return 1
+        for k in ${ks1}; do
+            eval v1="\${${hm1}[\"${k}\"]}"
+            eval v2="\${${hm2}[\"${k}\"]}"
+            [ "${v1}" ] || return 1
+            [ "${v2}" ] || return 1
+            _equal? "${v1}" "${v2}" || return 1
+        done
+        ;;
     *)
         [[ "${1}" == "${2}" ]] ;;
     esac
index 1602d54..d99c6cf 100644 (file)
--- a/c/types.c
+++ b/c/types.c
@@ -268,6 +268,9 @@ MalVal *_apply(MalVal *f, MalVal *args) {
 
 
 int _equal_Q(MalVal *a, MalVal *b) {
+    GHashTableIter iter;
+    gpointer key, value;
+
     if (a == NULL || b == NULL) { return FALSE; }
 
     // If types are the same or both are sequential then they might be equal
@@ -305,8 +308,22 @@ int _equal_Q(MalVal *a, MalVal *b) {
         }
         return TRUE;
     case MAL_HASH_MAP:
-        _error("_equal_Q does not support hash-maps yet");
-        return FALSE;
+        if (g_hash_table_size(a->val.hash_table) !=
+            g_hash_table_size(b->val.hash_table)) {
+            return FALSE;
+        }
+        g_hash_table_iter_init (&iter, a->val.hash_table);
+        while (g_hash_table_iter_next (&iter, &key, &value)) {
+            if (!g_hash_table_contains(b->val.hash_table, key)) {
+                return FALSE;
+            }
+            MalVal *aval = (MalVal *) g_hash_table_lookup(a->val.hash_table, key);
+            MalVal *bval = (MalVal *) g_hash_table_lookup(b->val.hash_table, key);
+            if (!_equal_Q(aval, bval)) {
+                return FALSE;
+            }
+        }
+        return TRUE;
     case MAL_FUNCTION_C:
     case MAL_FUNCTION_MAL:
         return a->val.f0 == b->val.f0;
index b31c9b9..20a9cff 100644 (file)
@@ -38,9 +38,7 @@ E._equal_Q = _equal_Q = (a,b) ->
       bkeys = (key for key of b)
       return false if akeys.length != bkeys.length
       for akey,i in akeys
-        bkey = bkeys[i]
-        return false if akey != bkey
-        return false if !_equal_Q(a[akey], b[bkey])
+        return false if !_equal_Q(a[akey], b[akey])
       true
     else a == b
 
index 0ed335f..21088fd 100644 (file)
@@ -58,6 +58,19 @@ namespace Mal {
                         }
                     }
                     return true;
+                } else if (a is MalHashMap) {
+                    var akeys = ((MalHashMap)a).getValue().Keys;
+                    var bkeys = ((MalHashMap)b).getValue().Keys;
+                    if (akeys.Count != bkeys.Count) {
+                        return false;
+                    }
+                    foreach (var k in akeys) {
+                        if (!_equal_Q(((MalHashMap)a).getValue()[k],
+                                      ((MalHashMap)b).getValue()[k])) {
+                            return false;
+                        }
+                    }
+                    return true;
                 } else {
                     return a == b;
                 }
index 9f5f0ef..fb579b3 100644 (file)
@@ -34,12 +34,9 @@ export function _equal_Q (a, b) {
         }
         return true
     case 'hash-map':
-        let akeys = Object.keys(a).sort(),
-            bkeys = Object.keys(b).sort()
-        if (akeys.length !== bkeys.length) { return false }
-        for (let i=0; i<akeys.length; i++) {
-            if (akeys[i] !== bkeys[i]) { return false }
-            if (! _equal_Q(a.get(akeys[i]), b.get(bkeys[i]))) { return false }
+        if (a.size !== b.size) { return false }
+        for (let k of a.keys()) {
+            if (! _equal_Q(a.get(k), b.get(k))) { return false }
         }
         return true
     default:
index b739b18..06f486c 100644 (file)
@@ -11,6 +11,9 @@ SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
 all:
        mvn install
 
+src/main/mal/%.java:
+       mvn install
+
 #.PHONY: stats tests $(TESTS)
 .PHONY: stats
 
index a8a2dfa..aa11cf8 100644 (file)
@@ -64,6 +64,20 @@ public class types {
                     }
                 }
                 return true;
+            } else if (a instanceof MalHashMap) {
+                if (((MalHashMap)a).value.size() != ((MalHashMap)b).value.size()) {
+                    return false;
+                }
+                //HashMap<String,MalVal> hm = (HashMap<String,MalVal>)a.value;
+                MalHashMap mhm = ((MalHashMap)a);
+                HashMap<String,MalVal> hm = (HashMap<String,MalVal>)mhm.value;
+                for (String k : hm.keySet()) {
+                    if (! _equal_Q(((MalVal)((MalHashMap)a).value.get(k)),
+                                   ((MalVal)((MalHashMap)b).value.get(k)))) {
+                        return false;
+                    }
+                }
+                return true;
             } else {
                 return a == b;
             }
index 68c3743..7897f89 100644 (file)
@@ -43,12 +43,9 @@ function _equal_Q (a, b) {
         }
         return true;
     case 'hash-map':
-        var akeys = Object.keys(a).sort(),
-            bkeys = Object.keys(b).sort();
-        if (akeys.length !== bkeys.length) { return false; }
-        for (var i=0; i<akeys.length; i++) {
-            if (akeys[i] !== bkeys[i]) { return false; }
-            if (! equal_Q(a[akeys[i]], b[bkeys[i]])) { return false; }
+        if (Object.keys(a).length !== Object.keys(b).length) { return false; }
+        for (var k in a) {
+            if (! _equal_Q(a[k], b[k])) { return false; }
         }
         return true;
     default:
index fb809a6..a170a45 100644 (file)
@@ -41,6 +41,16 @@ function equal_Q(a, b)
         tuple(a...) == tuple(b...)
     elseif isa(a,AbstractString)
         a == b
+    elseif isa(a,Dict)
+        if length(a) !== length(b)
+          return false
+        end
+        for (k,v) in a
+            if !equal_Q(v,b[k])
+                return false
+            end
+        end
+        return true
     else
         a === b
     end
index 6320396..2a98982 100644 (file)
@@ -111,16 +111,26 @@ _clone_obj = $(strip \
                      $(eval $(new_obj)_value := $(strip $($(1)_value)))))\
                    $(new_obj))))
 
+_hash_equal? = $(strip \
+                 $(if $(and $(call _EQ,$(foreach v,$(call __get_obj_values,$(1)),$(word 4,$(subst _, ,$(v)))),$(foreach v,$(call __get_obj_values,$(2)),$(word 4,$(subst _, ,$(v))))),\
+                            $(call _EQ,$(call _count,$(1)),$(words $(call gmsl_pairmap,_equal?,$(foreach v,$(call __get_obj_values,$(1)),$($(v))),\
+                                                                                               $(foreach v,$(call __get_obj_values,$(2)),$($(v))))))),\
+                      $(__true),))
+
 _equal? = $(strip \
             $(foreach ot1,$(call _obj_type,$(1)),$(foreach ot2,$(call _obj_type,$(2)),\
               $(if $(or $(call _EQ,$(ot1),$(ot2)),\
                         $(and $(call _sequential?,$(1)),$(call _sequential?,$(2)))),\
                 $(if $(or $(call _string?,$(1)),$(call _symbol?,$(1)),$(call _keyword?,$(1)),$(call _number?,$(1))),\
                   $(call _EQ,$($(1)_value),$($(2)_value)),\
-                $(if $(or $(call _vector?,$(1)),$(call _list?,$(1)),$(call _hash_map?,$(1))),\
+                $(if $(call _hash_map?,$(1)),\
+                  $(call _hash_equal?,$(1),$(2)),\
+                $(if $(or $(call _vector?,$(1)),$(call _list?,$(1))),\
                   $(if $(and $(call _EQ,$(call _count,$(1)),$(call _count,$(2))),\
-                             $(call _EQ,$(call _count,$(1)),$(words $(call gmsl_pairmap,_equal?,$(call __get_obj_values,$(1)),$(call __get_obj_values,$(2)))))),$(__true),),\
-                $(call _EQ,$(1),$(2))))))))
+                             $(call _EQ,$(call _count,$(1)),$(words $(call gmsl_pairmap,_equal?,$(call __get_obj_values,$(1)),\
+                                                                                                $(call __get_obj_values,$(2)))))),\
+                    $(__true),),\
+                $(call _EQ,$(1),$(2)))))))))
 
 _undefined? = $(or $(call _EQ,undefined,$(origin $(1))),$(filter $(__undefined),$($(1))))
 
index fa87197..0c1234c 100644 (file)
@@ -27,6 +27,16 @@ function _equal_Q($a, $b) {
             if (!_equal_Q($a[$i], $b[$i])) { return false; }
         }
         return true;
+    } elseif (_hash_map_Q($a)) {
+        if ($a->count() !== $b->count()) { return false; }
+        $hm1 = $a->getArrayCopy();
+        $hm2 = $b->getArrayCopy();
+        foreach (array_keys($hm1) as $k) {
+            if ($hm1[$k] !== $hm2[$k]) {
+                return false;
+            }
+        }
+        return true;
     } else {
         return $a === $b;
     }
index 1a2c5ed..4c1d64f 100644 (file)
 ;; Testing equality of hash-maps
 (= {:a 11 :b 22} (hash-map :b 22 :a 11))
 ;=>true
+(= {:a 11 :b 22} (hash-map :b 23 :a 11))
+;=>false
 (= {:a 11 :b 22} (hash-map :a 11))
 ;=>false
 (= {:a 11 :b 22} (list :a 11 :b 22))