1
0
mirror of https://github.com/chylex/Query.git synced 2025-04-10 10:15:46 +02:00

Optimize looking up units by a sequence of words

This commit is contained in:
chylex 2024-08-07 02:59:54 +02:00
parent 1954157c97
commit f79dff1cfb
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
4 changed files with 70 additions and 40 deletions

View File

@ -1,17 +1,8 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.Immutable;
namespace Calculator.Math;
public sealed record Unit(string ShortName, ImmutableArray<string> LongNames) {
internal void AssignNamesTo(Dictionary<string, Unit> nameToUnitDictionary) {
nameToUnitDictionary.Add(ShortName, this);
foreach (string longName in LongNames) {
nameToUnitDictionary.Add(longName, this);
}
}
public override string ToString() {
return ShortName;
}

View File

@ -10,7 +10,6 @@ using ExtendedNumerics;
namespace Calculator.Math;
sealed class UnitUniverse(
Unit primaryUnit,
FrozenDictionary<Unit, Func<Number, Number>> unitToConversionToPrimaryUnit,
FrozenDictionary<Unit, Func<Number, Number>> unitToConversionFromPrimaryUnit
) {
@ -61,9 +60,9 @@ sealed class UnitUniverse(
}
internal sealed class Builder {
private readonly Unit primaryUnit;
private readonly Dictionary<Unit, Func<Number, Number>> unitToConversionToPrimaryUnit = new (ReferenceEqualityComparer.Instance);
private readonly Dictionary<Unit, Func<Number, Number>> unitToConversionFromPrimaryUnit = new (ReferenceEqualityComparer.Instance);
private readonly Unit primaryUnit;
public Builder(Unit primaryUnit) {
this.primaryUnit = primaryUnit;
@ -113,7 +112,6 @@ sealed class UnitUniverse(
public UnitUniverse Build() {
return new UnitUniverse(
primaryUnit,
unitToConversionToPrimaryUnit.ToFrozenDictionary(ReferenceEqualityComparer.Instance),
unitToConversionFromPrimaryUnit.ToFrozenDictionary(ReferenceEqualityComparer.Instance)
);

View File

@ -1,4 +1,5 @@
using System.Collections.Frozen;
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@ -6,28 +7,72 @@ namespace Calculator.Math;
sealed class UnitUniverses {
private readonly FrozenDictionary<Unit, UnitUniverse> unitToUniverse;
private readonly FrozenDictionary<string, Unit> nameToUnit;
public WordLookupTrieNode UnitLookupByWords { get; }
internal UnitUniverses(params UnitUniverse[] universes) {
Dictionary<Unit, UnitUniverse> unitToUniverseBuilder = new (ReferenceEqualityComparer.Instance);
Dictionary<string, Unit> nameToUnitBuilder = new ();
WordLookupTrieNode.Builder unitLookupByWordsBuilder = new ();
foreach (UnitUniverse universe in universes) {
foreach (Unit unit in universe.AllUnits) {
unitToUniverseBuilder.Add(unit, universe);
unit.AssignNamesTo(nameToUnitBuilder);
unitLookupByWordsBuilder.Add(unit);
}
}
unitToUniverse = unitToUniverseBuilder.ToFrozenDictionary(ReferenceEqualityComparer.Instance);
nameToUnit = nameToUnitBuilder.ToFrozenDictionary();
}
public bool TryGetUnit(string name, [NotNullWhen(true)] out Unit? unit) {
return nameToUnit.TryGetValue(name, out unit);
UnitLookupByWords = unitLookupByWordsBuilder.Build();
}
public bool TryGetUniverse(Unit unit, [NotNullWhen(true)] out UnitUniverse? universe) {
return unitToUniverse.TryGetValue(unit, out universe);
}
public sealed record WordLookupTrieNode(Unit? Unit, FrozenDictionary<string, WordLookupTrieNode> Children) {
internal sealed class Builder {
private sealed record Node(Unit? Unit, Dictionary<string, Node> Children) {
internal static Node Create(Unit? unit = null) {
return new Node(unit, new Dictionary<string, Node>());
}
internal Node Child(string word) {
if (!Children.TryGetValue(word, out Node? child)) {
Children.Add(word, child = Create());
}
return child;
}
}
private readonly Node root = Node.Create();
public void Add(Unit unit) {
Add(unit.ShortName, unit);
foreach (string longName in unit.LongNames) {
Add(longName, unit);
}
}
private void Add(string name, Unit unit) {
Node node = root;
string[] words = name.Split(' ');
foreach (string word in words.AsSpan(..^1)) {
node = node.Child(word);
}
node.Children.Add(words[^1], Node.Create(unit));
}
public WordLookupTrieNode Build() {
return Build(root);
}
private WordLookupTrieNode Build(Node node) {
return new WordLookupTrieNode(node.Unit, node.Children.ToFrozenDictionary(static kvp => kvp.Key, kvp => Build(kvp.Value)));
}
}
}
}

View File

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Calculator.Math;
namespace Calculator.Parser;
@ -189,25 +187,23 @@ public sealed class Parser(ImmutableArray<Token> tokens) {
private bool MatchUnit([NotNullWhen(true)] out Unit? unit) {
int position = current;
List<string> words = [];
while (Match(out Token.Text? text)) {
words.Add(text.Value);
UnitUniverses.WordLookupTrieNode node = Units.All.UnitLookupByWords;
// ReSharper disable once AccessToModifiedClosure
while (Match(token => node.Children.ContainsKey(token.Value), out Token.Text? text)) {
node = node.Children[text.Value];
}
for (int i = words.Count; i > 0; i--) {
string unitName = string.Join(' ', words.Take(i));
unit = node.Unit;
if (Units.All.TryGetUnit(unitName, out unit)) {
current = position + i;
return true;
}
if (unit != null) {
return true;
}
else {
current = position;
return false;
}
current = position;
unit = null;
return false;
}
private bool MatchUnitConversionOperator() {