Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

モーラごとの調整結果保持のテストを追加 #1646

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
43f3ec3
AccentDiffクラスの追加just-pluck-itライブラリの追加
weweweok Oct 19, 2023
468d0a2
既存のdiff機能を更新
weweweok Oct 19, 2023
bc126d2
package-lock.jsonに変更を追加(push忘れ)
weweweok Oct 19, 2023
07eeca4
diffArraysを使用し、ソースコードの記述量を削減
weweweok Oct 19, 2023
f35b083
just-pluck-itを削除し、同じ役割の関数を作成
weweweok Oct 20, 2023
39a7483
誤字の修正
weweweok Oct 21, 2023
d3de6f0
indexの加算処理で、for文を回さないように変更
weweweok Oct 21, 2023
49657d9
命名規則に準拠していない変数を修正
weweweok Oct 21, 2023
b9af99a
参照外しを行う箇所の一部でstructuredCloneを使用
weweweok Oct 21, 2023
cf63b0b
インクリメントの記述を変更
weweweok Oct 21, 2023
bfba65d
クラスの目的を簡潔にまとめた
weweweok Oct 24, 2023
2b4ee7f
不要な参照回避を削除
weweweok Oct 24, 2023
1853237
jsdocを修正
weweweok Oct 24, 2023
65a3518
誤ったコメントの修正と、フラグの記述を変更
weweweok Oct 24, 2023
d483506
クラス名の変更
weweweok Oct 24, 2023
0ca4004
不要なimportを削除
weweweok Oct 27, 2023
f380d74
AccentDiffを削除し、TuningTranscrptionを追加
weweweok Oct 27, 2023
ca80cbf
変数名をcamelCaseに修正
weweweok Oct 27, 2023
90d418b
createFlatArrayの変数名を変更
weweweok Oct 27, 2023
58b0ab3
型宣言でany[]にしている箇所を変更
weweweok Nov 1, 2023
73dbe47
jsdocの修正と、beforeIndex -> moraPatchIndexに変更
weweweok Nov 4, 2023
ddc4f68
jsdocの修正
weweweok Nov 4, 2023
56f9a33
型の変更
weweweok Nov 4, 2023
e233502
変更漏れ
weweweok Nov 4, 2023
d403b15
neverの追加
weweweok Nov 4, 2023
db2be54
jsdocの修正
weweweok Nov 4, 2023
67a522d
リファクタリング
weweweok Nov 4, 2023
7b7b180
アクセント句内の差分のプルリクエストをいろいろ変えてみた
Hiroshiba Nov 5, 2023
3a07fcf
Merge branch 'main' into アクセント句内の差分のプルリクエストをいろいろ変えてみた
Hiroshiba Nov 10, 2023
2ad775e
コメントを調整
Hiroshiba Nov 10, 2023
1b67f74
不要なstructuredCloneを削除
Hiroshiba Nov 10, 2023
81841f9
private
Hiroshiba Nov 10, 2023
da456e2
currentTextIndex
Hiroshiba Nov 10, 2023
e2fae75
remove console.log
Hiroshiba Nov 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 35 additions & 24 deletions src/store/utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,9 @@ function skipMemoText(targettext: string): string {
/**
* 2つのアクセント句配列を比べて同じだと思われるモーラの調整結果を転写し
* 変更前のアクセント句の調整結果を変更後のアクセント句に保持する。
* 「こんにちは」 -> 「こんばんは」と変更した場合、以下の例において[]に囲まれる部分は、変更前のモーラが再利用される。
* <例>
*
* <例>
* 「こんにちは」 -> 「こんばんは」と変更した場合、[]に囲まれる部分で変更前のモーラが転写される。
* 「 [こん]ばん[は] 」
*/
export class TuningTranscription {
Expand All @@ -190,8 +190,11 @@ export class TuningTranscription {
this.afterAccent = JSON.parse(JSON.stringify(afterAccent));
}

createFlatArray<T, K extends keyof T>(collection: T[], key: K): T[K][] {
const result: T[K][] = [];
private createFlatArray<T, K extends keyof T>(
collection: T[],
key: K
): T[K] extends (infer U)[] ? U[] : T[K][] {
const result = [];
for (const element of collection) {
const value = element[key];
if (Array.isArray(value)) {
Expand All @@ -200,64 +203,72 @@ export class TuningTranscription {
result.push(value);
}
}
return result;
return result as T[K] extends (infer U)[] ? U[] : T[K][];
}

/**
* 変更前の配列を操作してpatchMora配列を作る。
*
* <例> (Uはundefined)
* 変更前のテキスト差分: [ "ズ", "ン", "ダ", "モ", "ン", "ナ", "ノ", "ダ" ]
* 変更後のテキスト差分: [ "ボ", "ク", "ズ", "ン", "ダ", "ナ", "ノ", "デ", "ス" ]
* ↓
* ↓ 再利用される文字列とundefinedで構成されたデータを作る。
* ↓ 比較しやすいように文字列とundefinedを記述しているが、
* ↓ 実際には"ズ"などの文字列部分が{text: "ズ"...}のようなデータ構造となる。
* ↓
* [ U , U , "ズ", "ン", "ダ", "ナ", "ノ", U , U ]
* 変更前 [ ズ, ン, ダ, モ, ン, ナ, ノ, ダ ]
* 変更後 [ ボ, ク, ズ, ン, ダ, ナ, ノ, デ, ス ]
*
* 再利用される文字列とundefinedで構成されたデータを作る。
* [ U, U, ズ, ン, ダ, ナ, ノ, U, U ]
*
* したがって、最終的にこちらのようなデータ構造(↓)が出力される
* 実際に作られるpatchMora配列: [ U , U , {text: "ズ"...}, {text: "ン"...}, {text: "ダ"...},{text: "ナ"...},{text: "ノ"...}, U , U ]
* 実際には"ズ"などの文字列部分は{text: "ズ"...}のようなデータ構造になっている
* [ U, U, {text: "ズ"...}, {text: "ン"...}, {text: "ダ"...}, {text: "ナ"...}, {text: "ノ"...}, U, U ]
*/
createDiffPatch() {
private createDiffPatch() {
const before = structuredClone(this.beforeAccent);
const after = structuredClone(this.afterAccent);

const beforeFlatArray = this.createFlatArray(before, "moras");
const afterFlatArray = this.createFlatArray(after, "moras");
const diffed = diffArrays(
this.createFlatArray(structuredClone(beforeFlatArray), "text" as never),
this.createFlatArray(structuredClone(afterFlatArray), "text" as never)
this.createFlatArray(beforeFlatArray, "text"),
this.createFlatArray(afterFlatArray, "text")
);

// FIXME: beforeFlatArrayを破壊的に変更しなくても良いようにしてasを不要にする
let currentTextIndex = 0;
for (const diff of diffed) {
if (diff.removed) {
beforeFlatArray.splice(currentTextIndex, diff.count);
} else if (diff.added) {
diff.value.forEach(() => {
beforeFlatArray.splice(currentTextIndex, 0, undefined as never);
beforeFlatArray.splice(
currentTextIndex,
0,
undefined as never as Mora
);
currentTextIndex++;
});
} else {
currentTextIndex += diff.value.length;
}
}
return beforeFlatArray;
return beforeFlatArray as (Mora | undefined)[];
}

/**
* 「こんにちは」 -> 「こんばんは」 とテキストを変更した場合、以下の例のように、moraPatch配列とafter(AccentPhrases)を比較し、
* text(key)の値が一致するとき、after[...]["moras"][moraIndex] = moraPatch[moraPatchIndex]と代入することで、モーラを再利用する。
* moraPatchとafterAccentを比較し、textが一致するモーラを転写する。
*
* <例> (「||」は等号記号を表す)
* 「こんにちは」 -> 「こんばんは」 とテキストを変更した場合、以下の例のように比較する。
*
* moraPatch = [ {text: "コ"...}, {text: "ン"...}, undefined , undefined , {text: "ハ"...} ]
* || || ||
* after[...]["moras"] = [ {text: "コ"...}, {text: "ン"...}, {text: "バ"...}, {text: "ン"...}, {text: "ハ"...} ]
*
* あとは一致したモーラを転写するだけ。
*
*/
mergeAccentPhrases(moraPatch: (Mora | undefined)[]): AccentPhrase[] {
private mergeAccentPhrases(moraPatch: (Mora | undefined)[]): AccentPhrase[] {
const after: AccentPhrase[] = structuredClone(this.afterAccent);
let moraPatchIndex = 0;

// 与えられたアクセント句は、AccentPhrases[ Number ][ Object Key ][ Number ]の順番で、モーラを操作できるため、二重forで回す
// AccentPhrasesから[ accentIndex ]["moras"][ moraIndex ]でモーラが得られる
for (let accentIndex = 0; accentIndex < after.length; accentIndex++) {
for (
let moraIndex = 0;
Expand Down
95 changes: 95 additions & 0 deletions tests/unit/lib/tuningTranscription.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { AccentPhrase, Mora } from "@/openapi";
import { TuningTranscription } from "@/store/utility";

function createDummyMora(text: string): Mora {
return {
text,
vowel: "dummy",
vowelLength: Math.random(),
pitch: Math.random(),
};
}

function createDummyAccentPhrase(moraTexts: string[]): AccentPhrase {
return {
moras: moraTexts.map(createDummyMora),
accent: Math.random(),
};
}

// AccentPhrasesから特定のmora textを持つものMoraを返す
function findMora(
accentPhrases: AccentPhrase[],
text: string
): Mora | undefined {
let candidate: Mora | undefined;
for (let i = 0; i < accentPhrases.length; i++) {
for (let j = 0; j < accentPhrases[i].moras.length; j++) {
if (accentPhrases[i].moras[j].text === text) {
if (candidate != undefined) {
throw new Error(`AccentPhraseに${text}が複数見つかりました`);
}
candidate = accentPhrases[i].moras[j];
}
}
}
return candidate;
}

it("2つ以上のアクセント句でも正しくデータを転写できる", async () => {
const before: AccentPhrase[] = [
createDummyAccentPhrase(["い", "え"]),
createDummyAccentPhrase(["か", "き", "く", "け", "こ"]),
createDummyAccentPhrase(["さ", "し", "す", "せ", "そ"]),
];
const after: AccentPhrase[] = [
createDummyAccentPhrase(["あ", "い", "う", "え", "お"]), // 最初・真ん中・最後に追加
createDummyAccentPhrase(["き", "け"]), // 最初・真ん中・最後を消去
createDummyAccentPhrase(["た", "ち", "つ", "て", "と"]), // すべて置き換え
];
const tuningTransctiption = new TuningTranscription(before, after);
const result = tuningTransctiption.transcribe();

// モーラ数などは変わっていない
expect(result.length).toEqual(after.length);
for (let i = 0; i < result.length; i++) {
expect(result[i].moras.length).toEqual(after[i].moras.length);
}

// 転写されている
["い", "え", "き", "け"].forEach((moraText) => {
expect(findMora(result, moraText)).toEqual(findMora(before, moraText));
});

// 転写されていない
["あ", "う", "お", "た", "ち", "つ", "て", "と"].forEach((moraText) => {
expect(findMora(result, moraText)).not.toEqual(findMora(before, moraText));
});
});

it("拗音のあるモーラも正しくデータを転写できる", async () => {
const before = [
createDummyAccentPhrase(["い", "しぃ", "う", "しゅ", "お", "しょ"]),
];
const after = [
createDummyAccentPhrase(["あ", "しゃ", "き", "きゅ", "お", "しょ"]),
];
const tuningTransctiption = new TuningTranscription(before, after);
const result = tuningTransctiption.transcribe();

// モーラ数などは変わっていない
expect(result.length).toEqual(after.length);
for (let i = 0; i < result.length; i++) {
expect(result[i].moras.length).toEqual(after[i].moras.length);
}

// 転写されている
["お", "しょ"].forEach((moraText) => {
expect(findMora(result, moraText)).toEqual(findMora(before, moraText));
});

// 転写されていない
["あ", "しゃ", "き", "きゅ"].forEach((moraText) => {
expect(findMora(result, moraText)).not.toEqual(findMora(before, moraText));
});
});