//--------------------------------------------------------------------------- // Learning Regular Expression from postiive/negative training sets. // // author: k.inaba (http://www.kmonos.net/) // license: NYSL 0.9982 (http://www.kmonos.net/nysl/) // // Outcome from PRML Hackathon #1 // http://atnd.org/events/1422 // Thank you for organizing such a great event! > naoya_t //--------------------------------------------------------------------------- import std.stdio; import std.conv; import std.numeric; import std.algorithm; import std.array; import std.random; import std.range; version = ZLearn; //--------------------------------------------------------------------------- // ユーティリティ //--------------------------------------------------------------------------- auto array(Range)(Range r) // なぜ標準ライブラリにないのか... { ElementType!(Range)[] arr; foreach(e; r) arr ~= e; return arr; } template Set(T) // すごい適当極まりないひどさの固まりのSetのような何か { alias T[] Set; } T[] set_add(T)( T[] set, T[] t... ) { set = set ~ t; set.sort; return array(uniq(set)); } void destructive_add(T)( ref T[] set, T t ) { set = set ~ t; set.sort; set = array(uniq(set)); } bool set_has(T)( T[] set, T[] t... ) { return !empty( findAmongSorted(set, t) ); } //--------------------------------------------------------------------------- // 非決定性オートマトン //--------------------------------------------------------------------------- class NFA { unittest { auto A = NFA.from_string("Hello"); assert( A.accept("Hello") ); assert( !A.accept("Hell") ); assert( !A.accept("Helloo") ); auto B = NFA.from_string("World"); assert( B.accept("World") ); assert( !B.accept("Worl") ); assert( !B.accept("WorldW") ); auto C = NFA.merge(A,B); assert( C.accept("Hello") ); assert( !C.accept("Hell") ); assert( !C.accept("Helloo") ); assert( C.accept("World") ); assert( !C.accept("Worl") ); assert( !C.accept("WorldW") ); C = NFA.randomize(C); assert( C.accept("Hello") ); assert( !C.accept("Hell") ); assert( !C.accept("Helloo") ); assert( C.accept("World") ); assert( !C.accept("Worl") ); assert( !C.accept("WorldW") ); auto D = NFA.mergeState(B, 0, 5); assert( D.accept("World") ); assert( !D.accept("Worl") ); assert( !B.accept("WorldWorld") ); assert( D.accept("WorldWorld") ); assert( D.accept("WorldWorldWorld") ); auto E = NFA.mergeState(A, 1, 3); assert( E.accept("Hello") ); assert( !E.accept("Hell") ); assert( !E.accept("Helloo") ); assert( E.accept("Hlo") ); assert( !A.accept("Hlo") ); version(ZLearn) { auto F = NFA.from_string("xzz"); assert( F.accept("xzz") ); assert( F.accept("xab") ); assert( !F.accept("xzzz") ); } } alias int State; int stateNum; // 状態の総数 Set!(State) ini; // 開始状態 Set!(State)[char][State] delta; // 遷移関数 Set!(State) fin; // 受理状態 string toString() { return "<< #st: " ~ to!(string)(stateNum) ~ "\n" " ini: " ~ to!(string)(ini) ~ "\n" " fin: " ~ to!(string)(fin) ~ "\n" " dlt: " ~ to!(string)(delta) ~ " >>"; } bool accept_none( string[] ss ) { foreach(s; ss) if( accept(s) ) return false; return true; } bool accept( string s ) { Set!(State) qs = ini; // 初期状態 foreach(c; s) // 一文字読んで { Set!(State) ps; foreach(q; qs) if( q in delta && c in delta[q] ) ps = ps.set_add( delta[q][c] ); qs = ps; // 次の状態に遷移 } foreach(q; qs) if( fin.set_has(q) ) return true; // 受理状態にあればaccept return false; } // 空のオートマトンを作る static NFA empty() { return new NFA; } private this() {} // 文字列からそれだけacceptするオートマトンを作る static NFA from_string( string s ) { NFA C = NFA.empty; C.stateNum = s.length+1; for(int q=0; q B.stateNum ) A = B; } return A; } NFA moil( string[] Dp, string[] Dn ) { NFA A = NFA.empty; // 正の例を受理するように(負の例にマッチしないように気をつけつつ)1個ずつ増やしてく for(int i=0; i