Intl.Segmenter の勘所:不満点の検討
前回までの実験結果から、以下の不満が出てきたので、何とか解消したい。
1.記号が消えている
2.不要な空行
3.対になっているカッコ(【】『』等)の抽出
不満点:記号が消えている、不要な空行
これらの不満点は、すぐに解消し以下のように修正した。
export function experiment03() {
const crSplit = `|`
const cleanString = sentence.replace(/<[^>]+>/g, crSplit)
const SegmenterClass = (Intl as any).Segmenter
const segmenter = new SegmenterClass("ja-JP", { granularity: "word" })
const segments = segmenter.segment(cleanString)
const stopWords = new Set([
/*`、`,`。`,*/ //これは必ず「isWordLike === false」になるみたいなので除外
`と`,`に`,`の`,`や`,`を`,`で`,`が`,
]);
let fifo = new Array<string>()
for (const { segment, isWordLike } of segments) {
//console.log(segment, isWordLike)
const word = segment.trim();
if (!isWordLike) {
if (word === crSplit) {
flushAll()
}
//↓:改良点
else fifo.push(word)
//↑:改良点
}
else {
if (stopWords.has(word)) {
flushAll()
}
else fifo.push(word)
}
}
flushAll()
function flushAll() {
let combine = ``
for (const w of fifo) {
combine += w
}
//↓:改良点
if (combine !== ``)
console.log(combine)
//↑:改良点
fifo = new Array<string>()
}
}
不満点:対になっているカッコ(【】『』等)の抽出
出力結果を確認すると、ここまでの検証内容ではカッコをうまく処理していないことがわかる。
元の文章はこんな感じで、
【通常EX+(★5にする事で解放)】
出力結果がこうなっていた。
【通常EX+(★5
する事
解放)】
カッコを定義する
全角・半角を含んだカッコのセットを用意する。
const brackets = new Set([
`【`,`『`,`「`,`[`,`(`,`(`,`「`,
]);
const cbrackets = new Set([
`】`,`』`,`」`,`]`,`)`,`)`,`」`,
]);
カッコの連続性を確認する
対策としては「閉じカッコ」を取り出すまで「文章が連続しているとみなす」必要がありそうなので。
以下のような検証用の関数を作成。
function isClosedBrackets() {
const openCount = fifo.filter(w => brackets.has(w)).length;
const closeCount = fifo.filter(w => cbrackets.has(w)).length;
const res = openCount - closeCount === 0
return res
}
要するに、カッコの対をカウントする。
修正結果はこんな感じ。
export function experiment04() {
const crSplit = `|`
const cleanString = sentence.replace(/<[^>]+>/g, crSplit)
const SegmenterClass = (Intl as any).Segmenter
const segmenter = new SegmenterClass("ja-JP", { granularity: "word" })
const segments = segmenter.segment(cleanString)
const stopWords = new Set([
`と`,`に`,`の`,`や`,`を`,`で`,`が`,
]);
//↓:改良点
const brackets = new Set([
`【`,`『`,`「`,`[`,`(`,`(`,`「`,
]);
const cbrackets = new Set([
`】`,`』`,`」`,`]`,`)`,`)`,`」`,
]);
//↑:改良点
let fifo = new Array<string>()
for (const { segment, isWordLike } of segments) {
//console.log(segment, isWordLike)
const word = segment.trim();
if (!isWordLike) {
if (word === crSplit) {
flushAll()
}
//↓:改良点
else if (brackets.has(word)) {
fifo.push(word)
}
else if (cbrackets.has(word)) {
fifo.push(word)
if (isClosedBrackets())
flushAll()
}
//↑:改良点
else fifo.push(word)
}
else {
if (stopWords.has(word)) {
//↓:改良点
//flushAll()
let isExist = false
for (const br of brackets) {
isExist = fifo.includes(br)
if (isExist) break
}
//↑:改良点
}
else fifo.push(word)
}
}
flushAll()
//↓:改良点
function isClosedBrackets() {
const openCount = fifo.filter(w => brackets.has(w)).length;
const closeCount = fifo.filter(w => cbrackets.has(w)).length;
const res = openCount - closeCount === 0
return res
}
//↑:改良点
function flushAll() {
let combine = ``
for (const w of fifo) {
combine += w
}
if (combine !== ``)
console.log(combine)
fifo = new Array<string>()
}
}
Intl.Segmenter API の有用性
JS/TSで、Intl.Segmenter APIを使えば、ある程度の特徴語を抽出できたように思う。
外部のパッケージを一切導入せず、またバージョン管理に悩まされる必要がないうえに、ブラウザ標準機能として利用できることは、大きな将来性と可能性を秘めていると思った。
プレイグラウンドを利用すると、簡単に検証できるので、興味ある方はぜひやってみてください。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter
プレイグラウンドを利用する場合、JSで以下のようになります。
function experiment04() {
const crSplit = `|`
const cleanString = sentence.replace(/<[^>]+>/g, crSplit)
const SegmenterClass = Intl.Segmenter
const segmenter = new SegmenterClass("ja-JP", { granularity: "word" })
const segments = segmenter.segment(cleanString)
const stopWords = new Set([
/*`、`,`。`,*/ //これは必ず「isWordLike === false」になるみたいなので除外
`と`,`に`,`の`,`や`,`を`,`で`,`が`,
]);
//↓:改良点
const brackets = new Set([
`【`,`『`,`「`,`[`,`(`,`(`,`「`,
]);
const cbrackets = new Set([
`】`,`』`,`」`,`]`,`)`,`)`,`」`,
]);
//↑:改良点
let fifo = new Array()
for (const { segment, isWordLike } of segments) {
//console.log(segment, isWordLike)
const word = segment.trim();
if (!isWordLike) {
if (word === crSplit) {
flushAll()
}
//↓:改良点
else if (brackets.has(word)) {
fifo.push(word)
}
else if (cbrackets.has(word)) {
fifo.push(word)
if (isClosedBrackets())
flushAll()
}
//↑:改良点
else fifo.push(word)
}
else {
if (stopWords.has(word)) {
//↓:改良点
//flushAll()
let isExist = false
for (const br of brackets) {
isExist = fifo.includes(br)
if (isExist) break
}
//↑:改良点
}
else fifo.push(word)
}
}
flushAll()
//↓:改良点
function isClosedBrackets() {
const openCount = fifo.filter(w => brackets.has(w)).length;
const closeCount = fifo.filter(w => cbrackets.has(w)).length;
const res = openCount - closeCount === 0
return res
}
//↑:改良点
function flushAll() {
let combine = ``
for (const w of fifo) {
combine += w
}
if (combine !== ``)
console.log(combine)
fifo = new Array()
}
}
const sentence = `ユニオンバースト<br>ライブ・オンステージ<br>【説明】<br><br>【挑発】味方全体の物理攻撃力を小アップさせ、自分の物理防御力と魔法防御力を大アップさせる。さらに敵全体を挑発する。<br>【効果】<br><br>味方全体の物理攻撃力を509アップ<br><br>自分の物理防御力を204アップ<br><br>自分の魔法防御力を204アップ<br><br>敵全体を挑発する<br><br>※各バフ約18秒<br><br>※挑発約10秒<br>ライブ・オンスペシャルステージ<br>(星6)<br>【強化点】<br><br>【挑発】味方全体の<br>物理攻撃力を大アップ<br>させ、<br>物理/魔法無効バリアを展開<br>し、自分の物理防御力と魔法防御力を特大アップさせる。さらに<br>敵全体を挑発してスタン<br>させる。<br><br>※攻撃バフ/防御バフ/バリア:約20秒※挑発約:12秒※スタン:2秒<br>スキル1<br>クレールスラッシュ<br>【説明】<br><br>前方範囲内の敵すべてに物理小ダメージを与え、さらにスタンさせる。<br>【効果】<br><br>範囲内の敵に1122の物理ダメージ<br><br>範囲内の敵を一定時間行動不能にする<br>(約2秒)<br>クレールスラッシュ+<br>(要専用装備)<br>【強化点】<br><br>前方範囲内の敵すべてに<br>物理小ダメージ<br>を与え、さらにスタンさせて<br>物理攻撃力を小ダウン(約12秒)させる<br>。<br>スキル2<br>アイドルエール<br>【説明】<br><br>自分の周囲の味方すべてのHPを中回復させる。<br>【効果】<br><br>範囲内の味方のHPを2035回復<br>EXスキル<br>ルミナススマイル<br>【説明】<br><br>バトル開始時に自分の物理防御力を中アップさせる。<br>【通常EX+(★5にする事で解放)】<br><br>上記の効果に<br>魔法防御力小アップ<br>が追加。`
experiment04()