mirror of
https://github.com/mii443/AzooKeyKanaKanjiConverter.git
synced 2025-12-03 02:58:27 +00:00
add prediction candidate
This commit is contained in:
@@ -72,13 +72,13 @@ public struct Candidate: Sendable {
|
||||
/// 入力となるテキスト
|
||||
public var text: String
|
||||
/// 評価値
|
||||
public let value: PValue
|
||||
public var value: PValue
|
||||
/// composingText.inputにおいて対応する文字数。
|
||||
public var correspondingCount: Int
|
||||
/// 最後のmid(予測変換に利用)
|
||||
public let lastMid: Int
|
||||
public var lastMid: Int
|
||||
/// DicdataElement列
|
||||
public let data: [DicdataElement]
|
||||
public var data: [DicdataElement]
|
||||
/// 変換として選択した際に実行する`action`。
|
||||
/// - note: 括弧を入力した際にカーソルを移動するために追加した変数
|
||||
public var actions: [CompleteAction]
|
||||
|
||||
@@ -595,15 +595,15 @@ import SwiftUtils
|
||||
}
|
||||
|
||||
/// 変換確定後の予測変換候補を要求する関数
|
||||
public func requestPredictionCandidates(leftSideCandidate: Candidate, options: ConvertRequestOptions) -> [Candidate] {
|
||||
public func requestPredictionCandidates(leftSideCandidate: Candidate, options: ConvertRequestOptions) -> [PredictionCandidate] {
|
||||
// ゼロヒント予測変換に基づく候補を列挙
|
||||
let zeroHintResults = self.converter.getZeroHintPredictionCandidates(preparts: [leftSideCandidate], N_best: 10)
|
||||
// 予測変換に基づく候補を列挙
|
||||
// TODO: implement
|
||||
let predictionResults = self.converter.getPredictionCandidates(prepart: leftSideCandidate, N_best: 10)
|
||||
// 学習・ユーザ辞書に基づく候補を列挙
|
||||
// TODO: implement
|
||||
// 絵文字、記号類を列挙
|
||||
// TODO: implement
|
||||
return zeroHintResults
|
||||
return zeroHintResults.chained(predictionResults).max(count: 10, sortedBy: {$0.value < $1.value})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,63 @@ struct Kana2Kanji {
|
||||
)
|
||||
}
|
||||
|
||||
func getPredictionCandidates(prepart: Candidate, N_best: Int) -> [PredictionCandidate] {
|
||||
var result: [PredictionCandidate] = []
|
||||
var count = 1
|
||||
var prefixCandidate = prepart
|
||||
prefixCandidate.actions = []
|
||||
var prefixCandidateData = prepart.data
|
||||
var totalWord = ""
|
||||
var totalRuby = ""
|
||||
var totalData: [DicdataElement] = []
|
||||
while count <= min(prepart.data.count, 3), let element = prefixCandidateData.popLast() {
|
||||
defer {
|
||||
count += 1
|
||||
}
|
||||
// prefixCandidateを更新する
|
||||
do {
|
||||
prefixCandidate.value -= element.value()
|
||||
prefixCandidate.value -= self.dicdataStore.getCCValue(prefixCandidateData.last?.rcid ?? CIDData.BOS.cid, element.lcid)
|
||||
if DicdataStore.includeMMValueCalculation(element) {
|
||||
let previousMid = prefixCandidateData.last(where: DicdataStore.includeMMValueCalculation)?.mid ?? MIDData.BOS.mid
|
||||
prefixCandidate.lastMid = previousMid
|
||||
prefixCandidate.value -= self.dicdataStore.getMMValue(previousMid, element.mid)
|
||||
}
|
||||
prefixCandidate.data = prefixCandidateData
|
||||
|
||||
prefixCandidate.text = prefixCandidateData.reduce(into: "") { $0 += $1.word }
|
||||
prefixCandidate.correspondingCount = prefixCandidateData.reduce(into: 0) { $0 += $1.ruby.count }
|
||||
}
|
||||
|
||||
|
||||
totalWord.insert(contentsOf: element.word, at: totalWord.startIndex)
|
||||
totalRuby.insert(contentsOf: element.ruby, at: totalRuby.startIndex)
|
||||
totalData.insert(element, at: 0)
|
||||
let dicdata = self.dicdataStore.getPredictionLOUDSDicdata(key: totalRuby).filter {$0.word.hasPrefix(totalWord)}
|
||||
|
||||
for data in dicdata {
|
||||
let ccValue = self.dicdataStore.getCCValue(prefixCandidateData.last?.rcid ?? CIDData.BOS.cid, data.lcid)
|
||||
let includeMMValueCalculation = DicdataStore.includeMMValueCalculation(data)
|
||||
let mmValue = includeMMValueCalculation ? self.dicdataStore.getMMValue(prefixCandidate.lastMid, data.mid):.zero
|
||||
let wValue = data.value()
|
||||
let newValue = prefixCandidate.value + mmValue + ccValue + wValue
|
||||
// 追加すべきindexを取得する
|
||||
let lastindex: Int = (result.lastIndex(where: {$0.value >= newValue}) ?? -1) + 1
|
||||
if lastindex == N_best {
|
||||
continue
|
||||
}
|
||||
// カウントがオーバーしている場合は除去する
|
||||
if result.count >= N_best {
|
||||
result.removeLast()
|
||||
}
|
||||
// 共通接頭辞を切り落とす
|
||||
let text = String(data.word.dropFirst(totalWord.count))
|
||||
result.insert(.replacement(.init(text: text, targetData: totalData, replacementData: [data], value: newValue)), at: lastindex)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// 入力がない状態から、妥当な候補を探す
|
||||
/// - parameters:
|
||||
/// - preparts: Candidate列。以前確定した候補など
|
||||
@@ -75,8 +132,8 @@ struct Kana2Kanji {
|
||||
/// ゼロヒント予測変換の結果
|
||||
/// - note:
|
||||
/// 「食べちゃ-てる」「食べちゃ-いる」などの間抜けな候補を返すことが多いため、学習によるもの以外を無効化している。
|
||||
func getZeroHintPredictionCandidates(preparts: some Collection<Candidate>, N_best: Int) -> [Candidate] {
|
||||
var result: [Candidate] = []
|
||||
func getZeroHintPredictionCandidates(preparts: some Collection<Candidate>, N_best: Int) -> [PredictionCandidate] {
|
||||
var result: [PredictionCandidate] = []
|
||||
for candidate in preparts {
|
||||
if let last = candidate.data.last {
|
||||
let dicdata = self.dicdataStore.getZeroHintPredictionDicdata(lastRcid: last.rcid)
|
||||
@@ -92,21 +149,11 @@ struct Kana2Kanji {
|
||||
if lastindex == N_best {
|
||||
continue
|
||||
}
|
||||
// データを作成する
|
||||
var nodedata = candidate.data
|
||||
nodedata.append(data)
|
||||
let candidate = Candidate(
|
||||
text: data.word,
|
||||
value: data.value(),
|
||||
correspondingCount: data.ruby.count,
|
||||
lastMid: data.mid,
|
||||
data: [data]
|
||||
)
|
||||
// カウントがオーバーしている場合は除去する
|
||||
if result.count >= N_best {
|
||||
result.removeLast()
|
||||
}
|
||||
result.insert(candidate, at: lastindex)
|
||||
result.insert(.additional(.init(text: data.word, data: [data], value: newValue)), at: lastindex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
60
Sources/KanaKanjiConverterModule/PredictionCandidate.swift
Normal file
60
Sources/KanaKanjiConverterModule/PredictionCandidate.swift
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// PredictionCandidate.swift
|
||||
//
|
||||
//
|
||||
// Created by miwa on 2023/09/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum PredictionCandidate: Sendable {
|
||||
case additional(AdditionalPredictionCandidate)
|
||||
case replacement(ReplacementPredictionCandidate)
|
||||
|
||||
public struct AdditionalPredictionCandidate: Sendable {
|
||||
public var text: String
|
||||
public var data: [DicdataElement]
|
||||
public var value: PValue
|
||||
}
|
||||
public struct ReplacementPredictionCandidate: Sendable {
|
||||
/// 予測変換として表示するデータ
|
||||
public var text: String
|
||||
/// 置換対象のデータ
|
||||
public var targetData: [DicdataElement]
|
||||
/// 置換後のデータ
|
||||
public var replacementData: [DicdataElement]
|
||||
/// 重み
|
||||
public var value: PValue
|
||||
}
|
||||
|
||||
public var value: PValue {
|
||||
switch self {
|
||||
case .additional(let c):
|
||||
c.value
|
||||
case .replacement(let c):
|
||||
c.value
|
||||
}
|
||||
}
|
||||
|
||||
public func join(to candidate: consuming Candidate) -> Candidate {
|
||||
switch self {
|
||||
case .additional(let c):
|
||||
for data in c.data {
|
||||
candidate.text.append(contentsOf: data.word)
|
||||
candidate.data.append(data)
|
||||
}
|
||||
candidate.value = c.value
|
||||
candidate.correspondingCount = candidate.data.reduce(into: 0) { $0 += $1.ruby.count }
|
||||
candidate.lastMid = c.data.last(where: DicdataStore.includeMMValueCalculation)?.mid ?? candidate.lastMid
|
||||
return candidate
|
||||
case .replacement(let c):
|
||||
candidate.data.removeLast(c.targetData.count)
|
||||
candidate.data.append(contentsOf: c.replacementData)
|
||||
candidate.text = candidate.data.reduce(into: "") {$0 += $1.word}
|
||||
candidate.value = c.value
|
||||
candidate.lastMid = candidate.data.last(where: DicdataStore.includeMMValueCalculation)?.mid ?? MIDData.BOS.mid
|
||||
candidate.correspondingCount = candidate.data.reduce(into: 0) { $0 += $1.ruby.count }
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user