>トップ
Kernelモジュールで定義されているcatchメソッドとthrowメソッドによる大域ジャンプは、異常時のための例外機構ではなく正常系のための手続きとして必要な時には便利です。
以前の典型的なcatch〜throwの使い方は次のようなものでした。
#!/usr/local/bin/ruby22 p "1" catch(:tag){ p "2" throw(:tag) p "3" } p "4"
単純な場合はこのような使いかたで問題ないのですが、このcatchとthrowにおけるtagの名前空間は一種の動的スコープのようになっているため、次のような場合に問題がありました。
#!/usr/local/bin/ruby22 def foo() p "foo-1" catch(:mytag){ p "foo-2" yield() p "foo-3" throw(:mytag) p "foo-4" } p "foo-5" end def bar() p "bar-1" catch(:mytag){ p "bar-2" foo(){ p "bar-3" throw(:mytag) p "bar-4" } p "bar-5" } p "bar-6" end bar()
これを実行すると、次のように出力されます。
"bar-1" "bar-2" "foo-1" "foo-2" "bar-3" # ここに "foo-5" # 注意 "bar-5" "bar-6"
メソッドbarにあるブロックの中から投げたthrowが、メソッドfooのcatchにつかまえられてしまっています。古いライブラリの一部に実際にこのような、「決め打ちタグと同名のタグによる意図しないcatch/throw」の可能性があるらしいので(以前、調査した資料を見た記憶があるのですが探し出せませんでした)、古いままのライブラリを使う場合などには一応注意する必要があります。
最近のrubyでは、次のようにcatchを通常の引数無しで呼出すと、ブロック引数としてthrowするためのオブジェクトが作られて渡されてきますので、それをthrowするのがベストプラクティスになっています。
#!/usr/local/bin/ruby22 p "1" catch {|tag_obj| p tag_obj throw(tag_obj) p "3" } p "4"
ここでtag_objのオブジェクトは、将来のrubyでの変更の可能性なども考えて、opaqueなものとして扱っておいたほうが良いでしょう。また、特別な意図がある場合に(再帰的なネストからいっぺんに抜出すなど)念入りに設計して使うのでない限り、従来の、タグを明示的に渡すcatchを使う理由は無いでしょう。
結論としては、キーワードに gem ruby と付けて検索するのが良い、という話。
cruby (MRI) の場合、現状、添付ライブラリは全てテストを付けて……という感じで運用されています。そういった運用であるために、昨今はなかなか「これを添付ライブラリに入れよう」という話は難しくなっています。また、検索しても一発でこれがベスト、というコードにたどり着けないこともけっこうあります。
現状、rubygem に登録されてるライブラリがメンテナンスされているものであることが多いので、そういったライブラリがないか、と最初に検索してみるのが良いかと思います。
Perl ないし、さらに古くは Awk の ~ 演算子が由来でしょうが、Ruby から始めたような人は、Perl などでは左辺が文字列で右辺が正規表現、と決められているので注意。
ruby は遅い、という批難はよく耳にしますし、当たっていることもあります。
が、NArray 使えよ、というような気がすることもよくありますので、ここではそういった視点からの NArray の紹介です。
NArray ( http://narray.rubyforge.org/index.html.ja 。なおインストールはお使いのシステムのパッケージシステムか rubygem でいいでしょう)は、ruby の数値配列ライブラリとして、古くから実績があり現在でもメンテナンスされているライブラリです。標準添付でこそありませんが(メンテナの方の意向によるものとうかがったことがあります)、デファクトスタンダードと言ってもいいのではないでしょうか。
C 言語や、ruby でも一般的な foo[i][j] という形での多次元配列へのアクセスではなく、一見した感じでは Basic 風というか、行と列の指定順では Fortran 風の foo[j, i] という構文で多次元アクセスをおこなうなど、ruby の Array とは使い勝手が大きく違うのが、最初は使い辛く感じるかもしれません。
(中身について興味がある方は、作者の田中さんによる http://www.slideshare.net/masa16tanaka/narray-pwrake を見ると良いでしょう)
しかし、数値配列の扱いのためにあえてそうしてある、という設計によるものですので、最初の難しさを乗り越える価値が十分にあるライブラリです。ちょっとしたことから使ってみましょう。
(なお、軽量言語の数値計算ライブラリでは、このような、言語の他の部分と癖が違う、という設計はよくあるもののようです。Java 3D ではオブジェクトを極力使い回すような設計になっていたり、Python の NumPy は、組み込みライブラリにまで影響して、Python のリストでは foo += bar と foo = foo + bar で結果が異なる、という仕様だったりしますが、うっかり言及するとこんな風にトマホークだかハンマーだかが飛んでくるらしいので、まぁどんな言語界隈にもモヒカンは生息してるのでしょう)
現在の一般的な PC での実行では、100 万要素ぐらいから、Array#sort! では遅さを感じるようになるでしょうか。
そういう時に NArray を使いましょう。ベンチマークのためのサンプルコードを示します。
筆者の手元の環境で 500 万要素のソートを実行してみた結果を以下に示します。
$ ruby19 -v array_sort.rb 5000000 ruby 1.9.3p125 (2012-02-16 revision 34643) [amd64-freebsd8] 13.510852 13.427535 13.424106 13.445138 13.704523 13.864022 13.632735 13.586616 13.757978 13.65584 13.562891 13.637271 13.846052 13.541271 13.626213 13.648054 13.401002 13.550984 13.689528 13.425835 $ ruby19 -v narray_sort.rb 5000000 ruby 1.9.3p125 (2012-02-16 revision 34643) [amd64-freebsd8] lib/complex.rb is deprecated 1.269857 1.261399 1.2575 1.268182 1.267217 1.261881 1.263131 1.263579 1.282607 1.263385 1.261822 1.264086 1.267125 1.264541 1.263302 1.260111 1.260393 1.265207 1.271147 1.26037
とりあえずすごく簡単な例、3 枚の平面の共通点を求める計算を NArray でやってみます( NMatrix と NVector を使っていますが、次のバージョンでは廃止予定という話もあるので一応覚えておいてください)。
(この例は Wikipedia の system of linear equations の記事 からもってきました( 2012 年 4 月末))
という 3 枚の平面をあらわす等式(連立方程式)を解いてみます。
このような連立方程式を行列による式にする場合、列ベクトル(列数が 1 列の行列)に左から正方行列を掛ける形にする場合と、行ベクトル(行数が 1 行の行列)に右から正方行列を掛ける形にする場合があります(手元のコンピュータグラフィックスの教科書には(山口富士夫先生の『CAD工学』p. 22 の注)米国と欧州で主流のスタイルが逆である、と説明があります)。
それぞれ、逆にする場合は、行列を転置して、式全体の順序も逆にするだけですが、コーディングでは取り違えないよう注意が必要です。また、NArray の NMatrix と NVector の * メソッドによる乗算では、それぞれの左右に応じて NVector を行または列であると適宜判断して計算してくれます。
ここでは、係数の並びが方程式と見た目で一致する(もう一つ理由がありますが、あとで説明します)、左から掛ける形にします。
NArray で左辺の行列と右辺のベクトルを作るコードは以下のようになります。
require "narray" a = NMatrix[ [ 3.0, 2.0, -1.0], [ 2.0, -2.0, 4.0], [-1.0, 0.5, -1.0]] b = NVector[ 1.0, -2.0, 0.0]
そして、このようなベクトル b と行列 a があるとき、NArray では b/a という計算をすると、b に対して左から a の逆行列を掛けたもの、すなわち (x y z)T が得られます。意味としては b/a というよりむしろ a\b なので、バックスラッシュが演算子として使えたらそうしたい感じ、とのことでした( [ruby-list:48724] )。なお NArray ではこの計算の時、逆行列を求めるのではなく LU 分解法で解くよう実装されています(具体的には a.lu.solve(b) というメソッドチェインになります。b/a はこれの略記法みたいな感じで、さきほどの仕様についても、もともとの solve の実装がそうなっていたため)。
(逆にしたい場合は転置( NMatrix#transpose )を入れればいいだけですが)行列を左から掛けるようにしておけば、ここでそのまま計算できます。コードの続きは次のようになります。
p b/a
実行すると、次のように解が出力されます。
$ ruby19 linear_equations.rb NVector.float(3): [ 1.0, -2.0, -2.0 ]
三人称を避けるべきという理由で kind_of? が推奨( http://jp.rubyist.net/magazine/?0011-CodeReview#l13 )
引数とレシーバが逆になった形では Module#=== がある。case obj when Klass の形で使える(case when の比較は === でおこなわれる)
Java の経験があると instanceof 演算子や (Javaの) Class#isInstance の語感にひきずられて Ruby の Object#instance_of? を使ってしまいそうだが、Ruby の Object#instance_of? は、obj.instance_of? klass とした場合、obj が klass の直接のインスタンス(サブクラスのインスタンスではない)である場合にのみ true になるという特殊な(リスコフの置換原則を破る)ものであるのでそれと意図した場合以外は普通使わない
るびまの記事では String#size についての言及はない。(UTF-8 などの)「文字」を意識した長さについて String#length を、バイト列としてのサイズは String#bytesize を、と使いわける(Ruby 1.9)
「本書では、Ruby で標準になりつつある習慣に従って、1 行のブロックにはブレースを、複数行のブロックには do/end を使います。」(プログラミング Ruby 第 2 版、§2.6)
「基本的にdo ... endを使用する。」(前田 修吾さんによる Rubyコーディング規約)
「したがって,本稿ではブロックの構文に波括弧だけを用いることにする。」(Ruby チュートリアル - 6.1 (OKIソフトウェア エンジニアリングソリューションセンタ))
これは通らない
irb> File.open 'input.txt'{|file|p file.readlines} SyntaxError: (irb):**: syntax error, unexpected '{', expecting $end File.open 'input.txt'{|file|p file.readlines} ^ from /usr/local/bin/irb19:12:in `'
これも通らない
irb> name = 'input.txt' ; File.open name{|file|p file.readlines} NoMethodError: undefined method `name' for main:Object from (irb):** from /usr/local/bin/irb19:12:in `'
do〜end なら通る
irb> File.open 'input.txt' do|file|p file.readlines end irb> name = 'input.txt' ; File.open name do|file|p file.readlines end
カッコを省略しなければ通る
irb> File.open('input.txt'){|file|p file.readlines} irb> name = 'input.txt' ; File.open(name){|file|p file.readlines}
parenthesize argument(s) for future version
『気になったのでまつもとさんに聞いてみたところ、「文法を簡単にしようと思ってたんだけど、RubyConfで『括弧の省略を駆使していかに英語っぽいコードを書くか』という内容でまるまる1セッション使った発表があって、諦めた」とのことでした。なるほど……。』( http://route477.net/d/?date=20081019 )
$ svn diff -r 13820:13821 Index: ChangeLog =================================================================== --- ChangeLog (revision 13820) +++ ChangeLog (revision 13821) @@ -1,3 +1,10 @@ +Mon Nov 5 01:20:33 2007 Yukihiro Matsumoto <matz@ruby-lang.org> + + * parse.y (call_args): remove "parenthesize argument(s) for future + version" warning. when I added this warning, I had a plan to + reimplement the parser that is simpler than the current one. + since we abandoned the plan, warning no longer required. + Mon Nov 5 01:02:56 2007 Minero Aoki <aamine@loveruby.net> * lib/net/http.rb (HTTPHeader#initialize): provide default Index: lib/rss/atom.rb (略) Index: parse.y =================================================================== --- parse.y (revision 13820) +++ parse.y (revision 13821) @@ -2287,7 +2287,6 @@ call_args : command { - rb_warn0("parenthesize argument(s) for future version"); /*%%%*/ $$ = NEW_LIST($1); /*%
Ruby の正規表現で \A は「文字列の先頭」にマッチする。ruby 1.9 からの機能である match の第 2 引数で開始位置を指定したマッチで nth > 0 の場合はマッチしない
irb(main):001:0> /\A/.match "foo", 1 => nil
「マッチ対象の先頭」にマッチさせる目的には \G を使う
なお、StringScan のマッチでは str[nth, -1] へのマッチであるかのようにふるまう( \A がマッチする)
irb(main):001:0> ss = StringScanner.new "foo" => #<StringScanner 0/3 @ "foo"> irb(main):002:0> ss.scan /\A./ => "f" irb(main):003:0> ss.scan /\A./ => "o" irb(main):004:0> ss.scan /\A./ => "o" irb(main):005:0> ss.scan /\A./ => nil
( StringScan のマッチには元々「先頭でマッチするか?」を見る機能があるのでこの \A は無意味だが)
proc は obsolete になる( http://jp.rubyist.net/magazine/?0001-Hotlinks )(注: まだある)
「次第に非推奨になりつつあります。Kernel#lambda を使ってください。」(プログラミング Ruby 第 2 版、Kernel#proc )、「今は proc よりも lambda のほうが好まれます。」(プログラミング Ruby 第 2 版、Kernel#lambda )
procやlambdaをブロックなしで呼び出し、現在のメソッドに与えら れているブロックをオブジェクト化する機能は、ブロック引数 (&block)またはProc.newに任されました。将来はprocやlambdaには この機能がなくなります。
( [ruby-list:38044] 。現状、lambda では警告が出る)
http://redmine.ruby-lang.org/wiki/ruby/DeveloperHowtoJa
メソッド名の末尾に ! がつくメソッドは変更が起きなかったときに nil を返すというルールになっている( [ruby-list:47807] より)。なお、「破壊的なメソッドは」ではない(破壊的でも ! のつかないメソッドはある)
/usr/local 以下にライブラリとかがあって、configure の --prefix でホームディレクトリなどを指定している場合、--with-opt-dir=/usr/local というオプションで一括して指定できる。configure が configure: WARNING: unrecognized options: --with-opt-dir とかメッセージを吐いてくるがスルーでよい
いきなり make main で、インタプリタと標準添付ライブラリのビルドができるようになったのは trunk と 1.8 枝では r22592 から( http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=22592 )。たとえば次のようにする(もっとエレガントな方法あったら教えてください)。git svn を使っている場合
REVISION=`git svn info | grep '^Revision: ' | awk '{print $2}'` make `test $REVISION -ge 22592 && echo main`
http://www.ruby-lang.org/ja/old-man/html/obsolete.html
Ruby 界隈、特に MRI のコミッタ等の会話で時折「boron で」といった表現が出てきますが、これは www.ruby-lang.org などのホストの「真の名前」で、hydrogen と helium から始まって現在まで後続の元素の名前が使われているものです。