refactor(common/models): convert traversal.children method to return array

This commit is contained in:
Joshua A. Horton 2024-07-25 14:38:10 +07:00
parent 23ffec0c26
commit 04d74fa9fa
3 changed files with 35 additions and 28 deletions

View file

@ -160,10 +160,12 @@ export class TrieTraversal implements LexiconTraversal {
}
}
*children(): Generator<{char: USVString, traversal: () => LexiconTraversal}> {
children(): {char: USVString, p: number, traversal: () => LexiconTraversal}[] {
let root = this.root;
const totalWeight = this.totalWeight;
const results: {char: USVString, p: number, traversal: () => LexiconTraversal}[] = [];
// Sorts _just_ the current level, and only if needed.
// We only care about sorting parts that we're actually accessing.
sortNode(root, true);
@ -183,21 +185,26 @@ export class TrieTraversal implements LexiconTraversal {
let internalNode = entryNode;
for(let lowSurrogate of internalNode.values) {
let prefix = this.prefix + entry + lowSurrogate;
yield {
results.push({
char: entry + lowSurrogate,
p: internalNode.children[lowSurrogate].weight / totalWeight,
traversal: function() { return new TrieTraversal(internalNode.children[lowSurrogate], prefix, totalWeight) }
}
});
}
} else {
// Determine how much of the 'leaf' entry has no Trie nodes, emulate them.
// No deduplication needed. If there were multiple keys available,
// we wouldn't be in a leaf node.
let fullText = entryNode.entries[0].key;
entry = entry + fullText[this.prefix.length + 1]; // The other half of the non-BMP char.
let prefix = this.prefix + entry;
yield {
const weight = entryNode.entries.reduce((best, current) => Math.max(best, current.weight), 0);
results.push({
char: entry,
p: weight / totalWeight,
traversal: function () {return new TrieTraversal(entryNode, prefix, totalWeight)}
}
});
}
} else if(isSentinel(entry)) {
continue;
@ -206,34 +213,41 @@ export class TrieTraversal implements LexiconTraversal {
continue;
} else {
let prefix = this.prefix + entry;
yield {
results.push({
char: entry,
p: entryNode.weight / totalWeight,
traversal: function() { return new TrieTraversal(entryNode, prefix, totalWeight)}
}
});
}
}
return;
return results;
} else { // type == 'leaf'
let prefix = this.prefix;
// No actual deduplication needed. If there were multiple keys available,
// we wouldn't be in a leaf node.
let children = root.entries.filter(function(entry) {
return entry.key != prefix && prefix.length < entry.key.length;
})
});
for(let {key} of children) {
let weight = children.reduce((best, current) => Math.max(current.weight, best), 0);
if(children.length > 0) {
const key = children[0].content;
let nodeKey = key[prefix.length];
if(isHighSurrogate(nodeKey)) {
// Merge the other half of an SMP char in!
nodeKey = nodeKey + key[prefix.length+1];
}
yield {
results.push({
char: nodeKey,
p: weight / totalWeight,
traversal: function() { return new TrieTraversal(root, prefix + nodeKey, totalWeight)}
}
});
};
return;
return results;
}
}

View file

@ -174,15 +174,13 @@ describe('Trie traversal abstractions', function() {
do {
assert.isNotEmpty(leafChildSequence);
let iter = curChild.traversal().children();
let curr = iter.next();
curChild = curr.value;
let children = curChild.traversal().children();
assert.equal(children.length, 1);
curChild = children[0];
// Test generator behavior - there should be one child, then the 'done' state.
assert.isDefined(curChild);
assert.equal(curChild.char, leafChildSequence[0]);
curr = iter.next();
assert.isTrue(curr.done);
// Prepare for iteration.
leafChildSequence.shift();
@ -275,15 +273,13 @@ describe('Trie traversal abstractions', function() {
do {
assert.isNotEmpty(leafChildSequence);
let iter = curChild.traversal().children();
let curr = iter.next();
curChild = curr.value;
let children = curChild.traversal().children();
assert.equal(children.length, 1);
curChild = children[0];
// Test generator behavior - there should be one child, then the 'done' state.
assert.isDefined(curChild);
assert.equal(curChild.char, leafChildSequence[0]);
curr = iter.next();
assert.isTrue(curr.done);
// Prepare for iteration.
leafChildSequence.shift();
@ -337,9 +333,6 @@ describe('Trie traversal abstractions', function() {
// Just to be sure our utility function is working right.
assert.equal(smpA + smpP + 'pl' + smpE, "𝖺𝗉pl𝖾");
let pKeys = ['p', smpP];
let leafChildSequence = ['l', smpE];
const aNode = rootTraversal.child(smpA);
assert.isOk(aNode);
assert.isNotOk(rootTraversal.child('a'));

View file

@ -67,7 +67,7 @@ declare interface LexiconTraversal {
* - If `char` = 'e', the child represents a prefix of 'the'.
* - Then `traversal` allows traversing the part of the lexicon prefixed by 'the'.
*/
children(): Generator<{char: USVString, traversal: () => LexiconTraversal}>;
children(): {char: USVString, p: number, traversal: () => LexiconTraversal}[];
/**
* Allows direct access to the traversal state that results when appending one