QuickSilverのあれ
どんなもんかと実装してみた.といってもObjective-Cよく分からなかったので,Ruby版のをベースに.実際速さを求めるなら,本家のようにNSRangeみたいな軽量な構造体使ったり,単一関数の再帰の方が速いかも.
なんにせよ,これだけ短いやつであんな探索出来るんだなぁとしみじみ.
// Written in the D programming language. import std.array : empty; import std.conv : to; import std.regex : regex, match; import std.string : indexOf, CaseSensitive; import std.traits : ReturnType, isSomeString; /** * Abbreviation scoring for string by Alcor * * If you consider product level, you should use light-weight range instead of slice. */ struct AbbreviationScorer(String) if (isSomeString!(String)) { private: alias ReturnType!(regex!(String)) Regex; enum ERROR = -1.0; // @@@BUG@@@ Give me Nullable! /* @@@BUG@@@ std.regex.match can't use const object */ static /* immutable */ Regex ReWS, ReUpper; static this() { ReWS = regex(to!(String)("\\s$")); ReUpper = regex(to!(String)("[A-Z]")); } String source_; public: /** * Initializes scorer object. * * Params: * source = to search string. */ this(String source) { source_ = source; } /** * Computes abbreviation score using _abbr_ string. * * Params: * abbr = abbreviated string. * Out: * Return value must not be negative. * Returns: * score between 0.0 and 1.0. */ double compute(String abbr) out(result) { /* @@@BUG@@@ http://d.puremagic.com/issues/show_bug.cgi?id=3667 assert(result >= 0.0); */ } body { if (abbr.empty) return 0.9; if (source_.length < abbr.length) return 0.0; auto abbrLength = abbr.length; foreach (i; 0..abbrLength) { auto result = scoreFor(abbr, abbrLength - i); if (result != ERROR) return result; } return 0.0; } private: double scoreFor(String abbr, uint pivot) { auto ahead = abbr[0..pivot]; auto atail = abbr[pivot..$]; auto found = source_.indexOf(ahead, CaseSensitive.no); if (found == -1) return ERROR; auto tail = source_[found + pivot..$]; auto tailScore = abbreviationScorer(tail).compute(atail); if (!(tailScore > 0.0)) return ERROR; return (to!(double)(found) + pivot - penalty(found) + tailScore * tail.length) / source_.length; } double penalty(int found) { /+ @@@BUG@@@ std.regex.Regex can't construct at compile time static /* immutable */ Regex ReWS = regex("\\s$"); static /* immutable */ Regex ReUpper = regex("[A-Z]"); +/ if (found == 0) return 0.0; auto skipped = source_[0..found]; auto matched = skipped.match(ReWS); if (!matched.empty) { auto length = matched.hit.length; return length + (skipped.length - length) * 0.15; } matched = source_[found..$].match(ReUpper); if (!matched.empty) { matched = skipped.match(ReUpper); auto length = matched.empty ? 0 : matched.hit.length; return length + (skipped.length - length) * 0.15; } return skipped.length; } } /** * Shortcut for AbbreviationScorer construction. */ AbbreviationScorer!(String) abbreviationScorer(String)(String source) { return typeof(return)(source); } version(unittest) { import std.math : approxEqual; import std.typetuple : TypeTuple; } unittest { struct Test { string source; string[] abbrs; double[] results; } static Test[] tests = [ {"hello", ["h", "he", "hel", "helo", "hello", "llo", "lo", "ho", "hx"], [0.92, 0.94, 0.96, 0.80, 1.00, 0.60, 0.40, 0.40, 0.00]}, {"HelloWorld", ["hw"], [0.90]} ]; foreach (Char; TypeTuple!(char, wchar, dchar)) { alias immutable(Char)[] String; foreach (test; tests) { auto scorer = abbreviationScorer(to!(String)(test.source)); foreach (i, abbr; test.abbrs) { assert(approxEqual(scorer.compute(to!(String)(abbr)), test.results[i])); } } } }