Megrez v1.1.8 // Add nodesBeginningAt().
This commit is contained in:
parent
69be62bb69
commit
87e39bf943
|
@ -26,8 +26,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
extension Megrez {
|
extension Megrez {
|
||||||
/// 分節讀音槽。
|
/// 分節讀音槽。
|
||||||
public class BlockReadingBuilder {
|
public class BlockReadingBuilder {
|
||||||
/// 該分節讀音曹內可以允許的最大詞長。
|
/// 給被丟掉的節點路徑施加的負權重。
|
||||||
private var mutMaximumBuildSpanLength = 10
|
private let kDroppedPathScore: Double = -999
|
||||||
/// 該分節讀音槽的游標位置。
|
/// 該分節讀音槽的游標位置。
|
||||||
private var mutCursorIndex: Int = 0
|
private var mutCursorIndex: Int = 0
|
||||||
/// 該分節讀音槽的讀音陣列。
|
/// 該分節讀音槽的讀音陣列。
|
||||||
|
@ -37,6 +37,8 @@ extension Megrez {
|
||||||
/// 該分節讀音槽所使用的語言模型。
|
/// 該分節讀音槽所使用的語言模型。
|
||||||
private var mutLM: LanguageModel
|
private var mutLM: LanguageModel
|
||||||
|
|
||||||
|
/// 公開該分節讀音槽內可以允許的最大詞長。
|
||||||
|
public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength }
|
||||||
/// 公開:多字讀音鍵當中用以分割漢字讀音的記號,預設為空。
|
/// 公開:多字讀音鍵當中用以分割漢字讀音的記號,預設為空。
|
||||||
public var joinSeparator: String = ""
|
public var joinSeparator: String = ""
|
||||||
/// 公開:該分節讀音槽的游標位置。
|
/// 公開:該分節讀音槽的游標位置。
|
||||||
|
@ -55,11 +57,11 @@ extension Megrez {
|
||||||
/// 分節讀音槽。
|
/// 分節讀音槽。
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - lm: 語言模型。可以是任何基於 Megrez.LanguageModel 的衍生型別。
|
/// - lm: 語言模型。可以是任何基於 Megrez.LanguageModel 的衍生型別。
|
||||||
/// - length: 指定該分節讀音曹內可以允許的最大詞長,預設為 10 字。
|
/// - length: 指定該分節讀音槽內可以允許的最大詞長,預設為 10 字。
|
||||||
/// - separator: 多字讀音鍵當中用以分割漢字讀音的記號,預設為空。
|
/// - separator: 多字讀音鍵當中用以分割漢字讀音的記號,預設為空。
|
||||||
public init(lm: LanguageModel, length: Int = 10, separator: String = "") {
|
public init(lm: LanguageModel, length: Int = 10, separator: String = "") {
|
||||||
mutLM = lm
|
mutLM = lm
|
||||||
mutMaximumBuildSpanLength = length
|
mutGrid = .init(spanLength: abs(length)) // 防呆
|
||||||
joinSeparator = separator
|
joinSeparator = separator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +114,7 @@ extension Megrez {
|
||||||
/// 用於輸入法組字區長度上限處理:
|
/// 用於輸入法組字區長度上限處理:
|
||||||
/// 將該位置要溢出的敲字內容遞交之後、再執行這個函數。
|
/// 將該位置要溢出的敲字內容遞交之後、再執行這個函數。
|
||||||
@discardableResult public func removeHeadReadings(count: Int) -> Bool {
|
@discardableResult public func removeHeadReadings(count: Int) -> Bool {
|
||||||
|
let count = abs(count) // 防呆
|
||||||
if count > length {
|
if count > length {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -120,8 +123,10 @@ extension Megrez {
|
||||||
if mutCursorIndex > 0 {
|
if mutCursorIndex > 0 {
|
||||||
mutCursorIndex -= 1
|
mutCursorIndex -= 1
|
||||||
}
|
}
|
||||||
mutReadings.removeFirst()
|
if !mutReadings.isEmpty {
|
||||||
mutGrid.shrinkGridByOneAt(location: 0)
|
mutReadings.removeFirst()
|
||||||
|
mutGrid.shrinkGridByOneAt(location: 0)
|
||||||
|
}
|
||||||
build()
|
build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,23 +136,22 @@ extension Megrez {
|
||||||
// MARK: - Walker
|
// MARK: - Walker
|
||||||
|
|
||||||
/// 對已給定的軌格按照給定的位置與條件進行正向爬軌。
|
/// 對已給定的軌格按照給定的位置與條件進行正向爬軌。
|
||||||
///
|
|
||||||
/// 其實就是將反向爬軌的結果顛倒順序再給出來而已,省得使用者自己再顛倒一遍。
|
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - at: 開始爬軌的位置。
|
/// - at: 開始爬軌的位置。
|
||||||
/// - score: 給定累計權重,非必填參數。預設值為 0。
|
/// - score: 給定累計權重,非必填參數。預設值為 0。
|
||||||
/// - nodesLimit: 限定最多只爬多少個節點。
|
/// - joinedPhrase: 用以統計累計長詞的內部參數,請勿主動使用。
|
||||||
/// - balanced: 啟用平衡權重,在節點權重的基礎上根據節點幅位長度來加權。
|
/// - longPhrases: 用以統計累計長詞的內部參數,請勿主動使用。
|
||||||
public func walk(
|
public func walk(
|
||||||
at location: Int,
|
at location: Int = 0,
|
||||||
score accumulatedScore: Double = 0.0,
|
score accumulatedScore: Double = 0.0,
|
||||||
nodesLimit: Int = 0,
|
joinedPhrase: String = "",
|
||||||
balanced: Bool = false
|
longPhrases: [String] = .init()
|
||||||
) -> [NodeAnchor] {
|
) -> [NodeAnchor] {
|
||||||
Array(
|
let newLocation = (mutGrid.width) - abs(location) // 防呆
|
||||||
|
return Array(
|
||||||
reverseWalk(
|
reverseWalk(
|
||||||
at: location, score: accumulatedScore,
|
at: newLocation, score: accumulatedScore,
|
||||||
nodesLimit: nodesLimit, balanced: balanced
|
joinedPhrase: joinedPhrase, longPhrases: longPhrases
|
||||||
).reversed())
|
).reversed())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,91 +159,125 @@ extension Megrez {
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - at: 開始爬軌的位置。
|
/// - at: 開始爬軌的位置。
|
||||||
/// - score: 給定累計權重,非必填參數。預設值為 0。
|
/// - score: 給定累計權重,非必填參數。預設值為 0。
|
||||||
/// - nodesLimit: 限定最多只爬多少個節點。
|
/// - joinedPhrase: 用以統計累計長詞的內部參數,請勿主動使用。
|
||||||
/// - balanced: 啟用平衡權重,在節點權重的基礎上根據節點幅位長度來加權。
|
/// - longPhrases: 用以統計累計長詞的內部參數,請勿主動使用。
|
||||||
public func reverseWalk(
|
public func reverseWalk(
|
||||||
at location: Int,
|
at location: Int,
|
||||||
score accumulatedScore: Double = 0.0,
|
score accumulatedScore: Double = 0.0,
|
||||||
nodesLimit: Int = 0,
|
joinedPhrase: String = "",
|
||||||
balanced: Bool = false
|
longPhrases: [String] = .init()
|
||||||
) -> [NodeAnchor] {
|
) -> [NodeAnchor] {
|
||||||
|
let location = abs(location) // 防呆
|
||||||
if location == 0 || location > mutGrid.width {
|
if location == 0 || location > mutGrid.width {
|
||||||
return [] as [NodeAnchor]
|
return .init()
|
||||||
}
|
}
|
||||||
|
|
||||||
var paths: [[NodeAnchor]] = []
|
var paths = [[NodeAnchor]]()
|
||||||
var nodes: [NodeAnchor] = mutGrid.nodesEndingAt(location: location)
|
var nodes = mutGrid.nodesEndingAt(location: location)
|
||||||
|
|
||||||
if balanced {
|
nodes = nodes.stableSorted {
|
||||||
nodes.sort {
|
$0.scoreForSort > $1.scoreForSort
|
||||||
$0.balancedScore > $1.balancedScore
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, n) in nodes.enumerated() {
|
if let nodeOfNodeZero = nodes[0].node, nodeOfNodeZero.score >= nodeOfNodeZero.kSelectedCandidateScore {
|
||||||
// 只檢查前 X 個 NodeAnchor 是否有 node。
|
// 在使用者有選過候選字詞的情況下,摒棄非依此據而成的節點路徑。
|
||||||
// 這裡有 abs 是為了防止有白癡填負數。
|
var nodeZero = nodes[0]
|
||||||
if abs(nodesLimit) > 0, i == abs(nodesLimit) {
|
nodeZero.accumulatedScore = accumulatedScore + nodeOfNodeZero.score
|
||||||
break
|
var path: [NodeAnchor] = reverseWalk(at: location - nodeZero.spanningLength, score: nodeZero.accumulatedScore)
|
||||||
}
|
path.insert(nodeZero, at: 0)
|
||||||
|
|
||||||
var n = n
|
|
||||||
guard let nNode = n.node else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
n.accumulatedScore = accumulatedScore + nNode.score
|
|
||||||
|
|
||||||
// 利用幅位長度來決定權重。
|
|
||||||
// 這樣一來,例:「再見」比「在」與「見」的權重更高。
|
|
||||||
if balanced {
|
|
||||||
n.accumulatedScore += n.additionalWeights
|
|
||||||
}
|
|
||||||
|
|
||||||
var path: [NodeAnchor] = reverseWalk(
|
|
||||||
at: location - n.spanningLength,
|
|
||||||
score: n.accumulatedScore
|
|
||||||
)
|
|
||||||
|
|
||||||
path.insert(n, at: 0)
|
|
||||||
|
|
||||||
paths.append(path)
|
paths.append(path)
|
||||||
|
} else if !longPhrases.isEmpty {
|
||||||
// 始終使用固定的候選字詞
|
var path = [NodeAnchor]()
|
||||||
if balanced, nNode.score >= 0 {
|
for theAnchor in nodes {
|
||||||
break
|
guard let theNode = theAnchor.node else { continue }
|
||||||
}
|
var theAnchor = theAnchor
|
||||||
}
|
let joinedValue = theNode.currentKeyValue.value + joinedPhrase
|
||||||
|
// 如果只是一堆單漢字的節點組成了同樣的長詞的話,直接棄用這個節點路徑。
|
||||||
if !paths.isEmpty {
|
// 打比方說「八/月/中/秋/山/林/涼」與「八月/中秋/山林/涼」在使用者來看
|
||||||
if var result = paths.first {
|
// 是「結果等價」的,那就扔掉前者。
|
||||||
for value in paths {
|
if longPhrases.contains(joinedValue) {
|
||||||
if let vLast = value.last, let rLast = result.last {
|
theAnchor.accumulatedScore = kDroppedPathScore
|
||||||
if vLast.accumulatedScore > rLast.accumulatedScore {
|
path.insert(theAnchor, at: 0)
|
||||||
result = value
|
paths.append(path)
|
||||||
}
|
continue
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result
|
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.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 = 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.insert(theAnchor, at: 0)
|
||||||
|
paths.append(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [] as [NodeAnchor]
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private functions
|
// MARK: - Private functions
|
||||||
|
|
||||||
private func build() {
|
private func build() {
|
||||||
let itrBegin: Int =
|
let itrBegin: Int =
|
||||||
(mutCursorIndex < mutMaximumBuildSpanLength) ? 0 : mutCursorIndex - mutMaximumBuildSpanLength
|
(mutCursorIndex < maxBuildSpanLength) ? 0 : mutCursorIndex - maxBuildSpanLength
|
||||||
let itrEnd: Int = min(mutCursorIndex + mutMaximumBuildSpanLength, mutReadings.count)
|
let itrEnd: Int = min(mutCursorIndex + maxBuildSpanLength, mutReadings.count)
|
||||||
|
|
||||||
for p in itrBegin..<itrEnd {
|
for p in itrBegin..<itrEnd {
|
||||||
for q in 1..<mutMaximumBuildSpanLength {
|
for q in 1..<maxBuildSpanLength {
|
||||||
if p + q > itrEnd {
|
if p + q > itrEnd {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
let strSlice = mutReadings[p..<(p + q)]
|
let arrSlice = mutReadings[p..<(p + q)]
|
||||||
let combinedReading: String = join(slice: strSlice, separator: joinSeparator)
|
let combinedReading: String = join(slice: arrSlice, separator: joinSeparator)
|
||||||
|
|
||||||
if !mutGrid.hasMatchedNode(location: p, spanningLength: q, key: combinedReading) {
|
if !mutGrid.hasMatchedNode(location: p, spanningLength: q, key: combinedReading) {
|
||||||
let unigrams: [Unigram] = mutLM.unigramsFor(key: combinedReading)
|
let unigrams: [Unigram] = mutLM.unigramsFor(key: combinedReading)
|
||||||
|
@ -252,12 +290,35 @@ extension Megrez {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func join(slice strSlice: ArraySlice<String>, separator: String) -> String {
|
private func join(slice arrSlice: ArraySlice<String>, separator: String) -> String {
|
||||||
var arrResult: [String] = []
|
var arrResult: [String] = []
|
||||||
for value in strSlice {
|
for value in arrSlice {
|
||||||
arrResult.append(value)
|
arrResult.append(value)
|
||||||
}
|
}
|
||||||
return arrResult.joined(separator: separator)
|
return arrResult.joined(separator: separator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Stable Sort Extension
|
||||||
|
|
||||||
|
// Reference: https://stackoverflow.com/a/50545761/4162914
|
||||||
|
|
||||||
|
extension Sequence {
|
||||||
|
/// Return a stable-sorted collection.
|
||||||
|
///
|
||||||
|
/// - Parameter areInIncreasingOrder: Return nil when two element are equal.
|
||||||
|
/// - Returns: The sorted collection.
|
||||||
|
func stableSorted(
|
||||||
|
by areInIncreasingOrder: (Element, Element) throws -> Bool
|
||||||
|
)
|
||||||
|
rethrows -> [Element]
|
||||||
|
{
|
||||||
|
try enumerated()
|
||||||
|
.sorted { a, b -> Bool in
|
||||||
|
try areInIncreasingOrder(a.element, b.element)
|
||||||
|
|| (a.offset < b.offset && !areInIncreasingOrder(b.element, a.element))
|
||||||
|
}
|
||||||
|
.map(\.element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -29,16 +29,23 @@ extension Megrez {
|
||||||
/// 幅位陣列。
|
/// 幅位陣列。
|
||||||
private var mutSpans: [Megrez.Span]
|
private var mutSpans: [Megrez.Span]
|
||||||
|
|
||||||
|
/// 該幅位內可以允許的最大詞長。
|
||||||
|
private var mutMaxBuildSpanLength = 10
|
||||||
|
|
||||||
|
/// 公開:該幅位內可以允許的最大詞長。
|
||||||
|
public var maxBuildSpanLength: Int { mutMaxBuildSpanLength }
|
||||||
|
|
||||||
/// 軌格的寬度,也就是其內的幅位陣列當中的幅位數量。
|
/// 軌格的寬度,也就是其內的幅位陣列當中的幅位數量。
|
||||||
var width: Int { mutSpans.count }
|
var width: Int { mutSpans.count }
|
||||||
|
|
||||||
public init() {
|
public init(spanLength: Int = 10) {
|
||||||
|
mutMaxBuildSpanLength = spanLength
|
||||||
mutSpans = [Megrez.Span]()
|
mutSpans = [Megrez.Span]()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 自我清空該軌格的內容。
|
/// 自我清空該軌格的內容。
|
||||||
public func clear() {
|
public func clear() {
|
||||||
mutSpans = [Megrez.Span]()
|
mutSpans.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 往該軌格的指定位置插入指定幅位長度的指定節點。
|
/// 往該軌格的指定位置插入指定幅位長度的指定節點。
|
||||||
|
@ -47,6 +54,8 @@ extension Megrez {
|
||||||
/// - location: 位置。
|
/// - location: 位置。
|
||||||
/// - spanningLength: 給定的幅位長度。
|
/// - spanningLength: 給定的幅位長度。
|
||||||
public func insertNode(node: Node, location: Int, spanningLength: Int) {
|
public func insertNode(node: Node, location: Int, spanningLength: Int) {
|
||||||
|
let location = abs(location) // 防呆
|
||||||
|
let spanningLength = abs(spanningLength) // 防呆
|
||||||
if location >= mutSpans.count {
|
if location >= mutSpans.count {
|
||||||
let diff = location - mutSpans.count + 1
|
let diff = location - mutSpans.count + 1
|
||||||
for _ in 0..<diff {
|
for _ in 0..<diff {
|
||||||
|
@ -62,24 +71,26 @@ extension Megrez {
|
||||||
/// - spanningLength: 給定的幅位長度。
|
/// - spanningLength: 給定的幅位長度。
|
||||||
/// - key: 索引鍵。
|
/// - key: 索引鍵。
|
||||||
public func hasMatchedNode(location: Int, spanningLength: Int, key: String) -> Bool {
|
public func hasMatchedNode(location: Int, spanningLength: Int, key: String) -> Bool {
|
||||||
|
let location = abs(location) // 防呆
|
||||||
|
let spanningLength = abs(spanningLength) // 防呆
|
||||||
if location > mutSpans.count {
|
if location > mutSpans.count {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let n = mutSpans[location].node(length: spanningLength)
|
let n = mutSpans[location].node(length: spanningLength)
|
||||||
return n == nil ? false : key == n?.key
|
return n != nil && key == n?.key
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 在該軌格的指定位置擴增一個幅位。
|
/// 在該軌格的指定位置擴增一個幅位。
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - location: 位置。
|
/// - location: 位置。
|
||||||
public func expandGridByOneAt(location: Int) {
|
public func expandGridByOneAt(location: Int) {
|
||||||
// 這裡加入 abs 完全是一個防呆設計
|
let location = abs(location) // 防呆
|
||||||
mutSpans.insert(Span(), at: abs(location))
|
mutSpans.insert(Span(), at: location)
|
||||||
if location != 0, abs(location) != mutSpans.count {
|
if location != 0, location != mutSpans.count {
|
||||||
for i in 0..<abs(location) {
|
for i in 0..<location {
|
||||||
// zaps overlapping spans
|
// zaps overlapping spans
|
||||||
mutSpans[i].removeNodeOfLengthGreaterThan(abs(location) - i)
|
mutSpans[i].removeNodeOfLengthGreaterThan(location - i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,6 +99,7 @@ extension Megrez {
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - location: 位置。
|
/// - location: 位置。
|
||||||
public func shrinkGridByOneAt(location: Int) {
|
public func shrinkGridByOneAt(location: Int) {
|
||||||
|
let location = abs(location) // 防呆
|
||||||
if location >= mutSpans.count {
|
if location >= mutSpans.count {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -99,11 +111,35 @@ extension Megrez {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 給定位置,枚舉出所有在這個位置開始的節點。
|
||||||
|
/// - Parameters:
|
||||||
|
/// - location: 位置。
|
||||||
|
public func nodesBeginningAt(location: Int) -> [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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
/// 給定位置,枚舉出所有在這個位置結尾的節點。
|
/// 給定位置,枚舉出所有在這個位置結尾的節點。
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - location: 位置。
|
/// - location: 位置。
|
||||||
public func nodesEndingAt(location: Int) -> [NodeAnchor] {
|
public func nodesEndingAt(location: Int) -> [NodeAnchor] {
|
||||||
var results: [NodeAnchor] = []
|
let location = abs(location) // 防呆
|
||||||
|
var results = [NodeAnchor]()
|
||||||
if !mutSpans.isEmpty, location <= mutSpans.count {
|
if !mutSpans.isEmpty, location <= mutSpans.count {
|
||||||
for i in 0..<location {
|
for i in 0..<location {
|
||||||
let span = mutSpans[i]
|
let span = mutSpans[i]
|
||||||
|
@ -127,7 +163,8 @@ extension Megrez {
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - location: 位置。
|
/// - location: 位置。
|
||||||
public func nodesCrossingOrEndingAt(location: Int) -> [NodeAnchor] {
|
public func nodesCrossingOrEndingAt(location: Int) -> [NodeAnchor] {
|
||||||
var results: [NodeAnchor] = []
|
let location = abs(location) // 防呆
|
||||||
|
var results = [NodeAnchor]()
|
||||||
if !mutSpans.isEmpty, location <= mutSpans.count {
|
if !mutSpans.isEmpty, location <= mutSpans.count {
|
||||||
for i in 0..<location {
|
for i in 0..<location {
|
||||||
let span = mutSpans[i]
|
let span = mutSpans[i]
|
||||||
|
@ -157,6 +194,7 @@ extension Megrez {
|
||||||
/// - 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) // 防呆
|
||||||
var node = NodeAnchor()
|
var node = NodeAnchor()
|
||||||
for nodeAnchor in nodesCrossingOrEndingAt(location: location) {
|
for nodeAnchor in nodesCrossingOrEndingAt(location: location) {
|
||||||
guard let theNode = nodeAnchor.node else {
|
guard let theNode = nodeAnchor.node else {
|
||||||
|
@ -182,6 +220,7 @@ extension Megrez {
|
||||||
/// - value: 給定字串。
|
/// - value: 給定字串。
|
||||||
/// - overridingScore: 給定權重數值。
|
/// - overridingScore: 給定權重數值。
|
||||||
public func overrideNodeScoreForSelectedCandidate(location: Int, value: String, overridingScore: Double) {
|
public func overrideNodeScoreForSelectedCandidate(location: Int, value: String, overridingScore: Double) {
|
||||||
|
let location = abs(location) // 防呆
|
||||||
for nodeAnchor in nodesCrossingOrEndingAt(location: location) {
|
for nodeAnchor in nodesCrossingOrEndingAt(location: location) {
|
||||||
guard let theNode = nodeAnchor.node else {
|
guard let theNode = nodeAnchor.node else {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -52,19 +52,9 @@ extension Megrez {
|
||||||
return stream
|
return stream
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 獲取加權量。
|
/// 獲取用來比較的權重。
|
||||||
public var additionalWeights: Double {
|
public var scoreForSort: Double {
|
||||||
(Double(spanningLength) - 1) * 0.75
|
node?.score ?? 0
|
||||||
}
|
|
||||||
|
|
||||||
/// 獲取平衡權重。
|
|
||||||
public var balancedScore: Double {
|
|
||||||
(node?.score ?? 0) + additionalWeights
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 獲取平衡累計權重。
|
|
||||||
public var balancedAccumulatedScore: Double {
|
|
||||||
accumulatedScore + additionalWeights
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ extension Megrez {
|
||||||
/// - node: 節點。
|
/// - node: 節點。
|
||||||
/// - length: 給定的節點長度。
|
/// - length: 給定的節點長度。
|
||||||
mutating func insert(node: Node, length: Int) {
|
mutating func insert(node: Node, length: Int) {
|
||||||
|
let length = abs(length) // 防呆
|
||||||
mutLengthNodeMap[length] = node
|
mutLengthNodeMap[length] = node
|
||||||
if length > mutMaximumLength {
|
if length > mutMaximumLength {
|
||||||
mutMaximumLength = length
|
mutMaximumLength = length
|
||||||
|
@ -57,6 +58,7 @@ extension Megrez {
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - length: 給定的節點長度。
|
/// - length: 給定的節點長度。
|
||||||
mutating func removeNodeOfLengthGreaterThan(_ length: Int) {
|
mutating func removeNodeOfLengthGreaterThan(_ length: Int) {
|
||||||
|
let length = abs(length) // 防呆
|
||||||
if length > mutMaximumLength { return }
|
if length > mutMaximumLength { return }
|
||||||
var max = 0
|
var max = 0
|
||||||
var removalList: [Int: Megrez.Node] = [:]
|
var removalList: [Int: Megrez.Node] = [:]
|
||||||
|
@ -79,7 +81,7 @@ extension Megrez {
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - length: 給定的節點長度。
|
/// - length: 給定的節點長度。
|
||||||
public func node(length: Int) -> Node? {
|
public func node(length: Int) -> Node? {
|
||||||
mutLengthNodeMap[length]
|
mutLengthNodeMap[abs(length)] // 防呆
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ extension Megrez {
|
||||||
/// 用來登記「當前選中的單元圖」的索引值的變數。
|
/// 用來登記「當前選中的單元圖」的索引值的變數。
|
||||||
private var mutSelectedUnigramIndex: Int = 0
|
private var mutSelectedUnigramIndex: Int = 0
|
||||||
/// 用來登記要施加給「『被標記為選中狀態』的候選字詞」的複寫權重的數值。
|
/// 用來登記要施加給「『被標記為選中狀態』的候選字詞」的複寫權重的數值。
|
||||||
private let kSelectedCandidateScore: Double = 99
|
public let kSelectedCandidateScore: Double = 99
|
||||||
/// 將當前節點列印成一個字串。
|
/// 將當前節點列印成一個字串。
|
||||||
public var description: String {
|
public var description: String {
|
||||||
"(node,key:\(mutKey),fixed:\(mutCandidateFixed ? "true" : "false"),selected:\(mutSelectedUnigramIndex),\(mutUnigrams))"
|
"(node,key:\(mutKey),fixed:\(mutCandidateFixed ? "true" : "false"),selected:\(mutSelectedUnigramIndex),\(mutUnigrams))"
|
||||||
|
@ -84,7 +84,7 @@ extension Megrez {
|
||||||
$0.score > $1.score
|
$0.score > $1.score
|
||||||
}
|
}
|
||||||
|
|
||||||
if mutUnigrams.count > 0 {
|
if !mutUnigrams.isEmpty {
|
||||||
mutScore = mutUnigrams[0].score
|
mutScore = mutUnigrams[0].score
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +133,7 @@ extension Megrez {
|
||||||
/// - index: 索引位置。
|
/// - index: 索引位置。
|
||||||
/// - fix: 是否將當前解點標記為「候選詞已鎖定」的狀態。
|
/// - fix: 是否將當前解點標記為「候選詞已鎖定」的狀態。
|
||||||
public func selectCandidateAt(index: Int = 0, fix: Bool = false) {
|
public func selectCandidateAt(index: Int = 0, fix: Bool = false) {
|
||||||
|
let index = abs(index)
|
||||||
mutSelectedUnigramIndex = index >= mutUnigrams.count ? 0 : index
|
mutSelectedUnigramIndex = index >= mutUnigrams.count ? 0 : index
|
||||||
mutCandidateFixed = fix
|
mutCandidateFixed = fix
|
||||||
mutScore = kSelectedCandidateScore
|
mutScore = kSelectedCandidateScore
|
||||||
|
@ -152,6 +153,7 @@ extension Megrez {
|
||||||
/// - index: 索引位置。
|
/// - index: 索引位置。
|
||||||
/// - score: 給定權重條件。
|
/// - score: 給定權重條件。
|
||||||
public func selectFloatingCandidateAt(index: Int, score: Double) {
|
public func selectFloatingCandidateAt(index: Int, score: Double) {
|
||||||
|
let index = abs(index) // 防呆
|
||||||
mutSelectedUnigramIndex = index >= mutUnigrams.count ? 0 : index
|
mutSelectedUnigramIndex = index >= mutUnigrams.count ? 0 : index
|
||||||
mutCandidateFixed = false
|
mutCandidateFixed = false
|
||||||
mutScore = score
|
mutScore = score
|
||||||
|
|
Loading…
Reference in New Issue