diff --git a/Source/Modules/ControllerModules/KeyHandler_Core.swift b/Source/Modules/ControllerModules/KeyHandler_Core.swift index 519c8b30..f29c4ec8 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Core.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Core.swift @@ -65,8 +65,10 @@ class KeyHandler { // Synchronize the sub-languageModel state settings to the new LM. syncBaseLMPrefs() - // Create new grid builder and clear the composer. - createNewBuilder() + // Create new compositor and clear the composer. + // When it recreates, it adapts to the latest imeMode settings. + // This allows it to work with correct LMs. + reinitCompositor() composer.clear() } } @@ -139,7 +141,7 @@ class KeyHandler { } func fixNode(value: String, respectCursorPushing: Bool = true) { - let cursorIndex = min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength) + let cursorIndex = min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength) compositor.grid.fixNodeSelectedCandidate(location: cursorIndex, value: value) // // 因半衰模組失能,故禁用之。 // let selectedNode: Megrez.NodeAnchor = compositor.grid.fixNodeSelectedCandidate( @@ -180,8 +182,8 @@ class KeyHandler { if nextPosition >= cursorIndex { break } nextPosition += node.spanningLength } - if nextPosition <= builderLength { - builderCursorIndex = nextPosition + if nextPosition <= compositorLength { + compositorCursorIndex = nextPosition } } } @@ -216,7 +218,7 @@ class KeyHandler { mgrPrefs.useSCPCTypingMode ? "" : currentUOM.suggest( - walkedNodes: walkedAnchors, cursorIndex: builderCursorIndex, + walkedNodes: walkedAnchors, cursorIndex: compositorCursorIndex, timestamp: NSDate().timeIntervalSince1970 ) @@ -224,7 +226,7 @@ class KeyHandler { IME.prtDebugIntel( "UOM: Suggestion retrieved, overriding the node score of the selected candidate.") compositor.grid.overrideNodeScoreForSelectedCandidate( - location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength), + location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength), value: overrideValue, overridingScore: findHighestScore(nodes: rawNodes, epsilon: kEpsilon) ) @@ -285,7 +287,7 @@ class KeyHandler { // MARK: - Extracted methods and functions (Megrez). - var isBuilderEmpty: Bool { compositor.grid.width == 0 } + var isCompositorEmpty: Bool { compositor.grid.width == 0 } var rawNodes: [Megrez.NodeAnchor] { /// 警告:不要對游標前置風格使用 nodesCrossing,否則會導致游標行為與 macOS 內建注音輸入法不一致。 @@ -301,7 +303,7 @@ class KeyHandler { currentLM.isSymbolEnabled = mgrPrefs.symbolInputEnabled } - func createNewBuilder() { + func reinitCompositor() { // Each Mandarin syllable is separated by a hyphen. compositor = Megrez.Compositor(lm: currentLM, separator: "-") } @@ -312,16 +314,16 @@ class KeyHandler { currentLM.hasUnigramsFor(key: reading) } - func insertReadingToBuilderAtCursor(reading: String) { + func insertToCompositorAtCursor(reading: String) { compositor.insertReadingAtCursor(reading: reading) } - var builderCursorIndex: Int { + var compositorCursorIndex: Int { get { compositor.cursorIndex } set { compositor.cursorIndex = newValue } } - var builderLength: Int { + var compositorLength: Int { compositor.length } diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift index 19447499..affc11f8 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift @@ -52,12 +52,12 @@ extension KeyHandler { if cancelCandidateKey { if (state is InputState.AssociatedPhrases) || mgrPrefs.useSCPCTypingMode - || isBuilderEmpty + || isCompositorEmpty { // 如果此時發現當前組字緩衝區為真空的情況的話, // 就將當前的組字緩衝區析構處理、強制重設輸入狀態。 // 否則,一個本不該出現的真空組字緩衝區會使前後方向鍵與 BackSpace 鍵失靈。 - // 所以這裡需要對 isBuilderEmpty 做判定。 + // 所以這裡需要對 isCompositorEmpty 做判定。 clear() stateCallback(InputState.EmptyIgnoringPreviousState()) } else { diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift index 18d797e8..e9137323 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift @@ -151,7 +151,7 @@ extension KeyHandler { keyConsumedByReading = true // If we have a tone marker, we have to insert the reading to the - // builder in other words, if we don't have a tone marker, we just + // compositor in other words, if we don't have a tone marker, we just // update the composing buffer. let composeReading = composer.hasToneMarker() if !composeReading { @@ -177,12 +177,12 @@ extension KeyHandler { IME.prtDebugIntel("B49C0979:語彙庫內無「\(reading)」的匹配記錄。") errorCallback() composer.clear() - stateCallback((builderLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState) + stateCallback((compositorLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState) return true } // ... and insert it into the grid... - insertReadingToBuilderAtCursor(reading: reading) + insertToCompositorAtCursor(reading: reading) // ... then walk the grid... let poppedText = popOverflowComposingTextAndWalk @@ -248,7 +248,7 @@ extension KeyHandler { if input.isSpace { // If the Space key is NOT set to be a selection key if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace { - if builderCursorIndex >= builderLength { + if compositorCursorIndex >= compositorLength { let composingBuffer = currentState.composingBuffer if !composingBuffer.isEmpty { stateCallback(InputState.Committing(poppedText: composingBuffer)) @@ -257,7 +257,7 @@ extension KeyHandler { stateCallback(InputState.Committing(poppedText: " ")) stateCallback(InputState.Empty()) } else if ifLangModelHasUnigrams(forKey: " ") { - insertReadingToBuilderAtCursor(reading: " ") + insertToCompositorAtCursor(reading: " ") let poppedText = popOverflowComposingTextAndWalk let inputting = buildInputtingState inputting.poppedText = poppedText @@ -363,7 +363,7 @@ extension KeyHandler { if input.isOptionHold { if ifLangModelHasUnigrams(forKey: "_punctuation_list") { if composer.isEmpty { - insertReadingToBuilderAtCursor(reading: "_punctuation_list") + insertToCompositorAtCursor(reading: "_punctuation_list") let poppedText: String! = popOverflowComposingTextAndWalk let inputting = buildInputtingState inputting.poppedText = poppedText diff --git a/Source/Modules/ControllerModules/KeyHandler_Misc.swift b/Source/Modules/ControllerModules/KeyHandler_Misc.swift index e3e0b1a6..c2d39f8b 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Misc.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Misc.swift @@ -34,7 +34,7 @@ extension KeyHandler { } var actualCandidateCursorIndex: Int { - var cursorIndex = builderCursorIndex + var cursorIndex = compositorCursorIndex switch mgrPrefs.useRearCursorMode { case false: do { @@ -52,7 +52,7 @@ extension KeyHandler { // (i.e. the cursor is always at the rear of the phrase.) // No crossing. switch cursorIndex { - case builderLength: cursorIndex -= 1 + case compositorLength: cursorIndex -= 1 default: break } } diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index e0b76cca..039772e2 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -48,44 +48,44 @@ extension KeyHandler { let arrSplit: [String] = Array(strNodeValue).map { String($0) } let codepointCount = arrSplit.count // This re-aligns the cursor index in the composed string - // (the actual cursor on the screen) with the builder's logical + // (the actual cursor on the screen) with the compositor's logical // cursor (reading) cursor; each built node has a "spanning length" // (e.g. two reading blocks has a spanning length of 2), and we // accumulate those lengths to calculate the displayed cursor // index. let spanningLength: Int = walkedNode.spanningLength - if readingCursorIndex + spanningLength <= builderCursorIndex { + if readingCursorIndex + spanningLength <= compositorCursorIndex { composedStringCursorIndex += strNodeValue.utf16.count readingCursorIndex += spanningLength } else { if codepointCount == spanningLength { var i = 0 - while i < codepointCount, readingCursorIndex < builderCursorIndex { + while i < codepointCount, readingCursorIndex < compositorCursorIndex { composedStringCursorIndex += arrSplit[i].utf16.count readingCursorIndex += 1 i += 1 } } else { - if readingCursorIndex < builderCursorIndex { + if readingCursorIndex < compositorCursorIndex { composedStringCursorIndex += strNodeValue.utf16.count readingCursorIndex += spanningLength - if readingCursorIndex > builderCursorIndex { - readingCursorIndex = builderCursorIndex + if readingCursorIndex > compositorCursorIndex { + readingCursorIndex = compositorCursorIndex } // Now we start preparing the contents of the tooltips used // in cases of moving cursors across certain emojis which emoji // char count is inequal to the reading count. // Example in McBopomofo: Typing 王建民 (3 readings) gets a tree emoji. // Example in vChewing: Typing 義麵 (2 readings) gets a pasta emoji. - switch builderCursorIndex { + switch compositorCursorIndex { case compositor.readings.count...: tooltipParameterRef[0] = compositor.readings[compositor.readings.count - 1] case 0: - tooltipParameterRef[1] = compositor.readings[builderCursorIndex] + tooltipParameterRef[1] = compositor.readings[compositorCursorIndex] default: do { - tooltipParameterRef[0] = compositor.readings[builderCursorIndex - 1] - tooltipParameterRef[1] = compositor.readings[builderCursorIndex] + tooltipParameterRef[0] = compositor.readings[compositorCursorIndex - 1] + tooltipParameterRef[1] = compositor.readings[compositorCursorIndex] } } } @@ -260,7 +260,7 @@ extension KeyHandler { } if composer.isEmpty { - insertReadingToBuilderAtCursor(reading: customPunctuation) + insertToCompositorAtCursor(reading: customPunctuation) let poppedText = popOverflowComposingTextAndWalk let inputting = buildInputtingState inputting.poppedText = poppedText @@ -385,7 +385,7 @@ extension KeyHandler { if composer.hasToneMarker(withNothingElse: true) { composer.clear() } else if composer.isEmpty { - if builderCursorIndex >= 0 { + if compositorCursorIndex >= 0 { deleteBuilderReadingInFrontOfCursor() walk() } else { @@ -398,7 +398,7 @@ extension KeyHandler { composer.doBackSpace() } - if composer.isEmpty, builderLength == 0 { + if composer.isEmpty, compositorLength == 0 { stateCallback(InputState.EmptyIgnoringPreviousState()) } else { stateCallback(buildInputtingState) @@ -416,7 +416,7 @@ extension KeyHandler { guard state is InputState.Inputting else { return false } if composer.isEmpty { - if builderCursorIndex != builderLength { + if compositorCursorIndex != compositorLength { deleteBuilderReadingToTheFrontOfCursor() walk() let inputting = buildInputtingState @@ -472,8 +472,8 @@ extension KeyHandler { return true } - if builderCursorIndex != 0 { - builderCursorIndex = 0 + if compositorCursorIndex != 0 { + compositorCursorIndex = 0 stateCallback(buildInputtingState) } else { IME.prtDebugIntel("66D97F90") @@ -500,8 +500,8 @@ extension KeyHandler { return true } - if builderCursorIndex != builderLength { - builderCursorIndex = builderLength + if compositorCursorIndex != compositorLength { + compositorCursorIndex = compositorLength stateCallback(buildInputtingState) } else { IME.prtDebugIntel("9B69908E") @@ -534,7 +534,7 @@ extension KeyHandler { // If reading is not empty, we cancel the reading. if !composer.isEmpty { composer.clear() - if builderLength == 0 { + if compositorLength == 0 { stateCallback(InputState.EmptyIgnoringPreviousState()) } else { stateCallback(buildInputtingState) @@ -580,8 +580,8 @@ extension KeyHandler { stateCallback(state) } } else { - if builderCursorIndex < builderLength { - builderCursorIndex += 1 + if compositorCursorIndex < compositorLength { + compositorCursorIndex += 1 stateCallback(buildInputtingState) } else { IME.prtDebugIntel("A96AAD58") @@ -629,8 +629,8 @@ extension KeyHandler { stateCallback(state) } } else { - if builderCursorIndex > 0 { - builderCursorIndex -= 1 + if compositorCursorIndex > 0 { + compositorCursorIndex -= 1 stateCallback(buildInputtingState) } else { IME.prtDebugIntel("7045E6F3") @@ -677,7 +677,7 @@ extension KeyHandler { var length = 0 var currentAnchor = Megrez.NodeAnchor() let cursorIndex = min( - actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength + actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength ) for anchor in walkedAnchors { length += anchor.spanningLength diff --git a/Source/Modules/IMEModules/mgrPrefs.swift b/Source/Modules/IMEModules/mgrPrefs.swift index 5247e038..28c7adc1 100644 --- a/Source/Modules/IMEModules/mgrPrefs.swift +++ b/Source/Modules/IMEModules/mgrPrefs.swift @@ -78,10 +78,10 @@ private let kMinCandidateListTextSize: CGFloat = 12 private let kMaxCandidateListTextSize: CGFloat = 196 // default, min and max composing buffer size (in codepoints) -// modern Macs can usually work up to 16 codepoints when the builder still +// modern Macs can usually work up to 16 codepoints when the compositor still // walks the grid with good performance slower Macs (like old PowerBooks) // will start to sputter beyond 12 such is the algorithmatic complexity -// of the Viterbi algorithm used in the builder library (at O(N^2)) +// of the Viterbi algorithm used in the Megrez library (at O(N^2)) private let kDefaultComposingBufferSize = 20 private let kMinComposingBufferSize = 10 private let kMaxComposingBufferSize = 40 diff --git a/Source/Modules/LangModelRelated/LMInstantiator.swift b/Source/Modules/LangModelRelated/LMInstantiator.swift index 6e3bfa41..906d9332 100644 --- a/Source/Modules/LangModelRelated/LMInstantiator.swift +++ b/Source/Modules/LangModelRelated/LMInstantiator.swift @@ -42,8 +42,8 @@ extension vChewing { /// LMInstantiator is a facade for managing a set of models including /// the input method language model, user phrases and excluded phrases. /// - /// It is the primary model class that the input controller and grammar builder - /// of vChewing talks to. When the grammar builder starts to build a sentence + /// It is the primary model class that the input controller and input compositor + /// of vChewing talks to. When the input compositor starts to build a sentence /// from a series of BPMF readings, it passes the readings to the model to see /// if there are valid unigrams, and use returned unigrams to produce the final /// results. diff --git a/Source/Modules/LanguageParsers/Megrez/1_BlockReadingBuilder.swift b/Source/Modules/LanguageParsers/Megrez/1_Compositor.swift similarity index 82% rename from Source/Modules/LanguageParsers/Megrez/1_BlockReadingBuilder.swift rename to Source/Modules/LanguageParsers/Megrez/1_Compositor.swift index c8f5f1e4..0749d667 100644 --- a/Source/Modules/LanguageParsers/Megrez/1_BlockReadingBuilder.swift +++ b/Source/Modules/LanguageParsers/Megrez/1_Compositor.swift @@ -24,40 +24,43 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ extension Megrez { - /// 分節讀音槽。 - public class BlockReadingBuilder { + /// 組字器。 + public class Compositor { /// 給被丟掉的節點路徑施加的負權重。 private let kDroppedPathScore: Double = -999 - /// 該分節讀音槽的游標位置。 + /// 該組字器的游標位置。 private var mutCursorIndex: Int = 0 - /// 該分節讀音槽的讀音陣列。 + /// 該組字器的讀音陣列。 private var mutReadings: [String] = [] - /// 該分節讀音槽的軌格。 + /// 該組字器的軌格。 private var mutGrid: Grid = .init() - /// 該分節讀音槽所使用的語言模型。 + /// 該組字器所使用的語言模型。 private var mutLM: LanguageModel - /// 公開該分節讀音槽內可以允許的最大詞長。 + /// 公開該組字器內可以允許的最大詞長。 public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength } /// 公開:多字讀音鍵當中用以分割漢字讀音的記號,預設為空。 public var joinSeparator: String = "" - /// 公開:該分節讀音槽的游標位置。 + /// 公開:該組字器的游標位置。 public var cursorIndex: Int { get { mutCursorIndex } set { mutCursorIndex = (newValue < 0) ? 0 : min(newValue, mutReadings.count) } } - /// 公開:該分節讀音槽的軌格(唯讀)。 + /// 公開:該組字器是否為空。 + public var isEmpty: Bool { grid.isEmpty } + + /// 公開:該組字器的軌格(唯讀)。 public var grid: Grid { mutGrid } - /// 公開:該分節讀音槽的長度,也就是內建漢字讀音的數量(唯讀)。 + /// 公開:該組字器的長度,也就是內建漢字讀音的數量(唯讀)。 public var length: Int { mutReadings.count } - /// 公開:該分節讀音槽的讀音陣列(唯讀)。 + /// 公開:該組字器的讀音陣列(唯讀)。 public var readings: [String] { mutReadings } - /// 分節讀音槽。 + /// 組字器。 /// - Parameters: /// - lm: 語言模型。可以是任何基於 Megrez.LanguageModel 的衍生型別。 - /// - length: 指定該分節讀音槽內可以允許的最大詞長,預設為 10 字。 + /// - length: 指定該組字器內可以允許的最大詞長,預設為 10 字。 /// - separator: 多字讀音鍵當中用以分割漢字讀音的記號,預設為空。 public init(lm: LanguageModel, length: Int = 10, separator: String = "") { mutLM = lm @@ -65,7 +68,7 @@ extension Megrez { joinSeparator = separator } - /// 分節讀音槽自我清空專用函數。 + /// 組字器自我清空專用函數。 public func clear() { mutCursorIndex = 0 mutReadings.removeAll() @@ -109,7 +112,7 @@ extension Megrez { return true } - /// 移除該分節讀音槽的第一個讀音單元。 + /// 移除該組字器的第一個讀音單元。 /// /// 用於輸入法組字區長度上限處理: /// 將該位置要溢出的敲字內容遞交之後、再執行這個函數。 @@ -179,12 +182,14 @@ extension Megrez { $0.scoreForSort > $1.scoreForSort } - if let nodeOfNodeZero = nodes[0].node, nodeOfNodeZero.score >= nodeOfNodeZero.kSelectedCandidateScore { + if let nodeZero = nodes[0].node, nodeZero.score >= nodeZero.kSelectedCandidateScore { // 在使用者有選過候選字詞的情況下,摒棄非依此據而成的節點路徑。 - var nodeZero = nodes[0] - nodeZero.accumulatedScore = accumulatedScore + nodeOfNodeZero.score - var path: [NodeAnchor] = reverseWalk(at: location - nodeZero.spanningLength, score: nodeZero.accumulatedScore) - path.insert(nodeZero, at: 0) + var anchorZero = nodes[0] + anchorZero.accumulatedScore = accumulatedScore + nodeZero.score + var path: [NodeAnchor] = reverseWalk( + at: location - anchorZero.spanningLength, score: anchorZero.accumulatedScore + ) + path.insert(anchorZero, at: 0) paths.append(path) } else if !longPhrases.isEmpty { var path = [NodeAnchor]() @@ -202,17 +207,12 @@ extension Megrez { continue } theAnchor.accumulatedScore = accumulatedScore + theNode.score - if joinedValue.count >= longPhrases[0].count { - path = reverseWalk( - at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: "", - longPhrases: .init() - ) - } else { - path = reverseWalk( - at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: joinedValue, - longPhrases: longPhrases - ) - } + path = reverseWalk( + at: location - theAnchor.spanningLength, + score: theAnchor.accumulatedScore, + joinedPhrase: (joinedValue.count >= longPhrases[0].count) ? "" : joinedValue, + longPhrases: .init() + ) path.insert(theAnchor, at: 0) paths.append(path) } @@ -234,17 +234,11 @@ extension Megrez { guard let theNode = theAnchor.node else { continue } theAnchor.accumulatedScore = accumulatedScore + theNode.score var path = [NodeAnchor]() - if theAnchor.spanningLength > 1 { - path = reverseWalk( - at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: "", - longPhrases: .init() - ) - } else { - path = reverseWalk( - at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, - joinedPhrase: theNode.currentKeyValue.value, longPhrases: longPhrases - ) - } + path = reverseWalk( + at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, + joinedPhrase: (theAnchor.spanningLength > 1) ? "" : theNode.currentKeyValue.value, + longPhrases: .init() + ) path.insert(theAnchor, at: 0) paths.append(path) } diff --git a/Source/Modules/LanguageParsers/Megrez/2_Grid.swift b/Source/Modules/LanguageParsers/Megrez/2_Grid.swift index 3eec69ba..8b1b82b8 100644 --- a/Source/Modules/LanguageParsers/Megrez/2_Grid.swift +++ b/Source/Modules/LanguageParsers/Megrez/2_Grid.swift @@ -38,6 +38,9 @@ extension Megrez { /// 軌格的寬度,也就是其內的幅位陣列當中的幅位數量。 var width: Int { mutSpans.count } + /// 軌格是否為空。 + var isEmpty: Bool { mutSpans.isEmpty } + public init(spanLength: Int = 10) { mutMaxBuildSpanLength = spanLength mutSpans = [Megrez.Span]() @@ -256,9 +259,9 @@ extension Megrez.Grid { strOutput += "\(np.currentKeyValue.value);\n" if (p + ni) < mutSpans.count { - let destinatedSpan = mutSpans[p + ni] - for q in 0...(destinatedSpan.maximumLength) { - if let dn = destinatedSpan.node(length: q) { + let destinationSpan = mutSpans[p + ni] + for q in 0...(destinationSpan.maximumLength) { + if let dn = destinationSpan.node(length: q) { strOutput += np.currentKeyValue.value + " -> " + dn.currentKeyValue.value + ";\n" } } diff --git a/Source/Modules/LanguageParsers/Megrez/3_Span.swift b/Source/Modules/LanguageParsers/Megrez/3_Span.swift index 6ea9d45a..9d000575 100644 --- a/Source/Modules/LanguageParsers/Megrez/3_Span.swift +++ b/Source/Modules/LanguageParsers/Megrez/3_Span.swift @@ -49,9 +49,7 @@ extension Megrez { mutating func insert(node: Node, length: Int) { let length = abs(length) // 防呆 mutLengthNodeMap[length] = node - if length > mutMaximumLength { - mutMaximumLength = length - } + mutMaximumLength = max(mutMaximumLength, length) } /// 移除任何比給定的長度更長的節點。 @@ -60,21 +58,19 @@ extension Megrez { mutating func removeNodeOfLengthGreaterThan(_ length: Int) { let length = abs(length) // 防呆 if length > mutMaximumLength { return } - var max = 0 + var lenMax = 0 var removalList: [Int: Megrez.Node] = [:] for key in mutLengthNodeMap.keys { if key > length { removalList[key] = mutLengthNodeMap[key] } else { - if key > max { - max = key - } + lenMax = max(lenMax, key) } } for key in removalList.keys { mutLengthNodeMap.removeValue(forKey: key) } - mutMaximumLength = max + mutMaximumLength = lenMax } /// 給定節點長度,獲取節點。 diff --git a/Source/Modules/LanguageParsers/Megrez/4_Node.swift b/Source/Modules/LanguageParsers/Megrez/4_Node.swift index 813cc30c..4f86ad46 100644 --- a/Source/Modules/LanguageParsers/Megrez/4_Node.swift +++ b/Source/Modules/LanguageParsers/Megrez/4_Node.swift @@ -25,7 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. extension Megrez { /// 節點。 - public class Node: CustomStringConvertible { + public class Node { /// 當前節點對應的語言模型。 private let mutLM: LanguageModel = .init() /// 鍵。 @@ -109,23 +109,16 @@ extension Megrez { for neta in precedingKeyValues { let bigrams = mutPrecedingBigramMap[neta] ?? [] for bigram in bigrams { - if bigram.score > max { - if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] { - newIndex = valRetrieved as Int - max = bigram.score - } + guard bigram.score > max else { continue } + if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] { + newIndex = valRetrieved as Int + max = bigram.score } } } } - - if mutScore != max { - mutScore = max - } - - if mutSelectedUnigramIndex != newIndex { - mutSelectedUnigramIndex = newIndex - } + mutScore = max + mutSelectedUnigramIndex = newIndex } /// 選中位於給定索引位置的候選字詞。 @@ -170,13 +163,5 @@ extension Megrez { } return 0.0 } - - public static func == (lhs: Node, rhs: Node) -> Bool { - lhs.mutUnigrams == rhs.mutUnigrams && lhs.mutCandidates == rhs.mutCandidates - && lhs.mutValueUnigramIndexMap == rhs.mutValueUnigramIndexMap - && lhs.mutPrecedingBigramMap == rhs.mutPrecedingBigramMap - && lhs.mutCandidateFixed == rhs.mutCandidateFixed - && lhs.mutSelectedUnigramIndex == rhs.mutSelectedUnigramIndex - } } } diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 825eaa84..7ad62bf7 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -17,7 +17,7 @@ 5B38F59D281E2E49007D5F5D /* 4_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */; }; 5B38F59E281E2E49007D5F5D /* 6_Bigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */; }; 5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */; }; - 5B38F5A1281E2E49007D5F5D /* 1_BlockReadingBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */; }; + 5B38F5A1281E2E49007D5F5D /* 1_Compositor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */; }; 5B38F5A2281E2E49007D5F5D /* 0_Megrez.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */; }; 5B38F5A3281E2E49007D5F5D /* 3_Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */; }; 5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */; }; @@ -286,7 +286,7 @@ 6A0D4EF515FC0DA600ABF4B3 /* IME-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IME-Info.plist"; sourceTree = ""; }; 6A0D4EF615FC0DA600ABF4B3 /* vChewing-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "vChewing-Prefix.pch"; sourceTree = ""; tabWidth = 4; usesTabs = 0; }; 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 6_Bigram.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; - 6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_BlockReadingBuilder.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; + 6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_Compositor.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = 0_Megrez.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 2_Grid.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 7_KeyValuePair.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; @@ -764,7 +764,7 @@ isa = PBXGroup; children = ( 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */, - 6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */, + 6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */, 6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */, 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */, 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */, @@ -1117,7 +1117,7 @@ 5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */, 5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */, 5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */, - 5B38F5A1281E2E49007D5F5D /* 1_BlockReadingBuilder.swift in Sources */, + 5B38F5A1281E2E49007D5F5D /* 1_Compositor.swift in Sources */, 5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0;