プログラミング/ruby/遠い世界の数学 の変更点
更新- 追加された行はこの色です。
- 削除された行はこの色です。
- プログラミング/ruby/遠い世界の数学 へ行く。
- プログラミング/ruby/遠い世界の数学 の差分を削除
[[公開メモ]]
#contents
* 概要 [#ma132a96]
http://qiita.com/cielavenir/items/cadbc5e24525b6a86cf8
こちらで紹介されていた、
http://nabetani.sakura.ne.jp/kanagawa.rb/evalex/
このお題について、単純に面白そうだったので趣旨もよく理解しないまま一人でやってみました。
どうやらこういうものがあったらしいです。> 神奈川Ruby会議
http://regional.rubykaigi.org/kana01/contents/02_pair_programing.html
** 仕様について [#h9437c63]
本来の仕様は数式を表わす文字列を受け取って、答えを表わす「文字列」を返さなければならないのですが、以下の関数の戻り値は整数になっていて、仕様を満たしていません。
ですので、すべて最後に .to_s を追加しないといけません。どうもすみません。
* 1. 始めに思いついた普通の(?)やり方 [#t102675d]
部分統治でしこしこ計算する。~
~& の初期値が -1 なので、32ビット(31ビット?)以上の数値が出てくると破綻しそう。
LANG:ruby
def calc(expr)
expr.split('*').inject(1) do |result, expr|
result * expr.split('+').inject(0) do |result, expr|
result + expr.split('&').inject(-1) do |result, expr|
result & expr.split('|').inject(0) do |result, expr|
result | expr.to_i
end
end
end
end
end
** 1-1. 短くした [#o7cc1ec8]
LANG:ruby
def calc(s,o='*+&|')
s.split(o[0]).map{|s|o=='|'?s.to_i: calc(s,o[1,3])}.reduce(o[0])
end
素直さや実行効率を考えると、たぶんこれが一番おすすめ。
** 1-2. もう1文字 [#o7cc1ec8]
LANG:ruby
def calc(s,o='*+&|')
o==''?s.to_i: s.split(o[0]).map{|s|calc(s,o[1,3])}.reduce(o[0])
end
恐らくゴルフじゃないので怒られそう・・・
* 2. 正規表現で手抜きをする [#qb461a1a]
括弧を挿入してあとは eval
LANG:ruby
def calc(expr)
eval expr.gsub(/[^()*]+/, '(\\0)')
.gsub(/[^()*+]+/, '(\\0)')
.gsub(/[^()*+&]+/,'(\\0)')
end
** 2-1. 短くした [#ecb8c47b]
LANG:ruby
def calc(s)
eval s.gsub(/[^*]+/){"(#{$&.gsub(/[^+]+/){"(#{$&.gsub(/[^&]+/,'(\\0)')})"}})"}
end
むしろ上のやつのが短い。~
もっと短くできるかしら?
** 2-2. ループにした [#m9b903ed]
LANG:ruby
def calc(s)
eval %w(& + *).inject(->(s){s}){|f,o| ->(s){s.gsub(/[^#{o}]+/){"(#{f.call $&})"}}}.call(s)
end
長くなった orz
* 3. 逆に考える [#k1384c6d]
LANG:ruby
def calc(expr)
eval expr.gsub(/[|\d]+/,'(\\0)')
.gsub(/[&|\d()]+/,'(\\0)')
.gsub(/[+&|\d()]+/,'(\\0)')
end
** 3-1. 短くする [#o91eb30d]
LANG:ruby
def calc(s)
1.upto(3){|i|s.gsub!(/[#{'|&+'[0,i]}\d()]+/,'(\\0)')};eval s
end
これはかなりすっきりかも。
** 3-2. 邪悪なゴルフ [#j9e27a6b]
LANG:ruby
def calc(s)
'&+'.gsub(//){s.gsub!(/[|#{$`}\d()]+/,'(\\0)')};eval s
end
短くは、なる。
** 2-3. この方針でいくなら [#u9985b32]
逆じゃないほうが1文字短い
LANG:ruby
def calc(s)
'+&'.gsub(//){s.gsub!(/[^*#{$`}()]+/,'(\\0)')};eval s
end
* 4. 括弧を演算子の両側に入れる案 [#g3d3398c]
LANG:ruby
def calc(s)
eval "(((#{s.gsub(/[*]/, ')\\0(')
.gsub(/[*+]/, ')\\0(')
.gsub(/[*+&]/, ')\\0(')})))"
end
あるいは、
LANG:ruby
def calc(s)
eval "(((#{s.gsub(/\*/, ')))*(((')
.gsub(/\+/, '))+((' )
.gsub(/\&/, ')&(' )})))"
end
** 4-1. ゴルフ [#j9e27a6b]
LANG:ruby
def calc(s)
'+&'.gsub(//){s.gsub!(/[*#{$`}]/,')\\0(')};eval "(((#{s})))"
end
最後に括弧で括るところで長くなる。
* 括弧を使えるようにする [#r26fe5fb]
お題にはないですが、式の中で括弧を使えるようにするには、
LANG:ruby
def calc_with_paren(s)
while s.sub!(/\(((?:[^(]+|\g<0>)*)\)/){calc_with_paren($1)}
end
calc(s)
end
とすれば良いはずが、ruby2.1.5 では \g<0> で再帰的なマッチをした後に
とすれば良いはず。ところが、ruby2.1.5 では \g<0> で再帰的なマッチをした後に
$1 を参照すると、negative string size (or size too big) のエラーが出たり、
あるいはおかしな部分文字列が返ってきたりでまともに使えませんでした。
$& は正しく返ってくるので、正規表現ライブラリのバグっぽい雰囲気です。
→ やはりそうでしたので、報告しておきました。~
https://github.com/k-takata/Onigmo/issues/48
このバグを回避するには、$1 を使わず $& を参照して、
以下のようにする必要がありました。
LANG:ruby
def calc_with_paren(s)
while s.sub!(/\((?:[^(]|\g<0>)*\)/){calc_with_paren($&[1,$&.length-2])}
end
calc(s)
end
* 質問・コメント [#m332a2d2]
#article_kcaptcha
Counter: 6351 (from 2010/06/03),
today: 2,
yesterday: 3