Skip to content

Instantly share code, notes, and snippets.

@igrep
Last active August 29, 2015 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igrep/522bd3f8578c4d28fb17 to your computer and use it in GitHub Desktop.
Save igrep/522bd3f8578c4d28fb17 to your computer and use it in GitHub Desktop.
神奈川Ruby会議01のペアプロ問題から。 http://nabetani.sakura.ne.jp/kanagawa.rb/evalex/
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