【戻る】 【進む】   目次   後     48. 動的プログラミング - 実例

実例

GREP プログラム
#!/usr/bin/env ruby
require "paramclass"
getopts("plh")
$KCODE = "SJIS"

module ParamClass
  def [](*x)
    klass = type.new(self)
    klass.class_eval do
      x.each do |y|
	include y
      end
    end
    klass
  end
end

class Printer
  def initialize(format, out = nil)
    @out = out
    extend format
  end
  def feed(record)
    line = format(record)
    @out << line if @out
    line
  end

#===== output formats
  module Plain
    def format(record)
      record.body
    end
  end
  module LinePc
    def format(record)
      line = "#{record.fname}:#{record.num+1}: "
      line << record.matchdata.collect { |d|
	"%2.f%%" % (d.pre_match.size.to_f / record.body.size * 100)
      }.join(", ")
      line << "\n"
    end
  end
  module Lead
    def format(record)
      line = record.matchdata.collect { |d|
	"#{record.fname}:#{record.num+1}: " +
	  (d[0] + d.post_match.sub(/\n\z/, '')).scan(/.{1,30}/ms)[0]
      }.join("\n")
      line << "\n"
    end
  end
end

class Pattern
  def initialize(pat)
    @pat = pat
  end
  def ===(data)
    data.scan(@pat)
    !data.matchdata.empty?
  end
end

class INT
  include Comparable
  def initialize; @n = 0; end
  def method_missing(meth, *args)
    @n.send meth, *args
  end
  def succ; @n += 1; end
  def to_i; @n; end
  def to_s; @n.to_s; end
end

class Volume
  include Enumerable
  def initialize(fnames, text, pager, record, scanner)
    @fnames, @text, @pager, @record, @scanner =
      fnames, text, pager, record, scanner
    @attr = {:vnum => INT.new, :vhit => INT.new}
  end
  def each(&b)
    @fnames.each do |fname|
      @attr[:fname] = fname
      yield @text.new(@pager, @record[@scanner], @attr)
    end
  end
end

class Text
  include Enumerable
  attr_reader :body
  def initialize(pager, record, attr)
    unless attr.is_a? Hash
      attr = {:fname => attr, :vnum => INT.new, :vhit => INT.new}
    end
    @pager, @record, @attr = pager, record, attr
    @name = @attr[:fname]
    @attr[:num] = INT.new
    @attr[:hit] = INT.new
    extend @pager
  end
  def each(&b)
    open(@name) do |@body|
      each_record do |page|
	yield @record.new(page, @attr)
	@attr[:num].succ
	@attr[:vnum].succ
      end
    end
  end

#===== data structures in Text
  module Liner
    def each_record(&b)
      @body.each(&b)
    end
  end

  require "kconv"
  module Htmls
    def each_record(&b)
      Kconv.tosjis(@body.read).gsub(/<.*?>/, '').each(&b)
    end
  end
end

class Record
  extend ParamClass
  attr_reader :body, :fname, :num, :hit, :vnum, :vhit, :matchdata
  def initialize(body, attr)
    @body, @attr = body, attr
    @matchdata = []
    @fname, @num, @hit, @vnum, @vhit =
      @attr[:fname], @attr[:num], @attr[:hit], @attr[:vnum], @attr[:vhit]
  end

#===== scanner in Record
  module StdScan
    def scan(pat)
      @body.scan(pat) do |x|
	@matchdata << $~
	@attr[:hit].succ
	@attr[:vhit].succ
      end
      @matchdata
    end
  end
end
#=====

#===== main
unless pat = ARGV.shift
  print <<-USAGE
  -p ... show only percentages in sentence
  -l ... show leading strings
  -h ... search HTML file
  USAGE
  exit
end

format, pager, scanner = if $OPT_p
  [Printer::LinePc, Text::Liner, Record::StdScan]
elsif $OPT_l
  [Printer::Lead, Text::Liner, Record::StdScan]
elsif $OPT_h
  [Printer::Plain, Text::Htmls, Record::StdScan]
else
  [Printer::Plain, Text::Liner, Record::StdScan]
end

printer = Printer.new(format, $stdout)
pattern = Pattern.new(pat)
Volume.new(ARGV, Text, pager, Record, scanner).each do |text|
  text.grep(pattern) do |record|
    printer.feed(record)
  end
end
#=====
grep.rb
 
【戻る】 【進む】   目次   後     48. 動的プログラミング - 実例