Last active
August 29, 2015 14:13
-
-
Save igrep/522bd3f8578c4d28fb17 to your computer and use it in GitHub Desktop.
神奈川Ruby会議01のペアプロ問題から。 http://nabetani.sakura.ne.jp/kanagawa.rb/evalex/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'strscan' | |
=begin | |
神奈川Ruby会議01のペアプロ問題から。 http://nabetani.sakura.ne.jp/kanagawa.rb/evalex/ | |
ちょうど今読んでいる http://www.amazon.co.jp/Programming-Principles-Practice-Using-Edition/dp/0321992784 | |
に電卓を作る例があったので、ほとんど写経するような形で書いてみた。 | |
書いておきながら細かいところをちゃんと説明できないので途中まで書いていてすごく不安だった... | |
=end | |
Token = Struct.new(:kind, :string) | |
def tokenize(string) | |
s = StringScanner.new string | |
result = [] | |
result << get_token(s) until s.eos? | |
result | |
end | |
def get_token(s) | |
integer(s) || operator(s) | |
end | |
def integer(s) | |
scanned_result = s.scan(/\d+/) | |
return if scanned_result.nil? | |
Token.new __method__, scanned_result | |
end | |
def operator(s) | |
scanned_result = s.scan(/[*+&|]/) | |
return if scanned_result.nil? | |
Token.new __method__, scanned_result | |
end | |
# ここからリファクタリングした後のコード。今度はメソッドがでかくなり、依然として汚い。 | |
# integerメソッドとoperatorメソッドもリファクタリングしたいけどもう疲れた...。 | |
OPERATORS = [ | |
'*', | |
'+', | |
'&', | |
'|', | |
] | |
def eval_tokens tokens, rule_id = 0 | |
operator = OPERATORS[rule_id] | |
if operator | |
left = eval_tokens(tokens, (rule_id + 1)) | |
token = tokens.shift | |
until token.nil? | |
if token.string == operator | |
left = left.__send__ operator, eval_tokens(tokens, (rule_id + 1)) | |
token = tokens.shift | |
else | |
tokens.unshift token | |
return left | |
end | |
end | |
return left | |
else | |
token = tokens.shift | |
if token.kind == :integer | |
return token.string.to_i | |
else | |
raise 'error' | |
end | |
end | |
end | |
# ここまでリファクタリングした後のコード。 | |
TEST_CASES = [ | |
# [ id, input, expected ], | |
[ 0, "4*5+6&7|8", "44" ], | |
[ 1, "15*5", "75" ], | |
[ 2, "15+5", "20" ], | |
[ 3, "15&5", "5" ], | |
[ 4, "15|5", "15" ], | |
[ 5, "30*15*5", "2250" ], | |
[ 6, "30*15+5", "600" ], | |
[ 7, "30*15&5", "150" ], | |
[ 8, "30*15|5", "450" ], | |
[ 9, "30+15*5", "225" ], | |
[ 10, "30+15+5", "50" ], | |
[ 11, "30+15&5", "35" ], | |
[ 12, "30+15|5", "45" ], | |
[ 13, "30&15*5", "70" ], | |
[ 14, "30&15+5", "19" ], | |
[ 15, "30&15&5", "4" ], | |
[ 16, "30&15|5", "14" ], | |
[ 17, "30|15*5", "155" ], | |
[ 18, "30|15+5", "36" ], | |
[ 19, "30|15&5", "5" ], | |
[ 20, "30|15|5", "31" ], | |
[ 21, "1+2+3+4+5+6+7+8+9+10", "55" ], | |
[ 22, "1*2*3*4*5*6*7*8*9*10", "3628800" ], | |
[ 23, "1+2+3+4+5*6+7+8+9+10", "600" ], | |
[ 24, "1*2*3*4*5+6*7*8*9*10", "1330560" ], | |
[ 25, "1|2|4|8|16|32|64|128|256|512", "1023" ], | |
[ 26, "2046&2045&2043&2039&2031&2015&1983&1919&1791&1535", "1024" ], | |
[ 27, "0+1|7*6", "42" ], | |
[ 28, "6|4+2&9+4", "10" ], | |
[ 29, "0&6+1&6|4*2", "0" ], | |
[ 30, "4|4*7+7+4&9", "56" ], | |
[ 31, "9&8*2+3*1|2|7", "280" ], | |
[ 32, "230+83*751&176", "50080" ], | |
[ 33, "89+62465*94&84", "5254536" ], | |
[ 34, "668&925+398*562", "599092" ], | |
[ 35, "15|9+348*302&23&77", "1452" ], | |
[ 36, "3&3&6|7+3|5*3|2&4*4", "0" ], | |
[ 37, "7*6|7|7*1&7|7&3&8*3", "0" ], | |
[ 38, "896+316*209*264&728", "2026464" ], | |
[ 39, "1844+41*64|7906|66842", "138965970" ], | |
[ 40, "2&41&6884*69857+68083", "0" ], | |
[ 41, "2+3*9*3|6|7&0+3+3*6&8", "0" ], | |
[ 42, "895+400*988|549&237+488", "938875" ], | |
[ 43, "30*48&99036+140&33+75|645", "22050" ], | |
[ 44, "278+2033+53*96*56|3303|3&14", "3177216" ], | |
[ 45, "5380&27|643*2+1888&74+30|16", "0" ], | |
[ 46, "0&3845+6645*4293+78&78*3102|9|3", "90127550385" ], | |
[ 47, "78&44956&67*974|413+13237*5588|54*668", "0" ], | |
[ 48, "500*206+145|167|163|465&668+662+806*681&458", "123896000" ], | |
[ 49, "82+14&0*344+34+542916&18*11|844|64*873223|840993", "23813260003764" ], | |
[ 50, "374958|6727+53965&53*954&29|6*138572+59|547783&43*8998", "12178274756590800" ], | |
] | |
def test | |
failures = TEST_CASES.select {|_id, input, expected| eval_tokens(tokenize(input)) != expected.to_i } | |
if failures.empty? | |
puts "All OK" | |
else | |
failures.each do|id, input, expected| | |
puts "#{id}: NG!: actual: #{actual}, expected: #{expected}" | |
end | |
end | |
end | |
# ここからリファクタリングする前のコード。超コピペだらけ。 | |
def ex1 tokens | |
left = ex2(tokens) | |
token = tokens.shift | |
until token.nil? do | |
case token.string | |
when '*' | |
left *= ex2(tokens) | |
token = tokens.shift | |
else | |
tokens.unshift token | |
return left | |
end | |
end | |
return left | |
end | |
def ex2 tokens | |
left = ex3(tokens) | |
token = tokens.shift | |
until token.nil? do | |
case token.string | |
when '+' | |
left += ex3(tokens) | |
token = tokens.shift | |
else | |
tokens.unshift token | |
return left | |
end | |
end | |
return left | |
end | |
def ex3 tokens | |
left = ex4(tokens) | |
token = tokens.shift | |
until token.nil? do | |
case token.string | |
when '&' | |
left &= ex4(tokens) | |
token = tokens.shift | |
else | |
tokens.unshift token | |
return left | |
end | |
end | |
return left | |
end | |
def ex4 tokens | |
left = ex5(tokens) | |
token = tokens.shift | |
until token.nil? do | |
case token.string | |
when '|' | |
left |= ex5(tokens) | |
token = tokens.shift | |
else | |
tokens.unshift token | |
return left | |
end | |
end | |
return left | |
end | |
def ex5 tokens | |
token = tokens.shift | |
if token.kind == :integer | |
token.string.to_i | |
else | |
raise 'error' | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment