From b4826605af1df3b57bda19426d687a4b65343d6d Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Wed, 7 May 2025 14:05:57 +0200 Subject: [PATCH] add dict comparison function --- lib/compare/registry.js | 6 ++-- lib/compare/structures.js | 56 +++++++++++++++++++++++---------- lib/compare/structures.types.js | 6 ++-- lib/structures/dict.js | 7 ++++- lib/structures/set.js | 6 +++- 5 files changed, 57 insertions(+), 24 deletions(-) diff --git a/lib/compare/registry.js b/lib/compare/registry.js index bfda00f..d773d7b 100644 --- a/lib/compare/registry.js +++ b/lib/compare/registry.js @@ -1,10 +1,8 @@ import { getInst, getType } from "../primitives/dynamic.js"; import { SymbolBool, SymbolChar, SymbolDouble, SymbolInt, SymbolType, SymbolUnit } from "../primitives/primitive_types.js"; -import { getHumanReadableName } from "../primitives/symbol.js"; import { symbolDict, symbolList, symbolProduct, symbolSet, symbolSum } from "../structures/type_constructors.js"; -import { capitalizeFirstLetter } from "../util/util.js"; import { compareBools, compareNumbers, compareStrings, compareUnits } from "./primitives.js"; -import { compareLists, compareProducts, compareSets, compareSums } from "./structures.js"; +import { compareDicts, compareLists, compareProducts, compareSets, compareSums } from "./structures.js"; import { compareTypes } from "./type.js"; const typeSymbolToCmp = new Map([ @@ -20,7 +18,7 @@ const typeSymbolToCmp = new Map([ [symbolProduct , compareProducts], [symbolSum , compareSums], [symbolSet , compareSets], - // [symbolDict , compareDicts], TODO + [symbolDict , compareDicts], ]); export const makeCompareFn = type => { diff --git a/lib/compare/structures.js b/lib/compare/structures.js index bde873a..a12c7dc 100644 --- a/lib/compare/structures.js +++ b/lib/compare/structures.js @@ -2,11 +2,11 @@ import { compareNumbers } from "./primitives.js" import { get, length as lengthLs } from "../structures/list.js"; -import { read, length as lengthSet } from "../structures/set.js"; -import { newProduct, getLeft, getRight } from "../structures/product.js"; +import { read as readSet, length as lengthSet, first as firstSet } from "../structures/set.js"; +import { read as readDict, length as lengthDict, first as firstDict } from "../structures/dict.js"; +import { getLeft, getRight } from "../structures/product.js"; import { match } from "../structures/sum.js"; -// (a -> a -> Int) -> [a] -> [a] -> Int export const compareLists = compareElems => x => y => { return compareNumbers(lengthLs(x))(lengthLs(y)) || (() => { @@ -20,13 +20,11 @@ export const compareLists = compareElems => x => y => { })(); }; -// (a -> a -> Int) -> (b -> b -> Int) -> (a, b) -> (a, b) -> Int export const compareProducts = compareLeft => compareRight => x => y => { return compareLeft (getLeft (x))(getLeft (y)) || compareRight(getRight(x))(getRight(y)); }; -// (a -> a -> Int) -> (b -> b -> Int) -> (a | b) -> (a | b) -> Int export const compareSums = compareLeft => compareRight => x => y => { // console.log("compareSums...", x, y) return match(x) @@ -46,21 +44,47 @@ export const compareSums = compareLeft => compareRight => x => y => { ); }; -// (a -> a -> Int) -> {a} -> {a} -> Int export const compareSets = compareElems => x => y => { return compareNumbers(lengthSet(x))(lengthSet(y)) || (() => { // sets have same size -> iterate over both sets and compare their elements pairwise // because of the underlying red-black tree, iteration happens in ordered fashion - const iterate = iterX => iterY => - read(iterX) - (keyX => nextX => - read(iterY) - // we could also use the comparison function that is embedded in the set object, - // but to be consistent with the other comparison-functions, we don't. - (keyY => nextY => compareElems(keyX)(keyY) || iterate(nextX)(nextY)) - (0)) // end of set y (we'll never get here because sets are same size) - (0); // end of set x - return iterate(first(x))(first(y)); + const iterate = iterX => entryY => + match(readSet(iterX)) + (entryX => + match(readSet(entryY)) + (resultY => compareElems + (getLeft(entryX)) + (getLeft(resultY)) + || iterate + (getRight(entryX)) + (getRight(resultY))) + (_ => 0)) + (_ => 0); // we made it, sets are equal + return iterate(firstSet(x))(firstSet(y)); + })(); +}; + +export const compareDicts = compareKeys => compareValues => x => y => { + return compareNumbers(lengthDict(x))(lengthDict(y)) + || (() => { + // dicts have same size -> iterate over both and compare their entries pairwise + // because of the underlying red-black tree, iteration happens in ordered fashion + const iterate = iterX => iterY => + match(readDict(iterX)) + (entryX => + match(readDict(iterY)) + (entryY => compareKeys + (getLeft(getLeft(entryX))) + (getLeft(getLeft(entryY))) + || compareValues + (getRight(getLeft(entryX))) + (getRight(getLeft(entryY))) + || iterate + (getRight(entryX)) + (getRight(entryY))) + (_ => 0)) + (_ => 0); // we made it, dicts are equal + return iterate(firstDict(x))(firstDict(y)); })(); }; diff --git a/lib/compare/structures.types.js b/lib/compare/structures.types.js index 16c8fce..c5729d0 100644 --- a/lib/compare/structures.types.js +++ b/lib/compare/structures.types.js @@ -1,5 +1,5 @@ import { getDefaultTypeParser } from "../parser/type_parser.js"; -import { compareLists, compareProducts, compareSets, compareSums } from "./structures.js"; +import { compareDicts, compareLists, compareProducts, compareSets, compareSums } from "./structures.js"; const mkType = getDefaultTypeParser(); @@ -11,4 +11,6 @@ export const ModuleCompareStructures = [ {i: compareSums, t: mkType("∀a,b: (a -> a -> Int) -> (b -> b -> Int) -> (a+b) -> (a+b) -> Int")}, {i: compareSets, t: mkType("∀a: (a -> a -> Int) -> {a} -> {a} -> Int")}, -]; \ No newline at end of file + + {i: compareDicts, t: mkType("∀a,b: (a -> a -> Int) -> (b -> b-> Int) -> (a => b) -> (a => b) -> Int")} +]; diff --git a/lib/structures/dict.js b/lib/structures/dict.js index a5cc1cc..94b0860 100644 --- a/lib/structures/dict.js +++ b/lib/structures/dict.js @@ -14,7 +14,12 @@ export const last = dict => dict.tree.end; export const read = iter => { if (iter !== undefined && iter.valid) { - return newRight(newProduct(newProduct(iter.key)(iter.value))(iter.clone().next())); + return newRight( + newProduct( + newProduct(iter.key)(iter.value) + (iter.clone().next()) + ) + ); } else { return newLeft(unit); diff --git a/lib/structures/set.js b/lib/structures/set.js index e2257f0..46ce176 100644 --- a/lib/structures/set.js +++ b/lib/structures/set.js @@ -17,7 +17,11 @@ export const last = set => set.tree.end; // test if iterator is 'done', and if not, get element and advance iterator. export const read = iter => { if (iter !== undefined && iter.valid) { - return newRight(newProduct(iter.key)(iter.clone().next())); + return newRight( + newProduct + (iter.key) + (iter.clone().next()) + ); } else { return newLeft(unit);