Last update: $Id: index.html,v 1.11 2001/05/30 21:08:11 taku-ku Exp $;
Darts は, Double-Array [Aoe 1989]を構築するための シ ンプルな C++ Template Library です. Double-Array は Trie を表現する ためのデータ構造で, 文字列からポインタ(整数)への写像を格納します. 検索は, 検索する文字列の長さに比例する時間で行なえ, 非常に高速です. オリジナル の Double-Array は, 動的に key の追加削除を行えるような 枠組ですが, Darts は ソート済の辞書を一括して Double-Array に変換することに機能を絞っています.
Exact match はもちろん, Common Prefix Search を非常に高速に行うことができます. 形態素解析器の辞書に用いるとかなりの高速化に繋がります.
また, ソースも 400行程度と非常にコンパクトなので, Double-Array の構築アルゴリズムや 検索アルゴリズムの勉強にも良い教材だと思います. 他の言語への移植もそう難 しくないでしょう.namespace Darts { template <class NodeType, class NodeUType, class ArrayType, class ArrayUType, class LengthFunc = Length<NodeType> > class DobuleArrayImpl { public: DoubleArrayImpl(); ~DoubleArrayImpl(); int set_array(void *ptr); void clear (); size_t get_size (); size_t get_nonzero_size (); int build (const size_t str_size, NodeType **str, size_t *len = 0, ArrayType *val = 0, int (*pg)(const char*, size_t, size_t) = 0); int open (const char *file, const char *mode = "r", size_t offset = 0, size_t _size = 0); int open (FILE *fp, size_t _size = 0); int save (const char *file, const char *mode = "w", size_t offset = 0); int save (FILE *fp); DartsInt search (const NodeType *key, size_t len = 0, size_t pos = 0) size_t common_prefix_search (const NodeType *key, ArrayType *result, size_t len = 0, size_t pos = 0) }; typedef Darts::DoubleArrayImpl<DartsChar, DartsUChar, DartsInt, DartsUInt, DARTS_LENGTH_FUNC > DoubleArray; };
NodeType | Trie の各ノードの型です. default は char (DartsChar)に設定されます. 一般的な C 文字列の検索なら, char 以外に設定する必要はありません. |
NodeUType | Trie の各ノードの型を符号無し整数に変換した型です.
default は unsigned char (DartsUChar)に設定されます. 一般的な C 文字列の検索なら, unsigned char 以外に設定する必要はありません. |
ArrayType | Double-Array の Base の要素に使用される型です. default は signed の 32bit 整数(DartsInt)に設定されます |
ArrayUType | Double-Array の Check の要素に使用される型です. default は unsigned の 32bit 整数(DartsUInt)に設定されます |
LengthFunc | NodeType の配列を引数にしたときに, その配列のサイズを返す関数オブジェクトを 指定します. 内部呼び出しに operator () を使っている ので, () を overload しておく必要があります. NodeType が, char の場合は, strlen を wrap した 関数オブジェクト. それ以外は 0 を終了条件とみなして配列のサイズを計算します. |
32bit 整数の定義は OS, コンパイラ依存です.
実際には configure script が, 自動的に判別し,32bit になるように選択してくれます.
もし64 bit 整数を用いる場合は, template 引数で個々に指定してください.
Darts::DoubleArrayImpl::build(const size_t size,
NodeType **str, size_t *len = 0, ArrayType *val = 0, int (*progress_func)(const char*, size_t, size_t) = 0)
Darts::DoubleArrayImpl::search(const NodeType *key,
size_t len = 0, size_t pos = 0)
Darts::DoubleArrayImpl::common_prefix_search
(const NodeType *key, ArrayType *result, size_t len = 0, size_t pos = 0)
Darts::DoubleArrayImpl::save(const char *file,
const char *mode = "w", size_t offset = 0)
Darts::DoubleArrayImpl::save(FILE *fp)
Darts::DoubleArrayImpl::open
(const char *file, const char *mode = "r", size_t offset = 0, size_t size = 0)
Darts::DoubleArrayImpl::open
(FILE *fp, size_t _size = 0)
Darts::DoubleArrayImpl::get_size()
Darts::DoubleArrayImpl::get_unit_size()
Darts::DoubleArrayImpl::get_nonzero_size()
Darts::DoubleArrayImpl::set_array(void *ptr)
Darts::DoubleArrayImpl::clear()
#include <iostream> #include <darts.h> int main (int argc, char **argv) { char *str[] = { "ALGOL", "ANSI", "ARCO", "ARPA", "ARPANET", "ASCII" }; int val[] = { 1, 2, 3, 4, 5, 6 }; Darts::DoubleArray da; da.build (6, str, 0, val); cout << da.search("ALGOL") << endl; cout << da.search("ANSI") << endl; cout << da.search("ARCO") << endl;; cout << da.search("ARPA") << endl;; cout << da.search("ARPANET") << endl;; cout << da.search("ASCII") << endl;; cout << da.search("APPARE") << endl; da.save("some_file"); } 実行結果 1 2 3 4 5 6 -1標準入力から対話的に Double-Array に対し Common Prefix Search を行う
#include <iostream> #include <string> #include <algorithm> #include <darts.h> int main (int argc, char **argv) { Darts::DoubleArray da; if (da.open("some_file") == -1) exit(-1); DartsInt r [1024]; char buf [1024]; while (fgets(buf, (int)sizeof(buf), stdin)) { buf[strlen(buf)-1] = '\0'; size_t result = da.common_prefix_search(buf, r); if (result == 0) { cout << buf << ": not found" << endl; } else { cout << buf << ": found, num=" << result << " "; copy (r, r + result, ostream_iterator<DartsInt>(cout, " ")); cout << endl; } } }他のサンプルとして, mkdarts.cc や darts.cc をご覧ください
% ./mkdart DictionaryFile DoubleArrayFileソート済みの辞書を DoubleArrayFile に変換します.
% ./darts DoubleArrayFileDoubleArrayFile に対し対話的に common prefix search を行います.
% cd tests % head -10 linux.words ALGOL ANSI ARCO ARPA ARPANET ASCII .. % ../mkdarts linux.words dar Making Double Array: 100% |*******************************************| Done!, Compression Ratio: 94.6903 % % ../darts dar Linux Linux: found, num=2 3697 3713 Windows Windows: not found LaTeX LaTeX: found, num=1 3529
struct Unit { ArrayType base; ArrayUType check; };のような Unit の配列として表現されています. このファイルの入出力は, fread, fwrite を用いて,
fread ((Unit *)array, sizeof(Unit), size, fp); fwrite ((Unit *)array, sizeof(Unit), size, fp);のように, 行われています. ごくまれですが, 構造体の配列の間に gap を挿入 する OS/コンパイラがあるそうです. したがって, このような読み書きは厳密に言えば移植性がありません. 私が使用している g++ 2.95.2 と Tru64 に附属の cxx は問題ないようなので, このままの状態にしていますが, いずれ移植性を高めるために書きなおすつもりです.
% ./configure % make % make check あとは, darts.h を include して使う
$Id: index.html,v 1.11 2001/05/30 21:08:11 taku-ku Exp $;
taku-ku@is.aist-nara.ac.jp