第28回 Ruby勉強会@関西に参加してきました。
毎月恒例の勉強会に参加。
小波先生の「エラトステネスの嵐」が特に好きでした。ちょっとずつ良くなる感じが体感できるストーリー仕立ては見ていて楽しいものです。BenchmarkやProfilerはサボってしまいがちですが、やっぱり重要です。
演習1の解答
英語が苦手なので、メソッド名は適当(^^;
# h:: 身長(m) # w:: 体重(kg) def bmi(h, w) w / (h ** 2) end require 'date' # year年における1年間の長さ[hours] def hours_of(year) this_y = Date.new(year, 1, 1) next_y = Date.new(year+1, 1, 1) days = (next_y - this_y) (days * 24).to_i end # year年から10年間の期間の長さ [minutes] def minutes_for_ten_years_from(year) (0...10).map{ |i| hours_of(year+i)*60 }.inject(0){|sum, mins| sum += mins }.to_i end SECONDS_FROM_BIRTHDAY = 9_4300_0000 # date_str に生まれ、それから9_4300_0000秒だけ # 生きているとした場合の年齢を求める def age_after_943Msec_from(date_str) y,m,d = date_str.split(/[-\/]/).map{ |s| s.to_i } kako_date = Date.new(y,m,d) mirai = Time.local(y,m,d) + SECONDS_FROM_BIRTHDAY mirai_date = Date.new(mirai.year, mirai.month, mirai.day) mirai_i = mirai_date.to_s.tr('-/','').to_i kako_i = kako_date.to_s.tr('-/','').to_i ((mirai_i - kako_i) / 1_00_00).to_i end
計算100の解答
「40〜80行くらい」という情報は聞いていたので、ボクが書いたら300行くらいかな、と予想し、実際そのくらいでした。相変わらず頼まれもしない機能を盛り込みつつ、テストコードがゼロ・・・(-_-;;
print_sjis_lib.rb
#! ruby -Ku require 'nkf' # NetBeans では、UTF-8の使用を強制されるため、 # puts, print メソッドを書き換えるモジュールを用意しました。 module PrintSjisLib def self.utf82sjis(*args) args.flatten.map{ |arg| NKF.nkf('-Ws', arg.to_s) } end alias :print_raw :print alias :puts_raw :puts def utf82sjis(*args) PrintSjisLib.utf82sjis(*args) end def print(*args) print_raw utf82sjis(*args) end def puts(*args) if args.empty? puts_raw '' else puts_raw utf82sjis(*args) end end end
calc_hundred.rb
#! ruby -Ku require 'print_sjis_lib' require 'enumerator' require 'forwardable' class Array def choise_one self[rand(size)] end end module CalcHundred # 全問題数 MAX_N_QUESTIONS = 100 # 10問ずつ区切る N_FRACTION = 10 # かんたんモード MODE_EASY = 1 # むずかしいモード MODE_DIFFICULT = 2 # モードの一覧 MODES = [ MODE_EASY, MODE_DIFFICULT ] # モードの表示内容 MODE_NAMES = { MODE_EASY => "#{MODE_EASY}:かんたん", MODE_DIFFICULT => "#{MODE_DIFFICULT}:むずかしい" } # 演算子の集合 OPS ={ MODE_EASY => %w(+ - *), MODE_DIFFICULT => %w(+ - * /) } # オペランドの最大値 MAX_VALUE = { MODE_EASY => 10, MODE_DIFFICULT => 100 } class ExitException < StandardError; end class Controller extend Forwardable def_delegators :@out, :puts, :print def_delegators :@in, :gets def initialize(max_n_q = nil) @in = $stdin @out = $stdout @out.extend PrintSjisLib @n_q = max_n_q || MAX_N_QUESTIONS setup end def setup @mode = nil @elapsed = 0 @n_correct = 0 @n_wrong = 0 end # mode を呼び出し時に設定することを可能にしています。 def select_mode(mode = nil) puts "計算 #{@n_q} 開始" @mode = mode ? mode : get_mode puts end def get_mode print "難易度を選択してください。 " + MODE_NAMES.values.sort.join(' ') + " > " @mode = case @in.gets when /\A\s*([#{MODES}])\s*\Z/ $1.to_i when nil raise ExitException, "EOFが入力されました。" else get_mode end end def wait_for_ready print "#{MODE_NAMES[@mode]} を開始します。 Press Enter > " gets; puts end def start wait_for_ready time_start = Time.now srand(time_start.to_i) (1..@n_q).each_slice(N_FRACTION) { |poses_q| poses_q.each do print q = mk_q $DEBUG ? sample_answer(q) : answer_by_user(q) end puts "#{poses_q.last} 問突破" if poses_q.last.modulo(N_FRACTION) == 0 } @elapsed = Time.now - time_start puts '', "#{@n_q} 問終了しました。" end def mk_q Question.create(@mode, OPS[@mode].choise_one) end def sample_answer(q) sleep 0.2 rand(2) == 0 ? (puts q.answer; correct) : (puts; incorrect) end def answer_by_user(q) q.check_answer(gets) ? correct : incorrect end def correct puts "正解" @n_correct += 1 end def incorrect puts "不正解" @n_wrong += 1 end def summary puts summary_info end def summary_info s = [] s << "正解 : %d 問" % @n_correct s << "不正解: %d 問" % @n_wrong s << "タイム: %s" % elapsed_time s.join("\n") end def elapsed_time "%d 分 %d 秒" % @elapsed.divmod(60) end def exit(e) puts e, "終了します" end end class Question def self.create(mode, op) q = self.new(mode, op) q.mk_operands q end def initialize(mode, op) @mode = mode @op = op end def mk_operands vs = Array.new(2).map{ rand(MAX_VALUE[@mode]) } if @op == '/' and (vs[1] == 0 or vs[0].modulo(vs[1]) != 0) mk_operands else @v1, @v2 = vs end end def to_s "%d %s %d = " % [@v1, @op, @v2] end def check_answer(ans) return false if /\A\s*\Z/ =~ ans.to_s ans.to_i == answer end def answer @ans ||= eval("#{@v1} #{@op} #{@v2}") end end def self.main(max = nil) begin calc = Controller.new(max) calc.select_mode calc.start calc.summary rescue ExitException => e calc.exit(e) end end end if $0 == __FILE__ max = ARGV.shift.to_i if max == 0 CalcHundred.main else CalcHundred.main(max) end end