diff --git a/Megrez.Tests/MegrezTests.cs b/Megrez.Tests/MegrezTests.cs index 4392172..b77bb4f 100644 --- a/Megrez.Tests/MegrezTests.cs +++ b/Megrez.Tests/MegrezTests.cs @@ -54,8 +54,8 @@ public void Test03_BasicFeaturesOfCompositor() { Assert.AreEqual(actual: compositor.Separator, expected: ""); Assert.AreEqual(actual: compositor.Cursor, expected: 0); Assert.AreEqual(actual: compositor.Length, expected: 0); - - Assert.IsTrue(compositor.InsertKey("a")); + bool foobar = compositor.InsertKey("a"); + Assert.IsTrue(foobar); Assert.AreEqual(actual: compositor.Cursor, expected: 1); Assert.AreEqual(actual: compositor.Length, expected: 1); Assert.AreEqual(actual: compositor.Spans.Count, expected: 1); diff --git a/Megrez/src/0_CSharpExtensions.cs b/Megrez/src/0_CSharpExtensions.cs index 97decb6..0bbc4c7 100644 --- a/Megrez/src/0_CSharpExtensions.cs +++ b/Megrez/src/0_CSharpExtensions.cs @@ -98,7 +98,7 @@ public BRange(int lowerbound, int upperbound) { public List ToList() { List result = new(); - for (int i = Lowerbound; i < Upperbound; i++) { + for (int i = Lowerbound; i <= Upperbound; i++) { result.Add(i); } return result; diff --git a/Megrez/src/1_Compositor.cs b/Megrez/src/1_Compositor.cs index 043f958..d5e0b28 100644 --- a/Megrez/src/1_Compositor.cs +++ b/Megrez/src/1_Compositor.cs @@ -70,15 +70,6 @@ public enum ResizeBehavior { /// public CompositorConfig Config = new(); - private static int _maxSpanLength = 10; - /// - /// 一個幅位單元內所能接受的最長的節點幅位長度。 - /// - public static int MaxSpanLength { - get => _maxSpanLength; - set => _maxSpanLength = Math.Max(6, value); - } - /// /// 公用變數,在生成索引鍵字串時用來分割每個索引鍵單位。最好是鍵盤無法直接敲的 ASCII 字元。 /// @@ -226,7 +217,7 @@ public bool InsertKey(string key) { if (string.IsNullOrEmpty(key) || key == Separator) return false; if (!TheLangModel.HasUnigramsFor(new() { key })) return false; Keys.Insert(Cursor, key); - List gridBackup = Spans; + List gridBackup = Spans.Select(x => x.HardCopy()).ToList(); ResizeGridAt(Cursor, ResizeBehavior.Expand); int nodesInserted = Update(); // 用來在 langModel.HasUnigramsFor() 結果不準確的時候防呆、恢復被搞壞的 Spans。 @@ -351,6 +342,7 @@ public string DumpDOT() { /// 指定是擴張還是縮減一個幅位。 internal void ResizeGridAt(int location, ResizeBehavior action) { location = Math.Max(Math.Min(location, Spans.Count), 0); // 防呆。 + int oldContainerLength = Spans.Count; switch (action) { case ResizeBehavior.Expand: Spans.Insert(location, new()); @@ -361,7 +353,9 @@ internal void ResizeGridAt(int location, ResizeBehavior action) { Spans.RemoveAt(location); break; } - DropWreckedNodesAt(location); + int newContainerLength = Spans.Count; + int sizeDelta = newContainerLength - oldContainerLength; + DropWreckedNodesAt(location, sizeDelta); } /// @@ -397,42 +391,22 @@ internal void ResizeGridAt(int location, ResizeBehavior action) { /// /// /// 給定的幅位座標。 - internal void DropWreckedNodesAt(int location) { + /// 在需要呼叫這個方法時,Grid 的長度變化量(可為負數)。 + internal void DropWreckedNodesAt(int location, int sizeDelta) { + int oldContainerLength = Spans.Count - sizeDelta; + if (oldContainerLength < 0) return; location = Math.Max(Math.Min(location, Spans.Count), 0); // 防呆。 if (Spans.IsEmpty()) return; - int affectedLength = MaxSpanLength - 1; + int affectedLength = oldContainerLength - 1; int begin = Math.Max(0, location - affectedLength); if (location < begin) return; - foreach (int delta in new BRange(begin, location)) { - foreach (int theLength in new BRange(location - delta + 1, MaxSpanLength)) { + foreach (int delta in new BRange(begin, location - 1)) { + foreach (int theLength in new BRange(location - delta + 1, oldContainerLength)) { Spans[delta].Nodes.Remove(theLength); } } } - /// - /// 自索引鍵陣列獲取指定範圍的資料。 - /// - /// 指定範圍。 - /// 拿到的資料。 - private List GetJoinedKeyArray(BRange range) => - range.Upperbound <= Keys.Count && range.Lowerbound >= 0 - ? Keys.GetRange(range.Lowerbound, range.Upperbound - range.Lowerbound).ToList() - : new(); - - /// - /// 在指定位置(以指定索引鍵陣列和指定幅位長度)拿取節點。 - /// - /// 指定游標位置。 - /// 指定幅位長度。 - /// 指定索引鍵陣列。 - /// 拿取的節點。拿不到的話就會是 null。 - private Node? GetNodeAt(int location, int length, List keyArray) { - location = Math.Max(Math.Min(location, Spans.Count - 1), 0); // 防呆。 - return Spans[location].NodeOf(length) is not {} - node ? null : node.KeyArray.SequenceEqual(keyArray) ? node : null; - } - /// /// 根據當前狀況更新整個組字器的節點文脈。 /// @@ -442,19 +416,22 @@ private List GetJoinedKeyArray(BRange range) => /// /// 新增或影響了多少個節點。如果返回「0」則表示可能發生了錯誤。 public int Update(bool updateExisting = false) { - BRange range = new(Math.Max(0, Cursor - MaxSpanLength), Math.Min(Cursor + MaxSpanLength, Keys.Count)); int nodesChanged = 0; - foreach (int position in range) { - foreach (int theLength in new BRange(1, Math.Min(MaxSpanLength, range.Upperbound - position) + 1)) { - List joinedKeyArray = GetJoinedKeyArray(new(position, position + theLength)); - BRange safeLocationRange = new(0, Spans.Count); - Node? theNode = safeLocationRange.Contains(position) ? GetNodeAt(position, theLength, joinedKeyArray) : null; - if (theNode is {}) { + int spansCount = Spans.Count; + if (spansCount < 1) return 0; + foreach (int position in new BRange(0, Spans.Count - 1)) { + int upperBound4Lengths = spansCount - position; + if (upperBound4Lengths < 1) continue; + foreach (int theLength in new BRange(1, upperBound4Lengths)) { + if (!(position + theLength <= Keys.Count && position >= 0)) continue; + List joinedKeyArray = Keys.GetRange(position, theLength); + if (Spans[position].NodeOf(theLength) is {} theNode) { if (!updateExisting) continue; List unigramsA = TheLangModel.UnigramsFor(joinedKeyArray); + // 自動銷毀無效的節點。 if (unigramsA.IsEmpty()) { if (theNode.KeyArray.Count == 1) continue; - Spans[position].Nullify(givenNode: theNode); + Spans[position].Nodes.Remove(theNode.SpanLength); } else { theNode.SyncingUnigramsFrom(unigramsA); } diff --git a/Megrez/src/4_SpanUnit.cs b/Megrez/src/4_SpanUnit.cs index 8363258..4ae2b9f 100644 --- a/Megrez/src/4_SpanUnit.cs +++ b/Megrez/src/4_SpanUnit.cs @@ -86,12 +86,6 @@ public bool Append(Node node) { return true; } - /// - /// 丟掉任何與給定節點完全雷同的節點。 - /// - /// 要參照的節點。 - public void Nullify(Node givenNode) => Nodes.Remove(givenNode.SpanLength); - /// /// 以給定的幅位長度,在當前幅位單元內找出對應的節點。 /// @@ -143,7 +137,7 @@ public List FetchOverlappingNodesAt(int givenLocation) { } // 再獲取以當前位置結尾或開頭的節點。 - int begin = givenLocation - Math.Min(givenLocation, MaxSpanLength - 1); + int begin = givenLocation - Math.Min(givenLocation, Spans.Count - 1); foreach (int theLocation in new BRange(begin, givenLocation)) { int alpha = givenLocation - theLocation + 1; int bravo = Spans[theLocation].MaxLength;