プログラミング/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: 5826 (from 2010/06/03),
today: 2,
yesterday: 4