/* * 文節の最小単位であるwordlistを構成する * * anthy_make_word_list_all() * 文節の形式を満たす部分文字列を列挙する * いくかの経路で列挙されたword_listは * anthy_commit_word_listでsplitter_contextに追加される * * Funded by IPA未踏ソフトウェア創造事業 2002 2/27 * Copyright (C) 2000-2006 TABATA Yusuke * Copyright (C) 2004-2006 YOSHIDA Yuichi * Copyright (C) 2000-2003 UGAWA Tomoharu * * $Id: wordlist.c,v 1.50 2002/11/17 14:45:47 yusuke Exp $ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wordborder.h" #define HF_THRESH 784 static void *weak_word_array; /* デバッグ用 */ void anthy_print_word_list(struct splitter_context *sc, struct word_list *wl) { xstr xs; if (!wl) { printf("--\n"); return ; } /* 接頭辞 */ xs.len = wl->part[PART_CORE].from - wl->from; xs.str = sc->ce[wl->from].c; anthy_putxstr(&xs); printf("."); /* 自立語 */ xs.len = wl->part[PART_CORE].len; xs.str = sc->ce[wl->part[PART_CORE].from].c; anthy_putxstr(&xs); printf("."); /* 接尾辞 */ xs.len = wl->part[PART_POSTFIX].len; xs.str = sc->ce[wl->part[PART_CORE].from + wl->part[PART_CORE].len].c; anthy_putxstr(&xs); printf("-"); /* 付属語 */ xs.len = wl->part[PART_DEPWORD].len; xs.str = sc->ce[wl->part[PART_CORE].from + wl->part[PART_CORE].len + wl->part[PART_POSTFIX].len].c; anthy_putxstr(&xs); anthy_print_wtype(wl->part[PART_CORE].wt); printf(" %s%s\n", anthy_seg_class_name(wl->seg_class), (wl->is_compound ? ",compound" : "")); } int anthy_dep_word_hash(xstr *xs) { return anthy_xstr_hash(xs) % WORD_HASH_MAX; } /** word_listを比較する、枝刈りのためなので、 厳密な比較である必要は無い */ static int word_list_same(struct word_list *wl1, struct word_list *wl2) { if (wl1->node_id != wl2->node_id || wl1->from != wl2->from || wl1->len != wl2->len || wl1->mw_features != wl2->mw_features || wl1->tail_ct != wl2->tail_ct || wl1->part[PART_CORE].len != wl2->part[PART_CORE].len || wl1->is_compound != wl2->is_compound || !anthy_wtype_equal(wl1->part[PART_CORE].wt, wl2->part[PART_CORE].wt) || wl1->head_pos != wl2->head_pos) { return 0; } if (wl1->part[PART_DEPWORD].dc != wl2->part[PART_DEPWORD].dc) { return 0; } /* 同じと判断 */ return 1; } static void set_features(struct word_list *wl) { if (anthy_wtype_get_pos(wl->part[PART_CORE].wt) == POS_NOUN && anthy_wtype_get_sv(wl->part[PART_CORE].wt)) { wl->mw_features |= MW_FEATURE_SV; } if (wl->part[PART_POSTFIX].len || wl->part[PART_PREFIX].len) { wl->mw_features |= MW_FEATURE_SUFFIX; } if (anthy_wtype_get_pos(wl->part[PART_CORE].wt) == POS_NUMBER) { wl->mw_features |= MW_FEATURE_NUM; } if (wl->part[PART_CORE].len == 1) { wl->mw_features |= MW_FEATURE_CORE1; } if (wl->part[PART_CORE].len == 0) { wl->mw_features |= MW_FEATURE_DEP_ONLY; } if (wl->part[PART_CORE].freq > HF_THRESH) { wl->mw_features |= MW_FEATURE_HIGH_FREQ; } } /** 作ったword_listのスコアを計算してからコミットする */ void anthy_commit_word_list(struct splitter_context *sc, struct word_list *wl) { struct word_list *tmp; xstr xs; /* 付属語だけのword_listで、長さ0のもやってくるので */ if (wl->len == 0) return; /**/ wl->last_part = PART_DEPWORD; /**/ set_features(wl); /* 文節境界の検索で使用するクラスの設定 */ anthy_set_seg_class(wl); /**/ xs.len = wl->part[PART_DEPWORD].len; xs.str = sc->ce[wl->part[PART_POSTFIX].from + wl->part[PART_POSTFIX].len].c; wl->dep_word_hash = anthy_dep_word_hash(&xs); if (wl->part[PART_POSTFIX].len) { xs.len = wl->part[PART_POSTFIX].len; xs.str = sc->ce[wl->part[PART_POSTFIX].from].c; } /* 同じ内容のword_listがないかを調べる */ for (tmp = sc->word_split_info->cnode[wl->from].wl; tmp; tmp = tmp->next) { if (word_list_same(tmp, wl)) { return ; } } /* wordlistのリストに追加 */ wl->next = sc->word_split_info->cnode[wl->from].wl; sc->word_split_info->cnode[wl->from].wl = wl; /* デバッグプリント */ if (anthy_splitter_debug_flags() & SPLITTER_DEBUG_WL) { anthy_print_word_list(sc, wl); } } struct word_list * anthy_alloc_word_list(struct splitter_context *sc) { return anthy_smalloc(sc->word_split_info->WlAllocator); } /* 後続の活用語尾、助詞、助動詞を付ける */ static void make_following_word_list(struct splitter_context *sc, struct word_list *tmpl) { /* このxsは自立語部の後続の文字列 */ xstr xs; xs.str = sc->ce[tmpl->from+tmpl->len].c; xs.len = sc->char_count - tmpl->from - tmpl->len; tmpl->part[PART_DEPWORD].from = tmpl->part[PART_POSTFIX].from + tmpl->part[PART_POSTFIX].len; if (tmpl->node_id >= 0) { /* 普通のword_list */ anthy_scan_node(sc, tmpl, &xs, tmpl->node_id); } else { /* 自立語がないword_list */ struct wordseq_rule rule; struct word_list new_tmpl; int i; int nr_rule = anthy_get_nr_dep_rule(); new_tmpl = *tmpl; /* 名詞35の後に続くルールに対して */ for (i = 0; i < nr_rule; ++i) { anthy_get_nth_dep_rule(i, &rule); if (anthy_wtype_get_pos(rule.wt) == POS_NOUN && anthy_wtype_get_scos(rule.wt) == SCOS_T35) { new_tmpl.part[PART_CORE].wt = rule.wt; new_tmpl.node_id = rule.node_id; new_tmpl.head_pos = anthy_wtype_get_pos(new_tmpl.part[PART_CORE].wt); anthy_scan_node(sc, &new_tmpl, &xs, new_tmpl.node_id); } } } } static void push_part_back(struct word_list *tmpl, int len, seq_ent_t se, wtype_t wt) { tmpl->len += len; tmpl->part[PART_POSTFIX].len += len; tmpl->part[PART_POSTFIX].wt = wt; tmpl->part[PART_POSTFIX].seq = se; tmpl->last_part = PART_POSTFIX; } /* 接尾辞をくっつける */ static void make_suc_words(struct splitter_context *sc, struct word_list *tmpl) { int i, right; wtype_t core_wt = tmpl->part[PART_CORE].wt; /* 数詞、名前、サ変名詞のいずれかに付属語は付く */ int core_is_num = 0; int core_is_name = 0; int core_is_sv_noun = 0; /* まず、接尾辞が付く自立語かチェックする */ if (anthy_wtype_include(anthy_wtype_num_noun, core_wt)) { core_is_num = 1; } if (anthy_wtype_include(anthy_wtype_name_noun, core_wt)) { core_is_name = 1; } if (anthy_wtype_get_sv(core_wt)) { core_is_sv_noun = 1; } if (!core_is_num && !core_is_name && !core_is_sv_noun) { return ; } right = tmpl->part[PART_CORE].from + tmpl->part[PART_CORE].len; /* 自立語の右側の文字列に対して */ for (i = 1; i <= sc->word_split_info->seq_len[right]; i++){ xstr xs; seq_ent_t suc; xs.str = sc->ce[right].c; xs.len = i; suc = anthy_get_seq_ent_from_xstr(&xs, sc->is_reverse); if (anthy_get_seq_ent_pos(suc, POS_SUC)) { /* 右側の文字列は付属語なので、自立語の品詞にあわせてチェック */ struct word_list new_tmpl; if (core_is_num && anthy_get_seq_ent_wtype_freq(suc, anthy_wtype_num_postfix)) { new_tmpl = *tmpl; push_part_back(&new_tmpl, i, suc, anthy_wtype_num_postfix); make_following_word_list(sc, &new_tmpl); } if (core_is_name && anthy_get_seq_ent_wtype_freq(suc, anthy_wtype_name_postfix)) { new_tmpl = *tmpl; push_part_back(&new_tmpl, i, suc, anthy_wtype_name_postfix); make_following_word_list(sc, &new_tmpl); } if (core_is_sv_noun && anthy_get_seq_ent_wtype_freq(suc, anthy_wtype_sv_postfix)) { new_tmpl = *tmpl; push_part_back(&new_tmpl, i, suc, anthy_wtype_sv_postfix); make_following_word_list(sc, &new_tmpl); } } } } static void push_part_front(struct word_list *tmpl, int len, seq_ent_t se, wtype_t wt) { tmpl->from = tmpl->from - len; tmpl->len = tmpl->len + len; tmpl->part[PART_PREFIX].from = tmpl->from; tmpl->part[PART_PREFIX].len += len; tmpl->part[PART_PREFIX].wt = wt; tmpl->part[PART_PREFIX].seq = se; } /* 接頭辞をくっつけてから接尾辞をくっつける */ static void make_pre_words(struct splitter_context *sc, struct word_list *tmpl) { int i; wtype_t core_wt = tmpl->part[PART_CORE].wt; int core_is_num = 0; /* 自立語は数詞か? */ if (anthy_wtype_include(anthy_wtype_num_noun, core_wt)) { core_is_num = 1; } /* 接頭辞を列挙する */ for (i = 1; i <= sc->word_split_info->rev_seq_len[tmpl->part[PART_CORE].from]; i++) { seq_ent_t pre; /* このxsは自立語部の前の文字列 */ xstr xs; xs.str = sc->ce[tmpl->part[PART_CORE].from - i].c; xs.len = i; pre = anthy_get_seq_ent_from_xstr(&xs, sc->is_reverse); if (anthy_get_seq_ent_pos(pre, POS_PRE)) { struct word_list new_tmpl; if (core_is_num && anthy_get_seq_ent_wtype_freq(pre, anthy_wtype_num_prefix)) { new_tmpl = *tmpl; push_part_front(&new_tmpl, i, pre, anthy_wtype_num_prefix); make_following_word_list(sc, &new_tmpl); /* 数の場合は接尾辞もくっつける */ make_suc_words(sc, &new_tmpl); }/* else if (anthy_get_seq_ent_wtype_freq(pre, anthy_wtype_prefix)) { new_tmpl = *tmpl; push_part_front(&new_tmpl, i, pre, anthy_wtype_prefix); make_following_word_list(sc, &new_tmpl); }*/ } } } /* wordlistを初期化する */ static void setup_word_list(struct word_list *wl, int from, int len, int is_compound, int is_weak) { int i; wl->from = from; wl->len = len; wl->is_compound = is_compound; /* partの配列を初期化する */ for (i = 0; i < NR_PARTS; i++) { wl->part[i].from = 0; wl->part[i].len = 0; wl->part[i].wt = anthy_wt_none; wl->part[i].seq = 0; wl->part[i].freq = 1;/* 頻度の低い単語としておく */ wl->part[i].dc = DEP_NONE; } /* 自立語のパートを設定 */ wl->part[PART_CORE].from = from; wl->part[PART_CORE].len = len; /**/ wl->mw_features = MW_FEATURE_NONE; wl->node_id = -1; wl->last_part = PART_CORE; wl->head_pos = POS_NONE; wl->tail_ct = CT_NONE; if (is_weak) { wl->mw_features |= MW_FEATURE_WEAK_SEQ; } } /* * ある独立語に対して、接頭辞、接尾辞、付属語を付けたものを * 文節の候補(=word_list)としてcacheに追加する */ static void make_word_list(struct splitter_context *sc, seq_ent_t se, int from, int len, int is_compound, int is_weak) { struct word_list tmpl; struct wordseq_rule rule; int nr_rule = anthy_get_nr_dep_rule(); int i; /* テンプレートの初期化 */ setup_word_list(&tmpl, from, len, is_compound, is_weak); tmpl.part[PART_CORE].seq = se; /* 各ルールにマッチするか比較 */ for (i = 0; i < nr_rule; ++i) { int freq; anthy_get_nth_dep_rule(i, &rule); if (!is_compound) { freq = anthy_get_seq_ent_wtype_freq(se, rule.wt); } else { freq = anthy_get_seq_ent_wtype_compound_freq(se, rule.wt); } if (freq) { /* 自立語の品詞はそのルールにあっている */ if (anthy_splitter_debug_flags() & SPLITTER_DEBUG_ID) { /* 品詞表のデバッグ用*/ xstr xs; xs.str = sc->ce[tmpl.part[PART_CORE].from].c; xs.len = tmpl.part[PART_CORE].len; anthy_putxstr(&xs); printf(" freq=%d rule_id=%d node_id=%d\n", freq, i, rule.node_id); } /* 遷移したルールの情報を転記する */ tmpl.part[PART_CORE].wt = rule.wt; tmpl.part[PART_CORE].freq = freq; tmpl.node_id = rule.node_id; tmpl.head_pos = anthy_wtype_get_pos(tmpl.part[PART_CORE].wt); /**/ tmpl.part[PART_POSTFIX].from = tmpl.part[PART_CORE].from + tmpl.part[PART_CORE].len; /**/ if (anthy_wtype_get_pos(rule.wt) == POS_NOUN || anthy_wtype_get_pos(rule.wt) == POS_NUMBER) { /* 接頭辞、接尾辞は名詞、数詞にしか付かないことにしている */ make_pre_words(sc, &tmpl); make_suc_words(sc, &tmpl); } /* 接頭辞、接尾辞無しで助詞助動詞をつける */ make_following_word_list(sc, &tmpl); } } } static void make_dummy_head(struct splitter_context *sc) { struct word_list tmpl; setup_word_list(&tmpl, 0, 0, 0, 0); tmpl.part[PART_CORE].seq = 0; tmpl.part[PART_CORE].wt = anthy_wtype_noun; tmpl.head_pos = anthy_wtype_get_pos(tmpl.part[PART_CORE].wt); make_suc_words(sc, &tmpl); } static int compare_hash(const void *kp, const void *cp) { const int *h = kp; const int *c = cp; return (*h) - ntohl(*c); } static int check_weak(xstr *xs) { const int *array = (int *)weak_word_array; int nr; int h; if (!array) { return 0; } nr = ntohl(array[1]); h = anthy_xstr_hash(xs); if (bsearch(&h, &array[16], nr, sizeof(int), compare_hash)) { return 1; } return 0; } /* コンテキストに設定された文字列の部分文字列から全てのword_listを列挙する */ void anthy_make_word_list_all(struct splitter_context *sc) { int i, j; xstr xs; seq_ent_t se; struct depword_ent { struct depword_ent *next; int from, len; int is_compound; int is_weak; seq_ent_t se; } *head, *de; struct word_split_info_cache *info; allocator de_ator; weak_word_array = anthy_file_dic_get_section("weak_words"); info = sc->word_split_info; head = NULL; de_ator = anthy_create_allocator(sizeof(struct depword_ent), 0); xs.str = sc->ce[0].c; xs.len = sc->char_count; anthy_gang_load_dic(&xs, sc->is_reverse); /* 全ての自立語を列挙 */ /* 開始地点のループ */ for (i = 0; i < sc->char_count ; i++) { int search_len = sc->char_count - i; int search_from = 0; if (search_len > 30) { search_len = 30; } /* 文字列長のループ(長い方から) */ for (j = search_len; j > search_from; j--) { /* seq_entを取得する */ xs.len = j; xs.str = sc->ce[i].c; se = anthy_get_seq_ent_from_xstr(&xs, sc->is_reverse); /* 単語として認識できない */ if (!se) { continue; } /* 各、部分文字列が単語ならば接頭辞、接尾辞の 最大長を調べてマークする */ if (j > info->seq_len[i] && anthy_get_seq_ent_pos(se, POS_SUC)) { info->seq_len[i] = j; } if (j > info->rev_seq_len[i + j] && anthy_get_seq_ent_pos(se, POS_PRE)) { info->rev_seq_len[i + j] = j; } /* 発見した自立語をリストに追加 */ if (anthy_get_seq_ent_indep(se) && /* 複合語で無い候補があることを確認 */ anthy_has_non_compound_ents(se)) { de = (struct depword_ent *)anthy_smalloc(de_ator); de->from = i; de->len = j; de->se = se; de->is_compound = 0; de->is_weak = check_weak(&xs); de->next = head; head = de; } /* 発見した複合語をリストに追加 */ if (anthy_has_compound_ents(se)) { de = (struct depword_ent *)anthy_smalloc(de_ator); de->from = i; de->len = j; de->se = se; de->is_compound = 1; de->is_weak = 0; de->next = head; head = de; } } } /* 発見した自立語全てに対して付属語パターンの検索 */ for (de = head; de; de = de->next) { make_word_list(sc, de->se, de->from, de->len, de->is_compound, de->is_weak); } /* 自立語の無いword_list */ for (i = 0; i < sc->char_count; i++) { struct word_list tmpl; setup_word_list(&tmpl, i, 0, 0, 0); if (i == 0) { make_following_word_list(sc, &tmpl); } else { int type = anthy_get_xchar_type(*sc->ce[i - 1].c); if ((type & (XCT_CLOSE | XCT_SYMBOL)) && !(type & XCT_PUNCTUATION)) { /* 句読点以外の記号 */ make_following_word_list(sc, &tmpl); } } } /* 先頭に0文字の自立語を付ける */ make_dummy_head(sc); anthy_free_allocator(de_ator); }