本文へ移動

Hikidashi引き出し

和暦と漢数字の相互変換、活用した語の辞書形、漢字への読みの割り当て、貼り付けた文からの単語の抽出、文の難しさ。日本語のテキストを扱う小さな道具を集めた引き出しです。

「引き出し」という名前は、道具が一つずつしまってあり、必要なときに取り出して使える、という意味です。 名前について(英語)

目次

やり方

手元の辞書で貼り付けた文から単語を取り出す

貼り付けられたページを、手元の辞書にある単語と漢字に変えます。辞書への問い合わせは一度だけです。

学習ツールに本のページが貼り付けられ、そこから単語の一覧を作りたいとします。文を読むのはHikidashiの役目で、何が単語かを決めるのは手元の辞書(データベース、JSONファイル、JMdictなど)です。手順は三つです。単語になりうるものを並べ、それをまとめて辞書に問い合わせ、その答えをもとに文を読みます。

1.候補を並べる

wordCandidatesは、辞書に載っていそうな文中の部分と、活用しているように見える部分の辞書形(「行きます」なら「行く」)を返します。数はEXTRACT_LIMITS.candidatesまでなので、一章分の文でも、問い合わせの大きさには上限があります。

import { wordCandidates } from "@johnmorrisdotca/hikidashi/extract";

const text = "毎日、先生と学校へ行きます。日本語を勉強しています。";
const candidates = wordCandidates(text);
console.log(candidates.length, candidates.slice(0, 8));
144 [
  '毎日',
  '先生と学校へ行く',
  '先生と学校へ行きる',
  '先生と学校へ行き',
  '先生と学校へ行',
  '先生と学校へ',
  '先生と学校',
  '先生と学'
]

2.辞書に一度だけ問い合わせる

候補をすべて一つのクエリーで引き、見つかった単語から、その語の種類へのMapを作ります。wordClassesOfは、辞書に載っている品詞("godan verb"のような英語の名前でもJMdictのタグでも)をHikidashiの種類に変えます。ここでは配列をデータベースの代わりにしています。

import { wordClassesOf } from "@johnmorrisdotca/hikidashi/deinflect";
import { type KnownWords, wordCandidates } from "@johnmorrisdotca/hikidashi/extract";

// 手元の辞書:単語と、その品詞。
const DICTIONARY = [
  { word: "毎日", pos: ["n", "adv"] },
  { word: "先生", pos: ["n"] },
  { word: "学校", pos: ["n"] },
  { word: "行く", pos: ["v5k-s", "vi"] },
  { word: "日本語", pos: ["n"] },
  { word: "勉強", pos: ["n", "vs-s"] },
  { word: "する", pos: ["vs-i"] },
];

const text = "毎日、先生と学校へ行きます。日本語を勉強しています。";
const wanted = new Set(wordCandidates(text));
// 実際のアプリでは SELECT word, pos FROM entries WHERE word = ANY($1) のように、候補を $1 に渡します。
const rows = DICTIONARY.filter((row) => wanted.has(row.word));
const known: KnownWords = new Map(rows.map((row) => [row.word, wordClassesOf(row.word, row.pos)]));
console.log([...known.keys()]);
[
  '毎日',   '先生',
  '学校',   '行く',
  '日本語', '勉強',
  'する'
]

3.文を読む

import { wordClassesOf } from "@johnmorrisdotca/hikidashi/deinflect";
import { extractFromText } from "@johnmorrisdotca/hikidashi/extract";

const known = new Map([
  ["毎日", []], ["先生", []], ["学校", []], ["日本語", []],
  ["行く", wordClassesOf("行く", ["v5k-s"])], ["勉強", wordClassesOf("勉強", ["vs-s"])],
]);
const found = extractFromText({ text: "毎日、先生と学校へ行きます。日本語を勉強しています。", known });
console.log(found.words);    // 辞書形:「行きます」は「行く」
console.log(found.kanji.join(""));
console.log(found.stats);
[ '毎日', '先生', '学校', '行く', '日本語', '勉強' ]
毎日先生学校行本語勉強
{ characters: 26, truncated: false, kanji: 11, words: 6 }

単語は辞書形で、文に最初に出てきた順に、一つずつ返ります。漢字は、単語に含まれるかどうかに関係なく返ります。ページを貼り付けた学習者は、どちらの一覧を求めているかもしれないからです。

確かめておくこと

  • **種類を持たない単語は、書かれたままの形でしか一致しません。**手順3の「先生」は[]なので、そのままの形で見つかります。種類を持たない動詞は辞書形でしか見つからず、「行きます」からは見つかりません。
  • **動詞の語幹と同じ形の名詞は、後ろに何もなければ名詞のままです。**辞書に「行き」があれば「東京行き」からは「行き」が、「行きます」からは「行く」が出ます。取り出し方の決まりを見てください。
  • **何も保存せず、そのまま返しもしません。**結果は、辞書にもともとあった単語と一文字ずつの漢字だけで、貼り付けた文そのものは返りません。文がEXTRACT_LIMITS.charactersより長くて切り詰めたときは、stats.truncatedがそれを示します。

ソースを見るGitHub で編集@johnmorrisdotca/hikidashi 1.1.0 のソースから作成しました。

この日本語は、まだ日本語を母語とする方の確認を受けていません。訂正を歓迎します。