diff --git a/Source/Modules/LangModelRelated/LMInstantiator.swift b/Source/Modules/LangModelRelated/LMInstantiator.swift index cab0dd7c..2f67c887 100644 --- a/Source/Modules/LangModelRelated/LMInstantiator.swift +++ b/Source/Modules/LangModelRelated/LMInstantiator.swift @@ -197,7 +197,7 @@ extension vChewing { if key == " " { /// 給空格鍵指定輸出值。 let spaceUnigram = Megrez.Unigram( - keyValue: Megrez.KeyValuePair(key: " ", value: " "), + keyValue: Megrez.KeyValuePaired(key: " ", value: " "), score: 0 ) return [spaceUnigram] @@ -225,7 +225,7 @@ extension vChewing { } // 準備過濾清單。因為我們在 Swift 使用 NSOrderedSet,所以就不需要統計清單了。 - var filteredPairs: Set = [] + var filteredPairs: Set = [] // 載入要過濾的 KeyValuePair 清單。 for unigram in lmFiltered.unigramsFor(key: key) { @@ -268,12 +268,12 @@ extension vChewing { /// - Returns: 經過語彙過濾處理+置換處理+去重複處理的單元圖結果陣列。 func filterAndTransform( unigrams: [Megrez.Unigram], - filter filteredPairs: Set + filter filteredPairs: Set ) -> [Megrez.Unigram] { var results: [Megrez.Unigram] = [] - var insertedPairs: Set = [] + var insertedPairs: Set = [] for unigram in unigrams { - var pair: Megrez.KeyValuePair = unigram.keyValue + var pair: Megrez.KeyValuePaired = unigram.keyValue if filteredPairs.contains(pair) { continue } if isPhraseReplacementEnabled { let replacement = lmReplacements.valuesFor(key: pair.value) diff --git a/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift b/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift index 438b4da5..07170818 100644 --- a/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift +++ b/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift @@ -151,7 +151,7 @@ extension vChewing { for netaRange in arrRangeRecords { let neta = strData[netaRange].split(separator: " ") let theValue: String = shouldReverse ? String(neta[0]) : String(neta[1]) - let kvPair = Megrez.KeyValuePair(key: key, value: theValue) + let kvPair = Megrez.KeyValuePaired(key: key, value: theValue) var theScore = defaultScore if neta.count >= 3, !shouldForceDefaultScore { theScore = .init(String(neta[2])) ?? defaultScore diff --git a/Source/Modules/LangModelRelated/SubLMs/lmCoreNS.swift b/Source/Modules/LangModelRelated/SubLMs/lmCoreNS.swift index 9fdd0575..c1f93e7f 100644 --- a/Source/Modules/LangModelRelated/SubLMs/lmCoreNS.swift +++ b/Source/Modules/LangModelRelated/SubLMs/lmCoreNS.swift @@ -146,7 +146,7 @@ extension vChewing { let strNetaSet = String(decoding: netaSet, as: UTF8.self) let neta = Array(strNetaSet.split(separator: " ").reversed()) let theValue: String = .init(neta[0]) - let kvPair = Megrez.KeyValuePair(key: key, value: theValue) + let kvPair = Megrez.KeyValuePaired(key: key, value: theValue) var theScore = defaultScore if neta.count >= 2, !shouldForceDefaultScore { theScore = .init(String(neta[1])) ?? defaultScore diff --git a/Source/Modules/LanguageParsers/Megrez/1_Compositor.swift b/Source/Modules/LanguageParsers/Megrez/1_Compositor.swift index 9a1cb525..72a3284b 100644 --- a/Source/Modules/LanguageParsers/Megrez/1_Compositor.swift +++ b/Source/Modules/LanguageParsers/Megrez/1_Compositor.swift @@ -35,7 +35,7 @@ extension Megrez { /// 該組字器的軌格。 private var mutGrid: Grid = .init() /// 該組字器所使用的語言模型。 - private var mutLM: LanguageModel + private var mutLM: LanguageModelProtocol /// 公開:該組字器內可以允許的最大詞長。 public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength } @@ -62,7 +62,7 @@ extension Megrez { /// - lm: 語言模型。可以是任何基於 Megrez.LanguageModel 的衍生型別。 /// - length: 指定該組字器內可以允許的最大詞長,預設為 10 字。 /// - separator: 多字讀音鍵當中用以分割漢字讀音的記號,預設為空。 - public init(lm: LanguageModel, length: Int = 10, separator: String = "") { + public init(lm: LanguageModelProtocol, length: Int = 10, separator: String = "") { mutLM = lm mutGrid = .init(spanLength: abs(length)) // 防呆 joinSeparator = separator @@ -140,8 +140,8 @@ extension Megrez { /// 對已給定的軌格按照給定的位置與條件進行正向爬軌。 /// - Parameters: - /// - at: 開始爬軌的位置。 - /// - score: 給定累計權重,非必填參數。預設值為 0。 + /// - location: 開始爬軌的位置。 + /// - accumulatedScore: 給定累計權重,非必填參數。預設值為 0。 /// - joinedPhrase: 用以統計累計長詞的內部參數,請勿主動使用。 /// - longPhrases: 用以統計累計長詞的內部參數,請勿主動使用。 public func walk( @@ -160,8 +160,8 @@ extension Megrez { /// 對已給定的軌格按照給定的位置與條件進行反向爬軌。 /// - Parameters: - /// - at: 開始爬軌的位置。 - /// - score: 給定累計權重,非必填參數。預設值為 0。 + /// - location: 開始爬軌的位置。 + /// - accumulatedScore: 給定累計權重,非必填參數。預設值為 0。 /// - joinedPhrase: 用以統計累計長詞的內部參數,請勿主動使用。 /// - longPhrases: 用以統計累計長詞的內部參數,請勿主動使用。 public func reverseWalk( @@ -219,11 +219,9 @@ extension Megrez { } else { // 看看當前格位有沒有更長的候選字詞。 var longPhrases = [String]() - for theAnchor in nodes { + for theAnchor in nodes.lazy.filter({ $0.spanningLength > 1 }) { guard let theNode = theAnchor.node else { continue } - if theAnchor.spanningLength > 1 { - longPhrases.append(theNode.currentKeyValue.value) - } + longPhrases.append(theNode.currentKeyValue.value) } longPhrases = longPhrases.stableSorted { @@ -249,10 +247,10 @@ extension Megrez { } var result: [NodeAnchor] = paths[0] - for neta in paths { - if neta.last!.accumulatedScore > result.last!.accumulatedScore { - result = neta - } + for neta in paths.lazy.filter({ + $0.last!.accumulatedScore > result.last!.accumulatedScore + }) { + result = neta } return result @@ -267,29 +265,20 @@ extension Megrez { for p in itrBegin.. itrEnd { - break - } + if p + q > itrEnd { break } let arrSlice = mutReadings[p..<(p + q)] let combinedReading: String = join(slice: arrSlice, separator: joinSeparator) - - if !mutGrid.hasMatchedNode(location: p, spanningLength: q, key: combinedReading) { - let unigrams: [Unigram] = mutLM.unigramsFor(key: combinedReading) - if !unigrams.isEmpty { - let n = Node(key: combinedReading, unigrams: unigrams) - mutGrid.insertNode(node: n, location: p, spanningLength: q) - } - } + if mutGrid.hasMatchedNode(location: p, spanningLength: q, key: combinedReading) { continue } + let unigrams: [Unigram] = mutLM.unigramsFor(key: combinedReading) + if unigrams.isEmpty { continue } + let n = Node(key: combinedReading, unigrams: unigrams) + mutGrid.insertNode(node: n, location: p, spanningLength: q) } } } private func join(slice arrSlice: ArraySlice, separator: String) -> String { - var arrResult: [String] = [] - for value in arrSlice { - arrResult.append(value) - } - return arrResult.joined(separator: separator) + arrSlice.joined(separator: separator) } } } @@ -303,7 +292,7 @@ extension Sequence { /// /// - Parameter areInIncreasingOrder: Return nil when two element are equal. /// - Returns: The sorted collection. - func stableSorted( + fileprivate func stableSorted( by areInIncreasingOrder: (Element, Element) throws -> Bool ) rethrows -> [Element] diff --git a/Source/Modules/LanguageParsers/Megrez/2_Grid.swift b/Source/Modules/LanguageParsers/Megrez/2_Grid.swift index cddc6b71..fcb7697a 100644 --- a/Source/Modules/LanguageParsers/Megrez/2_Grid.swift +++ b/Source/Modules/LanguageParsers/Megrez/2_Grid.swift @@ -91,11 +91,10 @@ extension Megrez { public func expandGridByOneAt(location: Int) { let location = abs(location) // 防呆 mutSpans.insert(Span(), at: location) - if location != 0, location != mutSpans.count { - for i in 0.. [NodeAnchor] { let location = abs(location) // 防呆 var results = [NodeAnchor]() - if location < mutSpans.count { // 此時 mutSpans 必然不為空 - let span = mutSpans[location] - for i in 1...maxBuildSpanLength { - if let np = span.node(length: i) { - results.append( - NodeAnchor( - node: np, - location: location, - spanningLength: i - ) + if location >= mutSpans.count { return results } + // 此時 mutSpans 必然不為空,因為 location 不可能小於 0。 + let span = mutSpans[location] + for i in 1...maxBuildSpanLength { + if let np = span.node(length: i) { + results.append( + .init( + node: np, + location: location, + spanningLength: i ) - } + ) } } return results @@ -144,20 +143,18 @@ extension Megrez { public func nodesEndingAt(location: Int) -> [NodeAnchor] { let location = abs(location) // 防呆 var results = [NodeAnchor]() - if !mutSpans.isEmpty, location <= mutSpans.count { - for i in 0..= location { - if let np = span.node(length: location - i) { - results.append( - NodeAnchor( - node: np, - location: i, - spanningLength: location - i - ) - ) - } - } + if mutSpans.isEmpty || location > mutSpans.count { return results } + for i in 0.. [NodeAnchor] { let location = abs(location) // 防呆 var results = [NodeAnchor]() - if !mutSpans.isEmpty, location <= mutSpans.count { - for i in 0..= location { - for j in 1...span.maximumLength { - if i + j < location { - continue - } - if let np = span.node(length: j) { - results.append( - NodeAnchor( - node: np, - location: i, - spanningLength: location - i - ) - ) - } - } + if mutSpans.isEmpty || location > mutSpans.count { return results } + for i in 0.. Node? { - mutLengthNodeMap[abs(length)] // 防呆 + // 防呆 Abs() + mutLengthNodeMap.keys.contains(abs(length)) ? mutLengthNodeMap[abs(length)] : nil } } } diff --git a/Source/Modules/LanguageParsers/Megrez/4_Node.swift b/Source/Modules/LanguageParsers/Megrez/4_Node.swift index db978782..1672b2b9 100644 --- a/Source/Modules/LanguageParsers/Megrez/4_Node.swift +++ b/Source/Modules/LanguageParsers/Megrez/4_Node.swift @@ -35,11 +35,11 @@ extension Megrez { /// 雙元圖陣列。 private var mutBigrams: [Bigram] /// 候選字詞陣列,以鍵值陣列的形式存在。 - private var mutCandidates: [KeyValuePair] = [] + private var mutCandidates: [KeyValuePaired] = [] /// 專門「用單元圖資料值來調查索引值」的辭典。 private var mutValueUnigramIndexMap: [String: Int] = [:] /// 專門「用給定鍵值來取對應的雙元圖陣列」的辭典。 - private var mutPrecedingBigramMap: [KeyValuePair: [Megrez.Bigram]] = [:] + private var mutPrecedingBigramMap: [KeyValuePaired: [Megrez.Bigram]] = [:] /// 狀態標記變數,用來記載當前節點是否處於候選字詞鎖定狀態。 private var mutCandidateFixed: Bool = false /// 用來登記「當前選中的單元圖」的索引值的變數。 @@ -52,7 +52,7 @@ extension Megrez { } /// 公開:候選字詞陣列(唯讀),以鍵值陣列的形式存在。 - public var candidates: [KeyValuePair] { mutCandidates } + public var candidates: [KeyValuePaired] { mutCandidates } /// 公開:用來登記「當前選中的單元圖」的索引值的變數(唯讀)。 public var isCandidateFixed: Bool { mutCandidateFixed } @@ -61,8 +61,8 @@ extension Megrez { /// 公開:當前節點的當前被選中的候選字詞「在該節點內的」目前的權重(唯讀)。 public var score: Double { mutScore } /// 公開:當前被選中的候選字詞的鍵值配對。 - public var currentKeyValue: KeyValuePair { - mutSelectedUnigramIndex >= mutUnigrams.count ? KeyValuePair() : mutCandidates[mutSelectedUnigramIndex] + public var currentKeyValue: KeyValuePaired { + mutSelectedUnigramIndex >= mutUnigrams.count ? KeyValuePaired() : mutCandidates[mutSelectedUnigramIndex] } /// 公開:給出當前單元圖陣列內最高的權重數值。 @@ -91,7 +91,9 @@ extension Megrez { mutCandidates.append(gram.keyValue) } - for gram in bigrams { + for gram in bigrams.lazy.filter({ [self] in + mutPrecedingBigramMap.keys.contains($0.precedingKeyValue) + }) { mutPrecedingBigramMap[gram.precedingKeyValue]?.append(gram) } } @@ -99,19 +101,18 @@ extension Megrez { /// 對擁有「給定的前述鍵值陣列」的節點提權。 /// - Parameters: /// - precedingKeyValues: 前述鍵值陣列。 - public func primeNodeWith(precedingKeyValues: [KeyValuePair]) { + public func primeNodeWith(precedingKeyValues: [KeyValuePaired]) { var newIndex = mutSelectedUnigramIndex var max = mutScore if !isCandidateFixed { for neta in precedingKeyValues { let bigrams = mutPrecedingBigramMap[neta] ?? [] - for bigram in bigrams { - guard bigram.score > max else { continue } - if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] { - newIndex = valRetrieved as Int - max = bigram.score - } + for bigram in bigrams.lazy.filter({ [self] in + $0.score > max && mutValueUnigramIndexMap.keys.contains($0.keyValue.value) + }) { + newIndex = mutValueUnigramIndexMap[bigram.keyValue.value] ?? newIndex + max = bigram.score } } } @@ -154,10 +155,8 @@ extension Megrez { /// - Parameters: /// - candidate: 給定的候選字詞字串。 public func scoreFor(candidate: String) -> Double { - for unigram in mutUnigrams { - if unigram.keyValue.value == candidate { - return unigram.score - } + for unigram in mutUnigrams.lazy.filter({ $0.keyValue.value == candidate }) { + return unigram.score } return 0.0 } diff --git a/Source/Modules/LanguageParsers/Megrez/5_LanguageModel.swift b/Source/Modules/LanguageParsers/Megrez/5_LanguageModel.swift index 4636ded3..abe8c822 100644 --- a/Source/Modules/LanguageParsers/Megrez/5_LanguageModel.swift +++ b/Source/Modules/LanguageParsers/Megrez/5_LanguageModel.swift @@ -23,9 +23,20 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +public protocol LanguageModelProtocol { + /// 給定鍵,讓語言模型找給一組單元圖陣列。 + func unigramsFor(key: String) -> [Megrez.Unigram] + + /// 給定當前鍵與前述鍵,讓語言模型找給一組雙元圖陣列。 + func bigramsForKeys(precedingKey: String, key: String) -> [Megrez.Bigram] + + /// 給定鍵,確認是否有單元圖記錄在庫。 + func hasUnigramsFor(key: String) -> Bool +} + extension Megrez { /// 語言模型框架,回頭實際使用時需要派生一個型別、且重寫相關函式。 - open class LanguageModel { + open class LanguageModel: LanguageModelProtocol { public init() {} // 這裡寫了一點假內容,不然有些 Swift 格式化工具會破壞掉函式的參數設計。 diff --git a/Source/Modules/LanguageParsers/Megrez/6_Bigram.swift b/Source/Modules/LanguageParsers/Megrez/6_Bigram.swift index cca1069f..b64e2658 100644 --- a/Source/Modules/LanguageParsers/Megrez/6_Bigram.swift +++ b/Source/Modules/LanguageParsers/Megrez/6_Bigram.swift @@ -27,9 +27,9 @@ extension Megrez { /// 雙元圖。 @frozen public struct Bigram: Equatable, CustomStringConvertible { /// 當前鍵值。 - public var keyValue: KeyValuePair + public var keyValue: KeyValuePaired /// 前述鍵值。 - public var precedingKeyValue: KeyValuePair + public var precedingKeyValue: KeyValuePaired /// 權重。 public var score: Double /// 將當前雙元圖列印成一個字串。 @@ -42,7 +42,7 @@ extension Megrez { /// - precedingKeyValue: 前述鍵值。 /// - keyValue: 當前鍵值。 /// - score: 權重(雙精度小數)。 - public init(precedingKeyValue: KeyValuePair, keyValue: KeyValuePair, score: Double) { + public init(precedingKeyValue: KeyValuePaired, keyValue: KeyValuePaired, score: Double) { self.keyValue = keyValue self.precedingKeyValue = precedingKeyValue self.score = score diff --git a/Source/Modules/LanguageParsers/Megrez/6_Unigram.swift b/Source/Modules/LanguageParsers/Megrez/6_Unigram.swift index ba47b85d..4bcd894e 100644 --- a/Source/Modules/LanguageParsers/Megrez/6_Unigram.swift +++ b/Source/Modules/LanguageParsers/Megrez/6_Unigram.swift @@ -27,7 +27,7 @@ extension Megrez { /// 單元圖。 @frozen public struct Unigram: Equatable, CustomStringConvertible { /// 鍵值。 - public var keyValue: KeyValuePair + public var keyValue: KeyValuePaired /// 權重。 public var score: Double /// 將當前單元圖列印成一個字串。 @@ -39,7 +39,7 @@ extension Megrez { /// - Parameters: /// - keyValue: 鍵值。 /// - score: 權重(雙精度小數)。 - public init(keyValue: KeyValuePair, score: Double) { + public init(keyValue: KeyValuePaired, score: Double) { self.keyValue = keyValue self.score = score } diff --git a/Source/Modules/LanguageParsers/Megrez/7_KeyValuePair.swift b/Source/Modules/LanguageParsers/Megrez/7_KeyValuePaired.swift similarity index 73% rename from Source/Modules/LanguageParsers/Megrez/7_KeyValuePair.swift rename to Source/Modules/LanguageParsers/Megrez/7_KeyValuePaired.swift index d7ffce8e..3e9dee80 100644 --- a/Source/Modules/LanguageParsers/Megrez/7_KeyValuePair.swift +++ b/Source/Modules/LanguageParsers/Megrez/7_KeyValuePaired.swift @@ -25,15 +25,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. extension Megrez { /// 鍵值配對。 - @frozen public struct KeyValuePair: Equatable, Hashable, Comparable, CustomStringConvertible { + @frozen public struct KeyValuePaired: Equatable, Hashable, Comparable, CustomStringConvertible { /// 鍵。一般情況下用來放置讀音等可以用來作為索引的內容。 public var key: String /// 資料值。 public var value: String /// 將當前鍵值列印成一個字串。 - public var description: String { - "(" + key + "," + value + ")" - } + public var description: String { "(" + key + "," + value + ")" } + /// 判斷當前鍵值配對是否合規。如果鍵與值有任一為空,則結果為 false。 + public var isValid: Bool { !key.isEmpty && !value.isEmpty } + /// 將當前鍵值列印成一個字串,但如果該鍵值配對為空的話則僅列印「()」。 + public var toNGramKey: String { !isValid ? "()" : "(" + key + "," + value + ")" } /// 初期化一組鍵值配對。 /// - Parameters: @@ -49,23 +51,23 @@ extension Megrez { hasher.combine(value) } - public static func == (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool { + public static func == (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool { lhs.key.count == rhs.key.count && lhs.value == rhs.value } - public static func < (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool { + public static func < (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool { (lhs.key.count < rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value < rhs.value) } - public static func > (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool { + public static func > (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool { (lhs.key.count > rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value > rhs.value) } - public static func <= (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool { + public static func <= (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool { (lhs.key.count <= rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value <= rhs.value) } - public static func >= (lhs: KeyValuePair, rhs: KeyValuePair) -> Bool { + public static func >= (lhs: KeyValuePaired, rhs: KeyValuePaired) -> Bool { (lhs.key.count >= rhs.key.count) || (lhs.key.count == rhs.key.count && lhs.value >= rhs.value) } } diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index b77c9f52..90825203 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -12,7 +12,7 @@ 5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */; }; 5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */; }; 5B38F59A281E2E49007D5F5D /* 6_Unigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1D15FC0EB100ABF4B3 /* 6_Unigram.swift */; }; - 5B38F59B281E2E49007D5F5D /* 7_KeyValuePair.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */; }; + 5B38F59B281E2E49007D5F5D /* 7_KeyValuePaired.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePaired.swift */; }; 5B38F59C281E2E49007D5F5D /* 2_Grid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */; }; 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 */; }; @@ -287,7 +287,7 @@ 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; }; + 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePaired.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 7_KeyValuePaired.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 5_LanguageModel.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 4_Node.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 3_NodeAnchor.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; @@ -769,7 +769,7 @@ 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */, 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */, 6A0D4F1D15FC0EB100ABF4B3 /* 6_Unigram.swift */, - 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */, + 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePaired.swift */, ); path = Megrez; sourceTree = ""; @@ -1084,7 +1084,7 @@ 5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */, 5B54E743283A7D89001ECBDC /* lmCoreNS.swift in Sources */, 5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */, - 5B38F59B281E2E49007D5F5D /* 7_KeyValuePair.swift in Sources */, + 5B38F59B281E2E49007D5F5D /* 7_KeyValuePaired.swift in Sources */, 5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */, 5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */, 5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */,