[[プログラミング言語/Ruby/コードリーディング]]

#contents
#br

-----

**素数判定 [#u53871db]
ま さ に 外 道(I)
 require 'mathn.rb'
 
 class PrimeDistincter
   def initialize
     @prime = Prime.new
     @cache = []
   end
   
   def prime?(x)
     extend_cache x if @cache.empty? || x > @cache.last
     @cache.include? x
   end
   
 private
   def extend_cache(lim)
     @prime.each do |x|
       break if x > lim
       @cache.push x
     end
   end
 end
 
 #使用例
 ppred = PrimeDistincter.new
 [1, 10, 2, 5, 7].each do |x|
   p ppred.prime?(x)
 end


**素数を求める [#u934120e]
ま さ に 外 道(II)
 require 'mathn.rb'
 
 def primes(lim)
   prms = []
   prime = Prime.new
   prime.each do |x|
     break if x > lim
     prms.push x
   end
   prms
 end
 
 puts primes(100).join(" ")


**うるう年測定 [#jd6b8ea7]
 require 'readline'
 
 module LeapYearDistincter
   LEAP_YEAR_JUDGE_TABLE = [
     [
       [true, false],
       [true, true]
     ],
     [
       [],
       []
     ]
   ]
   
   def leap_year?(y)
     f, h, fh = [4, 100, 400].collect{|x| (y % x).zero? ? 0 : 1 }
     LEAP_YEAR_JUDGE_TABLE[f][h][fh] or false
   end
   module_function :leap_year?
 end
 
 
 def recept_input
   while buffer = Readline.readline("Year? > ", true)
     yield buffer
   end
 end
 
 
 recept_input do |line|
   y = line.to_i
   if y >= 0
     puts "#{y}: #{LeapYearDistincter.leap_year?(y)}"
   else
     puts "#{y}: invalid year."
   end
 end


**ハノイの塔 [#wfd293b7]
 #Usage: ruby hanoi.rb [n number of disc]
 
 class Hanoi
   def initialize(n, h)
     @height = n
     @from = h[:from]
     @to = h[:to]
     @work = h[:work]
   end
   
   attr_reader :height, :from, :to, :work
   
   def each(&block)
     do_hanoi @height, @from, @to, @work, &block
   end
   
 private
   def do_hanoi(n, from, to, work, &block)
     if n == 1
       yield n, from, to
     else
       do_hanoi n - 1, from, work, to, &block
       yield n, from, to
       do_hanoi n - 1, work, to, from, &block
     end
   end
 end
 
 
 ##使用例
 num = (ARGV[0] || 3).to_i
 num >= 0 or raise "#{num}: Invalid Number"
 hanoi = Hanoi.new(num, :from => "A", :to => "B", :work => "C")
 hanoi.each do |n, from, to|
   puts "#{n}: #{from} -> #{to}"
 end


**転置行列 [#hef0d150]
 require 'matrix'
 
 def parse_matrix(src)
   matrix = []
   src.each do |line|
     line.chomp!
     next if line.empty?
     matrix.push line.split(/\s+/).map{|x| x.to_i }
   end
   Matrix.rows(matrix)
 end
 
 m = parse_matrix(ARGF.read)
 puts m.transpose.to_a.map{|v| v.join(" ") }


**数当てゲーム [#j5d9808f]
 #Usage: ruby guess_number.rb [n Difficulty]
 
 srand
 require 'readline'
 
 
 def try_answer(prompt)
   while buf = Readline.readline(prompt, true)
     yield buf
   end
 end
 
 
 difficulty = (ARGV[0] || 1).to_i
 difficulty >= 1 or raise "#{difficulty}: Invalid difficulty direction"
 
 answer = rand(10 * difficulty + 1)
 judge_table = ["Collect!", "Bigger.", "Smaller."]
 
 try_answer("guess? > ") do |line|
   try = line.to_i
   result = try <=> answer
   puts "#{try} is #{judge_table[result]}"
   break if result.zero?
 end


**数当てゲーム その2(Hit&Blow) [#d5c83be5]
 def help
   warn <<-HELP
 hit_and_blow.rb: play Hit & Blow game.
 usage: ruby hit_and_blow.rb [-option] [DIFFICULTY]
 
   h, --help     show this help
   v, --version  show version
   HELP
   exit 0
 end
 
 def version
   warn "hit_and_blow 0.0.1"
   exit 0
 end
 
 
 require 'readline'
 srand
 
 #n桁以下の数を乱数生成
 def generate_answer(col)
   bottom = 10 ** (col - 1)
   rand(10 ** col - bottom) + bottom
 end
 
 #答えの数の妥当性チェック(全桁の数がユニークか)
 def valid_answer?(n)
   n.to_s.split(//).uniq! ? false : true
 end
 
 def generate_valid_answer(col)
   answer = nil
   loop do
     answer = generate_answer(col)
     valid_answer?(answer) and break
   end
   answer
 end
 
 
 #数を桁ごとに分ける(col指定で0補完)
 def num_split_cols(n, col=nil)
   width = col || n.to_s.length
   sprintf("%0#{width}d", n).split(//).map{|n| n.to_i }
 end
 
 
 def input_number(prompt)
   while line = Readline.readline(prompt, true)
     n = line.to_i
     if n < 0
       warn "do not input negative number."
     else
       yield n
     end
   end
 end
 
 
 ARGV.delete_if do |x|
   case x
   when "-h", "--help"
     help
   when "-v", "--version"
     version
   end
 end
 
 
 n = (ARGV[0] || 4).to_i
 n > 0 or raise "#{n}: Invalid difficulty specification."
 answer = generate_valid_answer(n)
 answer_set = num_split_cols(answer, n)
 p answer if $DEBUG
 
 
 result = []
 def result.clear(width)
   fill(0, 0, width)
 end
 def result.count(n)
   join.count(n.to_s)
 end
 result.clear(n)
 
 
 input_number("try! >") do |reply|
   reply_set = num_split_cols(reply, n)
   
   #check `Hit'
   reply_set.each_with_index do |r, i|
     result[i] += 1 if r == answer_set[i]
   end
   
   #check `Blow'
   reply_set.each_with_index do |r, i|
     result[i] += 1 if answer_set.include?(r)
   end
   
   hit, blow = result.count(2), result.count(1)
   print "#{hit} Hit  &  #{blow} Blow\n"
   break if hit == n
   result.clear(n)
 end


**FizzBuzz [#jd36cc1e]
 def fizz_buzz(n)
   ret = "#{["Fizz"][n % 3]}#{["Buzz"][n % 5]}"
   ret.empty? ? n.to_s : ret
 end
 
 puts (1..100).collect{|n| fizz_buzz n }


**カレンダー出力 [#c7b520e4]
 #使い方: コマンドラインにテキトーに日付を入れで動かす
 
 require 'optparse'
 require 'time'
 require 'forwardable'
 require 'stringio'
 
 
 
 class Month
   extend Forwardable
   
   [:Sunday, :Monday, :Thuesday, :Wednesday,
     :Thursday, :Friday, :Saturday].each_with_index do |const, n|
     const_set const, n
   end
   
 private
   def initialize(datum)
     @datum = datum
     @days = [nil]
     load_days datum, @days
   end
   
   def load_days(datum, days)
     1.upto(31) do |day|
       date = Time.local(datum.year, datum.month, day)
       break unless date.month == datum.month
       days.push date
     end
   end
   
 public
   def_delegators "@datum", :year, :zone
   
   def name(abbrev=false)
     @datum.strftime(abbrev ? "%b" : "%B")
   end
   
   def_delegators "@days", :[], :first, :last
   
   def each
     @days.each{|d| yield d }
   end
 end
 
 
 class CalenderRender
   def initialize(mon)
     @mon = mon
   end
   
   def run(port=$stdout)
     weeks = calender_matrix(@mon)
     show_calender header_render(@mon) + calender_render(weeks)
   end
   
 private
   def show_calender(src, port=$stdout)
     port.print src
   end
   
   def calender_matrix(mon)
     weeks = []
     week = Array.new(7)
     1.upto(mon.last.day) do |n|
       week[mon[n].wday] = mon[n].day
       if mon[n].wday == Month::Saturday
         weeks.push week
         week = Array.new(7)
       end
     end
     weeks.push week
     weeks
   end
 end
 
 
 class CUICalenderRender < CalenderRender
 private
   def calender_matrix(mon)
     weeks = super
     weeks.unshift %w[Sun Mon Tue Wed Thu Fri Sat]
     weeks
   end
   
   def header_render(mon)
     col = 3 * 7 + (7 - 1)
     cap = "#{mon.name}, #{mon.year}"
     padding = (col - cap.length) / 2
     head = sprintf("%#{padding}s%s%#{padding}s", "", cap, "")
     sprintf("%#{col}s\n", head)
   end
   
   def calender_render(weeks)
     weeks.collect{|week|
       if week.all?{|day| day.nil? }
         ""
       else
         week.collect{|day| sprintf("%3s", day.to_s)}.join(" ")
       end
     }.join("\n")
   end
 end
 
 
 class HTMLCalenderRender < CalenderRender
   def header_render(mon)
     h = "#{mon.name}, #{mon.year}"
     <<-HTML
 <!-- generated by calender.rb -->
 <?xml version="1.0" ?>
 <!DOCTYPE html 
   PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <title>#{h}</title>
 </head>
 <body>
 <table>
   <tr>
     <th colspan="7">#{h}</th>
   </tr>
     HTML
   end
   
   def calender_render(weeks)
     buf = StringIO.new
     weeks.each do |week|
       buf.puts "\t<tr>"
       week.each do |day|
         buf.puts "\t\t<td>#{day}</td>" if day
       end
       buf.puts "\t</tr>"
     end
     buf.puts "</table>\n</body>\n</html>"
     buf.string
   end
 end
 
 
 class GUICalenderRender < CalenderRender
   #作成中
 end
 
 
 
 render_class = CUICalenderRender
 render_class_table = {
   "cui"  => CUICalenderRender,
   "gui"  => GUICalenderRender,
   "html" => HTMLCalenderRender
 }
 
 op = OptionParser.new
 op.on("-m MODE", "--show-mode=MODE",
         "specify calender type(cui|gui|html)"){|v|
       render_class = render_class_table[v.downcase] || CUICalenderRender
 }
 
 op.on("-h", "--help", "show this help"){
   warn op.help
   exit 0
 }
 op.on("-v", "--version", "show version"){
   warn "calender.rb 0.0.1"
   exit 0
 }
 
 op.parse!(ARGV)
 
 if ARGV.size > 1
   ARGV.replace [ARGV.join("/")]
 end
 
 datum = ARGV[0] ? Time.parse(ARGV[0]).getlocal : Time.now
 
 
 mon = Month.new(datum)
 render = render_class.new(mon)
 render.run


**配列いじり [#w04579bf]
 def exclude_without_first!(array)
   fst = array.first.dup
   array.fill(0)
   array[0] = fst
   array
 end


**Caesar暗号解読 [#gadcc14b]
総当り法による力任せ解読。
 ### Usage: ruby caesar.rb HINT [CODE_FILE]
 
 module CaesarCode
   class CharTable
     def initialize(char_set)
       @char_set = char_set
       @shifted = char_set.dup
       @shift_count = 0
       def @shifted.rotate
         temp = self.shift
         self.push temp
         self
       end
       @table = {}
       init_table
     end
     
     attr_reader :shift_count
     
     def [](key)
       @table[key]
     end
     
     def shift
       @shifted.rotate
       init_table
       @shift_count += 1
       self
     end
     
   private
     def init_table
       @table.clear
       @char_set.zip(@shifted).each do |x|
         @table[x[0]] = x[1]
       end
     end
   end
 end
 
 
 
 def convert(base, table)
   base.split(//).collect{|x| table[x] or x }.join("")
 end
 
 raise ArgumentError, "hint word not given" if ARGV.empty?
 hint = ARGV.shift
 char_set = "abcdefghijklmnopqrstuvwxyz .,-".split(//)
 table = CaesarCode::CharTable.new(char_set)
 src = ARGF.read
 org = src[0]
 
 until src.include?(hint)
   src = convert(src, table.shift)
   raise "can't decode" if org == src[0]
 end
 
 print src
 print "鍵(シフト数)は #{table.shift_count} です\n"


**ファイル読み込んで標準出力に出す [#l8316174]
 begin
   puts IO.readlines("kadai.txt").map{|line| "[#{line.chomp}]" }
 rescue => ex
   warn ex.message
 end


**Base64 [#e59c345e]
 require 'stringio'
 
 def version
   warn "base64.rb 0.0.1"
   exit 0
 end
 
 def help
   warn <<-HELP
 base64.rb: Base64 encoder/decoder
 usage: ruby base64.rb MODE [filenames ...]
 
 MODE:
   encode : ordinary file to Base64 format
   decode : Base64 format to original file
   none   : print not convert one
   HELP
   exit 0
 end
 
 
 proc_tbl = {
   :encode => proc{|org| [org].pack("m") },
   :decode => proc{|org| org.unpack("m").first },
   :none => proc{|org| org }
 }
 
 opt_mode = []
 
 ARGV.delete_if do |x|
   case x
   when "encode", "decode", "none"
     opt_mode.push x.intern
   when "-h", "--help"
     help
   when "-v", "--version"
     version
   end
 end
 
 opt_mode.empty? and opt_mode.push :none
 opt_mode.size == 1 or raise "Too many mode specification"
 mode = opt_mode.first
 
 $stdin.binmode
 $stdout.binmode
 
 def argf_imitate(argv, mode)
   if argv.empty?
     yield $stdin
   else
     argv.each do |path|
       File.open(path, mode){|f| yield f }
     end
   end
 end
 
 argf_imitate(ARGV, 'rb') do |f|
   buffer = StringIO.new(proc_tbl[mode].call(f.read))
   until buffer.eof?
     $stdout.print buffer.read(64)
   end
 end

----
**練習問題(アルゴリズム編) Ruby版 [#l15d170b]
問題の詳細については
[[練習問題(アルゴリズム編)]]を参照。

**スタック [#b484c4bc]
 require 'readline'
 require 'forwardable'
 
 module RPCalc
   class Stack
     extend Forwardable
     
     def initialize
       @body = []
     end
 
     def self.load(collection)
       stack = self.new
       collection.each{|x| stack.push x }
       stack
     end
     
     def_delegators :@body, :push, :pop, :size, :empty?
     def_delegator :@body, :last, :top
     
     def pretty_print
       ". " + @body.reverse.map{|x| x.to_s }.join(" ") + " ]"
     end
   end
   
   module Lexer
     def lex(str)
       str.strip.split(/\s+/)
     end
   end
 
   module Parser
     def parse(tokens)
       semantic_values = []
       tokens.each do |token|
         case token
         when /\A[_a-zA-Z]\w*\Z/
           semantic_values.push [token.intern, :IDENT]
         when /\A\d+\.\d+\Z/, /\A\d+\Z/
           semantic_values.push [token.to_f, :NUMBER]
         when /\A(\+|-|\*|\/)\Z/
           semantic_values.push [token.intern, :COPERATOR]
         else
           semantic_values.push [token.intern, :ETC]
         end
       end
       semantic_values
     end
   end
   
   module Evaluater
     def evaluate(svals, stack)
       svals.each do |value, type|
         case type
         when :COPERATOR
           unless o2 = stack.pop
             warn "Stack Empty."
             break
           end
           unless o1 = stack.pop
             warn "Stack Empty."
             stack.push o2
             break
           end
           stack.push o1.__send__(value, o2)
         when :NUMBER
           stack.push value
         when :IDENT
           exit if value == :exit
         when :ETC
           ; #ignore
         else
           raise Exception, "unknown semantic value: #{type}"
         end
       end
       stack.top
     end
   end
   
   class Calculator
     include Lexer, Parser, Evaluater
     
     def initialize
       @stack = Stack.new
     end
     
     def shell(prompt, echo=true)
       while line = Readline.readline(prompt, true)
         yield line
         puts @stack.pretty_print if echo
       end
     end
     
     def calculate(expr)
       tokens = lex(expr)
       svals = parse(tokens)
       evaluate(svals, @stack)
     end
   end
 end
 
 
 calc = RPCalc::Calculator.new
 calc.shell("> ") do |line|
   puts calc.calculate(line)
 end
 print "\n"


トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS