LM // Swiftify: LMUserOverride - phase 2.
This commit is contained in:
parent
8722e45dd8
commit
b21a25649c
|
@ -28,11 +28,52 @@ import Foundation
|
||||||
|
|
||||||
extension vChewing {
|
extension vChewing {
|
||||||
public class LMUserOverride {
|
public class LMUserOverride {
|
||||||
|
|
||||||
|
// MARK: - Private Structures
|
||||||
|
|
||||||
|
var mutCapacity: Int
|
||||||
|
var mutDecayExponent: Double
|
||||||
|
var mutLRUList = [KeyObservationPair]()
|
||||||
|
var mutLRUMap: [String: KeyObservationPair] = [:]
|
||||||
let kDecayThreshold: Double = 1.0 / 1_048_576.0
|
let kDecayThreshold: Double = 1.0 / 1_048_576.0
|
||||||
|
|
||||||
public init(capacity: Int = 0, decayExponent: Double = 0) {
|
struct Override {
|
||||||
mutCapacity = capacity
|
var count: Int = 0
|
||||||
mutDecayExponent = decayExponent
|
var timestamp: Double = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Observation {
|
||||||
|
var count: Int = 0
|
||||||
|
var overrides: [String: Override] = [:]
|
||||||
|
|
||||||
|
mutating func update(candidate: String, timestamp: Double) {
|
||||||
|
count += 1
|
||||||
|
if var neta = overrides[candidate] {
|
||||||
|
neta.timestamp = timestamp
|
||||||
|
neta.count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct KeyObservationPair: Equatable {
|
||||||
|
var key: String
|
||||||
|
var observation: Observation
|
||||||
|
|
||||||
|
var hashValue: Int { key.hashValue }
|
||||||
|
|
||||||
|
init(key: String, observation: Observation) {
|
||||||
|
self.key = key
|
||||||
|
self.observation = observation
|
||||||
|
}
|
||||||
|
|
||||||
|
static func == (lhs: KeyObservationPair, rhs: KeyObservationPair) -> Bool {
|
||||||
|
lhs.key == rhs.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(capacity: Int = 500, decayConstant: Double = 5400.0) {
|
||||||
|
mutCapacity = abs(capacity) // Ensures that this value is always > 0.
|
||||||
|
mutDecayExponent = log(0.5) / decayConstant
|
||||||
}
|
}
|
||||||
|
|
||||||
public func observe(
|
public func observe(
|
||||||
|
@ -42,25 +83,24 @@ extension vChewing {
|
||||||
timestamp: Double
|
timestamp: Double
|
||||||
) {
|
) {
|
||||||
let key = getWalkedNodesToKey(walkedNodes: walkedNodes, cursorIndex: cursorIndex)
|
let key = getWalkedNodesToKey(walkedNodes: walkedNodes, cursorIndex: cursorIndex)
|
||||||
if mutLRUMap[key] == nil {
|
guard key != "((),(),())" else { return }
|
||||||
let keyValuePair = KeyObservationPair(key: key, observation: Observation())
|
guard let map = mutLRUMap[key] else {
|
||||||
var observation: Observation = keyValuePair.observation
|
var observation: Observation = .init()
|
||||||
observation.update(candidate: candidate, timestamp: timestamp)
|
observation.update(candidate: candidate, timestamp: timestamp)
|
||||||
|
|
||||||
mutLRUList.insert(keyValuePair, at: 0)
|
|
||||||
mutLRUMap[key] = KeyObservationPair(key: key, observation: observation)
|
mutLRUMap[key] = KeyObservationPair(key: key, observation: observation)
|
||||||
|
mutLRUList.insert(KeyObservationPair(key: key, observation: observation), at: 0)
|
||||||
|
|
||||||
if mutLRUList.count > mutCapacity {
|
if mutLRUList.count > mutCapacity {
|
||||||
mutLRUMap[mutLRUList.reversed()[0].key] = nil
|
mutLRUMap[mutLRUList.reversed()[0].key] = nil
|
||||||
mutLRUList.removeLast()
|
mutLRUList.removeLast()
|
||||||
}
|
}
|
||||||
} else {
|
return
|
||||||
var obs = mutLRUMap[key]!.observation
|
}
|
||||||
|
var obs = map.observation
|
||||||
obs.update(candidate: candidate, timestamp: timestamp)
|
obs.update(candidate: candidate, timestamp: timestamp)
|
||||||
let pair = KeyObservationPair.init(key: key, observation: obs)
|
let pair = KeyObservationPair.init(key: key, observation: obs)
|
||||||
mutLRUList.insert(pair, at: 0)
|
mutLRUList.insert(pair, at: 0)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public func suggest(
|
public func suggest(
|
||||||
walkedNodes: [Megrez.NodeAnchor],
|
walkedNodes: [Megrez.NodeAnchor],
|
||||||
|
@ -71,6 +111,12 @@ extension vChewing {
|
||||||
guard let keyValuePair = mutLRUMap[key] else {
|
guard let keyValuePair = mutLRUMap[key] else {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IME.prtDebugIntel("Suggest - A: \(key)")
|
||||||
|
IME.prtDebugIntel("Suggest - B: \(keyValuePair.key)")
|
||||||
|
|
||||||
|
guard key != "((),(),())" else { return "" }
|
||||||
|
|
||||||
let observation = keyValuePair.observation
|
let observation = keyValuePair.observation
|
||||||
|
|
||||||
var candidate = ""
|
var candidate = ""
|
||||||
|
@ -119,104 +165,52 @@ extension vChewing {
|
||||||
func getWalkedNodesToKey(
|
func getWalkedNodesToKey(
|
||||||
walkedNodes: [Megrez.NodeAnchor], cursorIndex: Int
|
walkedNodes: [Megrez.NodeAnchor], cursorIndex: Int
|
||||||
) -> String {
|
) -> String {
|
||||||
var s = ""
|
var strOutput = ""
|
||||||
var n: [Megrez.NodeAnchor] = []
|
var arrNodes: [Megrez.NodeAnchor] = []
|
||||||
var ll = 0
|
var intLength = 0
|
||||||
for i in walkedNodes {
|
for nodeNeta in walkedNodes {
|
||||||
let nn = i
|
arrNodes.append(nodeNeta)
|
||||||
n.append(nn)
|
intLength += nodeNeta.spanningLength
|
||||||
ll += nn.spanningLength
|
if intLength >= cursorIndex {
|
||||||
if ll >= cursorIndex {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var r: [Megrez.NodeAnchor] = []
|
// 一個被 .reversed 過的陣列不能直接使用,因為不是正常的 Swift 陣列。
|
||||||
r.append(contentsOf: n.reversed())
|
// 那就新開一個正常的陣列、然後將內容拓印過去。
|
||||||
|
var arrNodesReversed: [Megrez.NodeAnchor] = []
|
||||||
|
arrNodesReversed.append(contentsOf: arrNodes.reversed())
|
||||||
|
|
||||||
if r.isEmpty {
|
if arrNodesReversed.isEmpty {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if let theAnchor = r.first, theAnchor.node != nil {
|
var strCurrent = "()"
|
||||||
let theNode = theAnchor.node!
|
var strPrev = "()"
|
||||||
let current = theNode.currentKeyValue().key
|
var strAnterior = "()"
|
||||||
r.removeFirst()
|
|
||||||
|
|
||||||
s = "" // 保險起見,這裡也清空 s。
|
for (theIndex, theAnchor) in arrNodesReversed.enumerated() {
|
||||||
if !r.isEmpty {
|
if strCurrent != "()", let nodeCurrent = theAnchor.node {
|
||||||
let value = theNode.currentKeyValue().value
|
let keyCurrent = nodeCurrent.currentKeyValue().key
|
||||||
if isEndingPunctuation(value: value) {
|
let valCurrent = nodeCurrent.currentKeyValue().value
|
||||||
s = "()"
|
strCurrent = "(\(keyCurrent), \(valCurrent))"
|
||||||
r = []
|
if let nodePrev = arrNodesReversed[theIndex + 1].node {
|
||||||
} else {
|
let keyPrev = nodePrev.currentKeyValue().key
|
||||||
s = "(\(theNode.currentKeyValue().key),\(value))"
|
let valPrev = nodePrev.currentKeyValue().value
|
||||||
r.removeFirst()
|
strPrev = "(\(keyPrev), \(valPrev))"
|
||||||
}
|
}
|
||||||
} else {
|
if let nodeAnterior = arrNodesReversed[theIndex + 2].node {
|
||||||
s = "()"
|
let keyAnterior = nodeAnterior.currentKeyValue().key
|
||||||
}
|
let valAnterior = nodeAnterior.currentKeyValue().value
|
||||||
let prev = s
|
strAnterior = "(\(keyAnterior), \(valAnterior))"
|
||||||
|
|
||||||
s = ""
|
|
||||||
if !r.isEmpty {
|
|
||||||
let value = theNode.currentKeyValue().value
|
|
||||||
if isEndingPunctuation(value: value) {
|
|
||||||
s = "()"
|
|
||||||
r = []
|
|
||||||
} else {
|
|
||||||
s = "(\(theNode.currentKeyValue().key),\(value))"
|
|
||||||
r.removeFirst()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s = "()"
|
|
||||||
}
|
|
||||||
let anterior = s
|
|
||||||
|
|
||||||
s = "(\(anterior),\(prev),\(current))"
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private Structures
|
|
||||||
|
|
||||||
var mutCapacity: Int
|
|
||||||
var mutDecayExponent: Double
|
|
||||||
var mutLRUList = [KeyObservationPair]()
|
|
||||||
var mutLRUMap: [String: KeyObservationPair] = [:]
|
|
||||||
|
|
||||||
struct Override {
|
|
||||||
var count: Int = 0
|
|
||||||
var timestamp: Double = 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Observation {
|
|
||||||
var count: Int = 0
|
|
||||||
var overrides: [String: Override] = [:]
|
|
||||||
|
|
||||||
mutating func update(candidate: String, timestamp: Double) {
|
|
||||||
count += 1
|
|
||||||
if var neta = overrides[candidate] {
|
|
||||||
neta.timestamp = timestamp
|
|
||||||
neta.count += 1
|
|
||||||
}
|
}
|
||||||
|
break // 我們只取第一個有效結果。
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KeyObservationPair: Equatable {
|
strOutput = "(\(strAnterior),\(strPrev),\(strCurrent))"
|
||||||
var key: String
|
|
||||||
var observation: Observation
|
|
||||||
|
|
||||||
var hashValue: Int { key.hashValue }
|
return strOutput
|
||||||
|
|
||||||
init(key: String, observation: Observation) {
|
|
||||||
self.key = key
|
|
||||||
self.observation = observation
|
|
||||||
}
|
|
||||||
|
|
||||||
static func == (lhs: KeyObservationPair, rhs: KeyObservationPair) -> Bool {
|
|
||||||
lhs.key == rhs.key
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,17 +31,10 @@ import Cocoa
|
||||||
/// 同時,這些變數不對外開放任意存取權限。
|
/// 同時,這些變數不對外開放任意存取權限。
|
||||||
/// 我們只在 mgrLangModel 內部寫幾個回傳函數、供其餘控制模組來讀取。
|
/// 我們只在 mgrLangModel 內部寫幾個回傳函數、供其餘控制模組來讀取。
|
||||||
|
|
||||||
private let kUserOverrideModelCapacity: Int = 500
|
|
||||||
private let kObservedOverrideHalflife: Double = 5400.0
|
|
||||||
|
|
||||||
private var gLangModelCHS = vChewing.LMInstantiator()
|
private var gLangModelCHS = vChewing.LMInstantiator()
|
||||||
private var gLangModelCHT = vChewing.LMInstantiator()
|
private var gLangModelCHT = vChewing.LMInstantiator()
|
||||||
private var gUserOverrideModelCHS = vChewing.LMUserOverride(
|
private var gUserOverrideModelCHS = vChewing.LMUserOverride()
|
||||||
capacity: kUserOverrideModelCapacity, decayExponent: kObservedOverrideHalflife
|
private var gUserOverrideModelCHT = vChewing.LMUserOverride()
|
||||||
)
|
|
||||||
private var gUserOverrideModelCHT = vChewing.LMUserOverride(
|
|
||||||
capacity: kUserOverrideModelCapacity, decayExponent: kObservedOverrideHalflife
|
|
||||||
)
|
|
||||||
|
|
||||||
class mgrLangModel: NSObject {
|
class mgrLangModel: NSObject {
|
||||||
/// 寫幾個回傳函數、供其餘控制模組來讀取那些被設為 fileprivate 的器外變數。
|
/// 寫幾個回傳函數、供其餘控制模組來讀取那些被設為 fileprivate 的器外變數。
|
||||||
|
|
Loading…
Reference in New Issue