Pre Merge pull request !39 from ShikiSuen/upd/1.6.3sp1
This commit is contained in:
commit
1ff5f17b5f
|
@ -108,6 +108,27 @@ class KeyHandler {
|
||||||
|
|
||||||
// MARK: - Functions dealing with Megrez.
|
// MARK: - Functions dealing with Megrez.
|
||||||
|
|
||||||
|
func fixNodeIfNecessary() {
|
||||||
|
let width = _builder.grid.width
|
||||||
|
if width > kMaxComposingBufferNeedsToWalkSize {
|
||||||
|
var index = 0
|
||||||
|
for anchor in _walkedNodes {
|
||||||
|
if index >= (width - kMaxComposingBufferNeedsToWalkSize) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
guard let node = anchor.node else { continue }
|
||||||
|
if node.score < node.kSelectedCandidateScore {
|
||||||
|
let candidate = node.currentKeyValue.value
|
||||||
|
_builder.grid.fixNodeSelectedCandidate(
|
||||||
|
location: index + anchor.spanningLength,
|
||||||
|
value: candidate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
index += anchor.spanningLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func walk() {
|
func walk() {
|
||||||
// Retrieve the most likely grid, i.e. a Maximum Likelihood Estimation
|
// Retrieve the most likely grid, i.e. a Maximum Likelihood Estimation
|
||||||
// of the best possible Mandarin characters given the input syllables,
|
// of the best possible Mandarin characters given the input syllables,
|
||||||
|
|
|
@ -190,6 +190,9 @@ extension KeyHandler {
|
||||||
// ... get and tweak override model suggestion if possible...
|
// ... get and tweak override model suggestion if possible...
|
||||||
dealWithOverrideModelSuggestions()
|
dealWithOverrideModelSuggestions()
|
||||||
|
|
||||||
|
// ... and fix nodes if necessary...
|
||||||
|
fixNodeIfNecessary()
|
||||||
|
|
||||||
// ... then update the text.
|
// ... then update the text.
|
||||||
_composer.clear()
|
_composer.clear()
|
||||||
|
|
||||||
|
|
|
@ -147,12 +147,101 @@ extension Megrez {
|
||||||
joinedPhrase: String = "",
|
joinedPhrase: String = "",
|
||||||
longPhrases: [String] = .init()
|
longPhrases: [String] = .init()
|
||||||
) -> [NodeAnchor] {
|
) -> [NodeAnchor] {
|
||||||
let newLocation = (mutGrid.width) - abs(location) // 防呆
|
let location = abs(location) // 防呆
|
||||||
return Array(
|
if location >= mutGrid.width {
|
||||||
reverseWalk(
|
return .init()
|
||||||
at: newLocation, score: accumulatedScore,
|
}
|
||||||
joinedPhrase: joinedPhrase, longPhrases: longPhrases
|
|
||||||
).reversed())
|
var paths = [[NodeAnchor]]()
|
||||||
|
var nodes = mutGrid.nodesBeginningAt(location: location)
|
||||||
|
|
||||||
|
nodes = nodes.stableSorted {
|
||||||
|
$0.scoreForSort > $1.scoreForSort
|
||||||
|
}
|
||||||
|
|
||||||
|
if let nodeOfNodeZero = nodes[0].node, nodeOfNodeZero.score >= nodeOfNodeZero.kSelectedCandidateScore {
|
||||||
|
// 在使用者有選過候選字詞的情況下,摒棄非依此據而成的節點路徑。
|
||||||
|
var nodeZero = nodes[0]
|
||||||
|
nodeZero.accumulatedScore = accumulatedScore + nodeOfNodeZero.score
|
||||||
|
var path: [NodeAnchor] = walk(at: location + nodeZero.spanningLength, score: nodeZero.accumulatedScore)
|
||||||
|
path.insert(nodeZero, at: 0)
|
||||||
|
paths.append(path)
|
||||||
|
} else if !longPhrases.isEmpty {
|
||||||
|
var path = [NodeAnchor]()
|
||||||
|
for theAnchor in nodes {
|
||||||
|
guard let theNode = theAnchor.node else { continue }
|
||||||
|
var theAnchor = theAnchor
|
||||||
|
let joinedValue = joinedPhrase + theNode.currentKeyValue.value
|
||||||
|
// 如果只是一堆單漢字的節點組成了同樣的長詞的話,直接棄用這個節點路徑。
|
||||||
|
// 打比方說「八/月/中/秋/山/林/涼」與「八月/中秋/山林/涼」在使用者來看
|
||||||
|
// 是「結果等價」的,那就扔掉前者。
|
||||||
|
if longPhrases.contains(joinedValue) {
|
||||||
|
theAnchor.accumulatedScore = kDroppedPathScore
|
||||||
|
path.insert(theAnchor, at: 0)
|
||||||
|
paths.append(path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
theAnchor.accumulatedScore = accumulatedScore + theNode.score
|
||||||
|
if joinedValue.count >= longPhrases[0].count {
|
||||||
|
path = walk(
|
||||||
|
at: location + theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: "",
|
||||||
|
longPhrases: .init()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
path = walk(
|
||||||
|
at: location + theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: joinedValue,
|
||||||
|
longPhrases: longPhrases
|
||||||
|
)
|
||||||
|
}
|
||||||
|
path.insert(theAnchor, at: 0)
|
||||||
|
paths.append(path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 看看當前格位有沒有更長的候選字詞。
|
||||||
|
var longPhrases = [String]()
|
||||||
|
for theAnchor in nodes {
|
||||||
|
guard let theNode = theAnchor.node else { continue }
|
||||||
|
if theAnchor.spanningLength > 1 {
|
||||||
|
longPhrases.append(theNode.currentKeyValue.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
longPhrases = longPhrases.stableSorted {
|
||||||
|
$0.count > $1.count
|
||||||
|
}
|
||||||
|
for theAnchor in nodes {
|
||||||
|
var theAnchor = theAnchor
|
||||||
|
guard let theNode = theAnchor.node else { continue }
|
||||||
|
theAnchor.accumulatedScore = accumulatedScore + theNode.score
|
||||||
|
var path = [NodeAnchor]()
|
||||||
|
if theAnchor.spanningLength > 1 {
|
||||||
|
path = walk(
|
||||||
|
at: location + theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: "",
|
||||||
|
longPhrases: .init()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
path = walk(
|
||||||
|
at: location + 1, score: theAnchor.accumulatedScore,
|
||||||
|
joinedPhrase: theNode.currentKeyValue.value, longPhrases: longPhrases
|
||||||
|
)
|
||||||
|
}
|
||||||
|
path.insert(theAnchor, at: 0)
|
||||||
|
paths.append(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard !paths.isEmpty else {
|
||||||
|
return .init()
|
||||||
|
}
|
||||||
|
|
||||||
|
var result: [NodeAnchor] = paths[0]
|
||||||
|
for neta in paths {
|
||||||
|
if neta.last!.accumulatedScore > result.last!.accumulatedScore {
|
||||||
|
result = neta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 對已給定的軌格按照給定的位置與條件進行反向爬軌。
|
/// 對已給定的軌格按照給定的位置與條件進行反向爬軌。
|
||||||
|
|
|
@ -189,14 +189,43 @@ extension Megrez {
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 獲取給定的範圍內的節點,以節錨為單位來獲取一個結果陣列。
|
||||||
|
/// - Parameters:
|
||||||
|
/// - range: 給定首尾範圍,必須得是閉區間。
|
||||||
|
func nodes(in range: ClosedRange<Int>) -> [NodeAnchor] {
|
||||||
|
var result = [NodeAnchor]()
|
||||||
|
if !mutSpans.isEmpty, range.upperBound <= mutSpans.count {
|
||||||
|
for i in 0..<range.upperBound {
|
||||||
|
let span = mutSpans[i]
|
||||||
|
if i + span.maximumLength > range.lowerBound {
|
||||||
|
for j in 1...span.maximumLength {
|
||||||
|
guard let np = span.node(length: j),
|
||||||
|
(i + j) > range.lowerBound
|
||||||
|
else { continue }
|
||||||
|
result.append(
|
||||||
|
NodeAnchor(
|
||||||
|
node: np,
|
||||||
|
location: i,
|
||||||
|
spanningLength: j
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
/// 將給定位置的節點的候選字詞改為與給定的字串一致的候選字詞。
|
/// 將給定位置的節點的候選字詞改為與給定的字串一致的候選字詞。
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - location: 位置。
|
/// - location: 位置。
|
||||||
/// - value: 給定字串。
|
/// - value: 給定字串。
|
||||||
@discardableResult public func fixNodeSelectedCandidate(location: Int, value: String) -> NodeAnchor {
|
@discardableResult public func fixNodeSelectedCandidate(location: Int, value: String) -> NodeAnchor {
|
||||||
let location = abs(location) // 防呆
|
let location = abs(location) // 防呆
|
||||||
var node = NodeAnchor()
|
var anchor = NodeAnchor()
|
||||||
for nodeAnchor in nodesCrossingOrEndingAt(location: location) {
|
var selectedIndex = 0
|
||||||
|
var anchors = nodesCrossingOrEndingAt(location: location)
|
||||||
|
for nodeAnchor in anchors {
|
||||||
guard let theNode = nodeAnchor.node else {
|
guard let theNode = nodeAnchor.node else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -205,13 +234,24 @@ extension Megrez {
|
||||||
theNode.resetCandidate()
|
theNode.resetCandidate()
|
||||||
for (i, candidate) in candidates.enumerated() {
|
for (i, candidate) in candidates.enumerated() {
|
||||||
if candidate.value == value {
|
if candidate.value == value {
|
||||||
theNode.selectCandidateAt(index: i)
|
selectedIndex = i
|
||||||
node = nodeAnchor
|
anchor = nodeAnchor
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return node
|
guard let node = anchor.node else {
|
||||||
|
return anchor
|
||||||
|
}
|
||||||
|
anchors = nodes(in: (location - anchor.spanningLength)...location)
|
||||||
|
for nodeAnchor in anchors {
|
||||||
|
if let theNode = nodeAnchor.node {
|
||||||
|
theNode.resetCandidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.selectCandidateAt(index: selectedIndex)
|
||||||
|
anchor.node = node
|
||||||
|
return anchor
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 將給定位置的節點的與給定的字串一致的候選字詞的權重複寫為給定權重數值。
|
/// 將給定位置的節點的與給定的字串一致的候選字詞的權重複寫為給定權重數值。
|
||||||
|
@ -221,7 +261,10 @@ extension Megrez {
|
||||||
/// - overridingScore: 給定權重數值。
|
/// - overridingScore: 給定權重數值。
|
||||||
public func overrideNodeScoreForSelectedCandidate(location: Int, value: String, overridingScore: Double) {
|
public func overrideNodeScoreForSelectedCandidate(location: Int, value: String, overridingScore: Double) {
|
||||||
let location = abs(location) // 防呆
|
let location = abs(location) // 防呆
|
||||||
for nodeAnchor in nodesCrossingOrEndingAt(location: location) {
|
let anchor = NodeAnchor()
|
||||||
|
let selectedIndex = 0
|
||||||
|
var anchors = nodesCrossingOrEndingAt(location: location)
|
||||||
|
for nodeAnchor in anchors {
|
||||||
guard let theNode = nodeAnchor.node else {
|
guard let theNode = nodeAnchor.node else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -235,6 +278,14 @@ extension Megrez {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
guard let node = anchor.node else { return }
|
||||||
|
anchors = nodes(in: (location - anchor.spanningLength)...location)
|
||||||
|
for nodeAnchor in anchors {
|
||||||
|
if let theNode = nodeAnchor.node {
|
||||||
|
theNode.resetCandidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.selectFloatingCandidateAt(index: selectedIndex, score: overridingScore)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue