Megrez // Upgrade to v1.2.1 release.
This commit is contained in:
parent
4b83af377d
commit
f4b164b05c
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
/// 給定節點長度,獲取節點。
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = "<group>"; };
|
||||
6A0D4EF615FC0DA600ABF4B3 /* vChewing-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "vChewing-Prefix.pch"; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
|
||||
6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 6_Bigram.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_BlockReadingBuilder.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_Compositor.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = 0_Megrez.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 2_Grid.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 7_KeyValuePair.swift; sourceTree = "<group>"; 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;
|
||||
|
|
Loading…
Reference in New Issue