前回(チームラボクイズ2010 やってみた!)はJavaで「チームラボクイズ2010」をやってみました。
かなりゴリ押しだったので,あまり満足していませんでした。
なので,今回は言語を「Ruby」に変えて再挑戦してみました。
ちなみにRubyは初心者です!いろいろ調べながらやりました!
Ruby素晴らしい!!一回作ったというのもありますが,改良を加えたりしても前回より圧倒的に早くコーディングが終わりました。
機能が充実していてとても面白い言語でした。
まだまだ知らないことだらけなので,どんどん学んでいきたいです!
以下より,ソースコードとちょっとしたコメントです。
ソースコード:
teamLabQuiz2010.rb
## 定数宣言 ##<br>#問題文<br>STR = "2ABACBDEDFDFDFDCBACDAFB2"<br>#当てはめる文字(記号)<br>CH = ['0', '1', '3', '4', '5', '6',<br> '7', '8', '9', '+', '-', '*', '/', '=']<br>#置き換える文字<br>MARK = ['A', 'B', 'C', 'D', 'E', 'F']<br><br><br>#文法が正しい<br>def isGrammarOk(str)<br> #'='を含んでいるか<br> return false unless /=/ =~ str<br> <br> #'1'を含んでいるか<br> return false unless /1/ =~ str<br> <br> #演算記号の右隣が'0'でないか<br> return false if /[^0-9]0/ =~ str<br> <br> #演算記号が並んでいないか<br> return false if /[^0-9]{2}/ =~ str<br> <br> #不正チェック終了→文法的に正しい<br> return true<br>end<br><br>#演算記号(計算順)<br>OPE = ['-','+','*','/']<br><br>#式を計算し値を返す<br>def calc(str)<br> #演算記号がない場合,パースして値をreturn<br> return str.to_f if /^[0-9]+$/ =~ str<br> <br> #順番に計算<br> OPE.each do |ope|<br> #末尾から先頭に向かって演算記号を探す<br> if (pos = str.rindex(ope)) != nil<br> #演算記号に一致した場合<br> <br> #左辺,右辺を再帰処理して,数値型に<br> leftVal = calc(str.slice(0,pos))<br> rightVal = calc(str.slice(pos+1, str.size))<br> <br> #演算記号による分岐<br> case ope<br> when "-"<br> return leftVal - rightVal<br> when "+"<br> return leftVal + rightVal<br> when "*"<br> return leftVal * rightVal<br> when "/"<br> return leftVal / rightVal<br> end<br> end<br> end<br> <br>end<br><br>#'='の左辺と右辺が等しいか<br>def isCorrect(str)<br> #'='で区切り配列に分ける<br> equ_array = str.split(/\s*=\s*/)<br> clear_equ = []<br> <br> #それぞれの項について<br> equ_array.each do |equ|<br> #文字列式から数値に<br> clear_equ << calc(equ)<br> #一致しなかったらfalse<br> return false if clear_equ[0] != clear_equ[clear_equ.size-1]<br> end<br> <br> #すべて一致したのでtrue<br> return true<br>end<br><br>#1つにまとめただけ<br>def check(str)<br> return isGrammarOk(str) && isCorrect(str)<br>end<br><br>#順列の生成<br>CH.permutation(MARK.size).each do |s|<br> #それぞれの順列について<br><br> #問題文に当てはめる<br> str = STR<br> s.size.times do |i|<br> str = str.gsub(MARK[i], s[i])<br> end<br> <br> #当てはめた式が正しければ出力<br> puts str if check(str)<br>end<br><br>
コメントといっても,便利だったところを挙げるぐらいです。
動的配列イイ!
動的配列はほとんど使ったことがないので,とても便利に感じました。自由に使えるのはいいですね。
縛りがなくて,他のことをいろいろ気にせずにコーディングに集中できました。
デフォルトで順列生成をサポート
Rubyには「順列生成」のクラスが用意されていて,今回はそれを使ってみました。
CH.permutation(MARK.size).each do |s|<br>
この部分ですね。
CHの宣言はこんな感じです。
#当てはめる文字(記号)<br>CH = ['0', '1', '3', '4', '5', '6',<br> '7', '8', '9', '+', '-', '*', '/', '=']<br>
配列からそのまま順列が生成できるのです!!
順列の生成がデフォルトでサポートという。。。素晴らしい!
14P6の順列ですね。
前回は14^6でゴリ押ししたので,差がとても大きいです。
しかーし・・・
計算量を省いたので今回の方が明らかに速いと思ったら・・・
timeコマンドでの測定結果:
Java版: 3m4.13s
Ruby版: 4m22.74s
あれ・・・?遅いだと・・・
たしかにRubyとJavaではもともとの速度が違います。
あと,プログラマの問題ですねw
ウエイトの高い処理が多いのでしょう・・・
スクリプト言語を使うときは,「ウエイト」をよく考えてコーディングする必要がありますね。
でも,前回に比べては,書きやすかったし,あとから見返しても読みやすいです。
手軽さから言えば,Rubyの圧勝でした。
正規表現便利!
最後に,今回プログラムの中では初めて「正規表現」を使ってみました!
なかなか便利です。
Java版でも正規表現を取り入れれば,文法チェック周辺がすっきりするかもな~。
順列を使って計算量が減ったかと思えば,逆にちょっと遅くなってるというオチでしたが,Rubyなどのスクリプト言語の便利さやすばらしさ,また正規表現の便利さも学べたのでとても身になりました。
Rubyももっと使えるようにして,自分のツールにしてみたいと思います。
また,視野を広げるためにPythonとかその他の言語にも手を付けていきたいなぁと思っています。
追記(03/28):
追記を分割しました。
チームラボクイズ2010 やってみた! Part.2 on Ruby 追記