mirror of
https://github.com/mii443/AzooKeyKanaKanjiConverter.git
synced 2025-08-22 23:15:25 +00:00
fix: 動的ユーザ辞書が適切に読めていなかった問題を修正
This commit is contained in:
@ -256,8 +256,8 @@ public final class DicdataStore {
|
|||||||
var generator = TypoCorrectionGenerator(inputs: inputs, leftIndex: leftIndex, rightIndexRange: rightIndexRange, needTypoCorrection: needTypoCorrection)
|
var generator = TypoCorrectionGenerator(inputs: inputs, leftIndex: leftIndex, rightIndexRange: rightIndexRange, needTypoCorrection: needTypoCorrection)
|
||||||
var targetLOUDS: [String: LOUDS.MovingTowardPrefixSearchHelper] = [:]
|
var targetLOUDS: [String: LOUDS.MovingTowardPrefixSearchHelper] = [:]
|
||||||
var stringToInfo: [([Character], (endIndex: Int, penalty: PValue))] = []
|
var stringToInfo: [([Character], (endIndex: Int, penalty: PValue))] = []
|
||||||
|
// 動的辞書(一時学習データ、動的ユーザ辞書)から取り出されたデータ
|
||||||
var temporaryMemoryDicdata: [Int: [DicdataElement]] = [:]
|
var dynamicDicdata: [Int: [DicdataElement]] = [:]
|
||||||
// ジェネレータを舐める
|
// ジェネレータを舐める
|
||||||
while let (characters, info) = generator.next() {
|
while let (characters, info) = generator.next() {
|
||||||
guard let firstCharacter = characters.first else {
|
guard let firstCharacter = characters.first else {
|
||||||
@ -291,7 +291,7 @@ public final class DicdataStore {
|
|||||||
for (depth, dicdata) in result.dicdata {
|
for (depth, dicdata) in result.dicdata {
|
||||||
for data in dicdata {
|
for data in dicdata {
|
||||||
if info.penalty.isZero {
|
if info.penalty.isZero {
|
||||||
temporaryMemoryDicdata[depth, default: []].append(data)
|
dynamicDicdata[depth, default: []].append(data)
|
||||||
}
|
}
|
||||||
let ratio = Self.penaltyRatio[data.lcid]
|
let ratio = Self.penaltyRatio[data.lcid]
|
||||||
let pUnit: PValue = Self.getPenalty(data: data) / 2 // 負の値
|
let pUnit: PValue = Self.getPenalty(data: data) / 2 // 負の値
|
||||||
@ -299,7 +299,29 @@ public final class DicdataStore {
|
|||||||
if self.shouldBeRemoved(value: data.value() + adjust, wordCount: data.ruby.count) {
|
if self.shouldBeRemoved(value: data.value() + adjust, wordCount: data.ruby.count) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
temporaryMemoryDicdata[depth, default: []].append(data.adjustedData(adjust))
|
dynamicDicdata[depth, default: []].append(data.adjustedData(adjust))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.dynamicUserDict.isEmpty {
|
||||||
|
// 動的ユーザ辞書にデータがある場合、この位置で処理する
|
||||||
|
let katakanaString = String(characters).toKatakana()
|
||||||
|
let dynamicUserDictResult = self.getMatchDynamicUserDict(katakanaString)
|
||||||
|
updated = updated || !dynamicUserDictResult.isEmpty
|
||||||
|
for data in dynamicUserDictResult {
|
||||||
|
let depth = characters.endIndex
|
||||||
|
if info.penalty.isZero {
|
||||||
|
dynamicDicdata[depth, default: []].append(data)
|
||||||
|
} else {
|
||||||
|
let ratio = Self.penaltyRatio[data.lcid]
|
||||||
|
let pUnit: PValue = Self.getPenalty(data: data) / 2 // 負の値
|
||||||
|
let adjust = pUnit * info.penalty * ratio
|
||||||
|
if self.shouldBeRemoved(value: data.value() + adjust, wordCount: Array(data.ruby).count) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dynamicDicdata[depth, default: []].append(data.adjustedData(adjust))
|
||||||
|
}
|
||||||
|
// stringToInfoにも追加(getLOUDSDataInRangeでの処理のため)
|
||||||
|
stringToInfo.append((Array(data.ruby), (depth - 1, info.penalty)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if availableMaxIndex < characters.endIndex - 1 {
|
if availableMaxIndex < characters.endIndex - 1 {
|
||||||
@ -314,7 +336,7 @@ public final class DicdataStore {
|
|||||||
return (
|
return (
|
||||||
Dictionary(stringToInfo, uniquingKeysWith: {$0.penalty < $1.penalty ? $1 : $0}),
|
Dictionary(stringToInfo, uniquingKeysWith: {$0.penalty < $1.penalty ? $1 : $0}),
|
||||||
targetLOUDS.map { ($0.key, $0.value.indicesInDepth(depth: minCount - 1 ..< .max) )},
|
targetLOUDS.map { ($0.key, $0.value.indicesInDepth(depth: minCount - 1 ..< .max) )},
|
||||||
temporaryMemoryDicdata.flatMap {
|
dynamicDicdata.flatMap {
|
||||||
minCount < $0.key + 1 ? $0.value : []
|
minCount < $0.key + 1 ? $0.value : []
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -401,13 +423,6 @@ public final class DicdataStore {
|
|||||||
}
|
}
|
||||||
dicdata.append(contentsOf: result)
|
dicdata.append(contentsOf: result)
|
||||||
}
|
}
|
||||||
do {
|
|
||||||
let result = self.getMatchDynamicUserDict(segments[i - fromIndex])
|
|
||||||
for item in result {
|
|
||||||
stringToInfo[Array(item.ruby)] = (i, 0)
|
|
||||||
}
|
|
||||||
dicdata.append(contentsOf: result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if fromIndex == .zero {
|
if fromIndex == .zero {
|
||||||
let result: [LatticeNode] = dicdata.compactMap {
|
let result: [LatticeNode] = dicdata.compactMap {
|
||||||
|
@ -223,4 +223,83 @@ final class DicdataStoreTests: XCTestCase {
|
|||||||
XCTAssertTrue(result.contains(where: {$0.word == "九千九百九十九億九千九百九十九万九千九百九十九"}))
|
XCTAssertTrue(result.contains(where: {$0.word == "九千九百九十九億九千九百九十九万九千九百九十九"}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDynamicUserDict() throws {
|
||||||
|
let dicdataStore = DicdataStore(convertRequestOptions: requestOptions())
|
||||||
|
|
||||||
|
// 動的ユーザ辞書を設定
|
||||||
|
let testDynamicUserDict = [
|
||||||
|
DicdataElement(word: "テスト単語", ruby: "テストタンゴ", lcid: CIDData.固有名詞.cid, rcid: CIDData.固有名詞.cid, mid: MIDData.一般.mid, value: -10),
|
||||||
|
DicdataElement(word: "カスタム変換", ruby: "カスタムヘンカン", lcid: CIDData.固有名詞.cid, rcid: CIDData.固有名詞.cid, mid: MIDData.一般.mid, value: -12),
|
||||||
|
DicdataElement(word: "動的辞書", ruby: "ドウテキジショ", lcid: CIDData.固有名詞.cid, rcid: CIDData.固有名詞.cid, mid: MIDData.一般.mid, value: -11)
|
||||||
|
]
|
||||||
|
dicdataStore.sendToDicdataStore(.importDynamicUserDict(testDynamicUserDict))
|
||||||
|
|
||||||
|
// 完全一致テスト
|
||||||
|
do {
|
||||||
|
let result = dicdataStore.getMatchDynamicUserDict("テストタンゴ")
|
||||||
|
XCTAssertEqual(result.count, 1)
|
||||||
|
XCTAssertEqual(result.first?.word, "テスト単語")
|
||||||
|
XCTAssertEqual(result.first?.ruby, "テストタンゴ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 前方一致テスト
|
||||||
|
do {
|
||||||
|
let result = dicdataStore.getPrefixMatchDynamicUserDict("カスタム")
|
||||||
|
XCTAssertEqual(result.count, 1)
|
||||||
|
XCTAssertEqual(result.first?.word, "カスタム変換")
|
||||||
|
XCTAssertEqual(result.first?.ruby, "カスタムヘンカン")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 変換動作テスト
|
||||||
|
do {
|
||||||
|
var c = ComposingText()
|
||||||
|
c.insertAtCursorPosition("テストタンゴ", inputStyle: .direct)
|
||||||
|
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: c.input.endIndex - 1 ..< c.input.endIndex, needTypoCorrection: false)
|
||||||
|
XCTAssertTrue(result.contains(where: {$0.data.word == "テスト単語"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 複数の動的ユーザ辞書エントリの変換テスト
|
||||||
|
do {
|
||||||
|
var c = ComposingText()
|
||||||
|
c.insertAtCursorPosition("ドウテキジショ", inputStyle: .direct)
|
||||||
|
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: c.input.endIndex - 1 ..< c.input.endIndex, needTypoCorrection: false)
|
||||||
|
XCTAssertTrue(result.contains(where: {$0.data.word == "動的辞書"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存在しないエントリのテスト
|
||||||
|
do {
|
||||||
|
let result = dicdataStore.getMatchDynamicUserDict("ソンザイシナイ")
|
||||||
|
XCTAssertEqual(result.count, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDynamicUserDictWithConversion() throws {
|
||||||
|
let dicdataStore = DicdataStore(convertRequestOptions: requestOptions())
|
||||||
|
|
||||||
|
// 動的ユーザ辞書を設定
|
||||||
|
let testDynamicUserDict = [
|
||||||
|
DicdataElement(word: "テストワード", ruby: "テストワード", lcid: CIDData.固有名詞.cid, rcid: CIDData.固有名詞.cid, mid: MIDData.一般.mid, value: -8),
|
||||||
|
DicdataElement(word: "特殊読み", ruby: "トクシュヨミ", lcid: CIDData.固有名詞.cid, rcid: CIDData.固有名詞.cid, mid: MIDData.一般.mid, value: -9)
|
||||||
|
]
|
||||||
|
dicdataStore.sendToDicdataStore(.importDynamicUserDict(testDynamicUserDict))
|
||||||
|
|
||||||
|
// ローマ字入力での変換テスト
|
||||||
|
do {
|
||||||
|
var c = ComposingText()
|
||||||
|
sequentialInput(&c, sequence: "tesutowaーdo", inputStyle: .roman2kana)
|
||||||
|
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: c.input.endIndex - 1 ..< c.input.endIndex, needTypoCorrection: false)
|
||||||
|
XCTAssertTrue(result.contains(where: {$0.data.word == "テストワード"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 動的ユーザ辞書の単語が通常の辞書よりも優先されることのテスト
|
||||||
|
do {
|
||||||
|
var c = ComposingText()
|
||||||
|
c.insertAtCursorPosition("トクシュヨミ", inputStyle: .direct)
|
||||||
|
let result = dicdataStore.getLOUDSDataInRange(inputData: c, from: 0, toIndexRange: c.input.endIndex - 1 ..< c.input.endIndex, needTypoCorrection: false)
|
||||||
|
let dynamicUserDictResult = result.first(where: {$0.data.word == "特殊読み"})
|
||||||
|
XCTAssertNotNil(dynamicUserDictResult)
|
||||||
|
XCTAssertEqual(dynamicUserDictResult?.data.metadata, .isFromUserDictionary)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user