asearch
増井さんの論文を読んで実験中。
参考文献
<URL:http://pitecan.com/papers/JUS91/JUS91.pdf>
asearch.rb
module ASearch class Shift def initialize(seq) @bits = Hash.new @goal = 1 @size = 0 push_seq(seq) end attr_reader :size, :goal def [](key) @bits.fetch(key, 0) end private def push(key) @goal <<= 1 @size += 1 return if key.nil? @bits[key] = @bits.fetch(key, 0) | @goal end def push_seq(seq) seq.each do |key| push(key) end end end class Machine def initialize(goal, mismatch) @goal = goal @layer = Array.new(1, 1) + Array.new(mismatch, 0) @mismatch = mismatch @cursor_mask = 1 end def step(mask) (0..@mismatch).inject(0) do |low, x| prev = @layer[x] @layer[x] = (((prev << 1) | (low << 2)) & mask) | low | low << 1 prev end @layer[0] |= 1 @cursor_mask <<= 1 valid?(@cursor_mask) end def valid?(goal) @layer.inject(goal) do |g, v| return true if (v & g) != 0 (g << 1) | (g >> 1) | g end return false end def done? valid?(@goal) end end class Scan def initialize(pat) data = prepare_data(pat) @shift = make_shift(data) end def prepare_data(obj) obj.to_a end def make_shift(seq) Shift.new(seq) end def make_machine(mismatch) Machine.new(@shift.goal, mismatch) end def match(seq, mismatch=1) m = make_machine(mismatch) prepare_data(seq).each do |key| return false unless m.step(@shift[key]) end m.done? end def sub_seq(ary, first, mismatch) m = make_machine(mismatch) found = nil last = first + @shift.size + mismatch (first..last).each do |i| return found unless m.step(@shift[ary[i]]) found = i if m.done? end found end def scan(seq, mismatch) ary = prepare_data(seq) len = ary.size - @shift.size + mismatch + 1 len.times do |s| e = sub_seq(ary, s, mismatch) next unless e yield(s, e, ary[s..e]) end end end class Text < Scan def prepare_data(text) text.split('') end end end if __FILE__ == $0 pattern = ARGV.shift str = ARGV.shift mismatch = (ARGV.shift || 1).to_i as = ASearch::Text.new(pattern) p as p as.match(str, mismatch) as.scan(str, mismatch) do |*s| p s end require 'scan_clone' class ASearch::Text def make_shift(seq) ASearch::ArrayShift.new(seq) end end as = ASearch::Text.new(pattern) p as p as.match(str, mismatch) as.scan(str, mismatch) do |*s| p s end end