SSブログ

一週間に一回Rubyプログラミングねた10-Ruby2.1と遅延評価 [プログラミング]

ブロック、クロージャ系の話題としてはとりあえず最後にしたいのですが、遅延評価で引っかかっている。むぅイマイチ有用性が掴めん。というか、すごく計算効率がいいものを見てしまったから、それをどうやって高階関数とかで実現できるのかを知っておきたい。できれば自分で書けるぐらいになっておきたい。

 
環境にRuby2.0が入ってないので、Ubuntu serverにrbenvを入れる。rvmの方がお手軽に導入できるのだが、色々問題があるらしいので、あえて面倒な手順をやってみる。前に結構面倒な思いをしたんだよね。少しでも改善していればいいんだけど。
$ sudo aptitude install rbenv
The following NEW packages will be installed:
  libreadline5{a} libruby1.8{a} rbenv ruby1.8{a}
0 packages upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
Need to get 1,974 kB of archives. After unpacking 7,158 kB will be used.
Do you want to continue? [Y/n/?]

あれ?まだRuby1.8だし。
aptitude showで見たらVersion: 0.1.2+git20100922-1って書いてある。古くない? そもそもRuby1.8ってメンテされてないんだけど、rbenvを入れるような人は自分で入れるのが普通とかそういうことなんだろうか。多くの開発環境の言語のバージョンを操作するアプリに任せて入れないパッケージって多いよね。Ruby1.9ですら古くなりつつあるんだろうけど面倒だな。rvmで終わらせたいな。スゲー軟派ですw。

ソースから入れるのはやっぱり本家から入れたほうがいいんだろうね。面倒だがひさしぶりにやっておいたほうがいいのかな。
https://github.com/sstephenson/rbenv#installation
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >>  ~/.bashrc
source ~/.bashrc
type rbenv

シェル再起動するの面倒だったので…。というか、sshで抜けずにきっちりシェルを初めから動かすやり方が知らない。何かあったっけ? 忘れたw。

インストールと言ってもチェックアウトしてパスを通すだけだなぁ。同様にお手軽にインスコできるプラグインを入れる。別々にしておく必要性が感じられないんだけど、作った人は独立させておきたいらしい。恐らく、自分の使い勝手のせいかもしれないな。あえてrbenv install以外でスタンドアローンで動くようにしているし。
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build


んでMacでrvmを使っているんだけれども、やっぱりそれよりか多くのバージョンがサポートされてますね。まぁより多く使われているから当然のことなんだろうけど。
~$ rbenv install -l
Available versions:
  1.8.6-p383
  1.8.6-p420
  1.8.7-p249
  1.8.7-p302
  1.8.7-p334
  1.8.7-p352
  1.8.7-p357
  1.8.7-p358
  1.8.7-p370
  1.8.7-p371
  1.8.7-p374
  1.8.7-p375
  1.9.1-p378
  1.9.1-p430
  1.9.2-p0
  1.9.2-p180
  1.9.2-p290
  1.9.2-p318
  1.9.2-p320
  1.9.2-p326
  1.9.3-dev
  1.9.3-p0
  1.9.3-p125
  1.9.3-p194
  1.9.3-p286
  1.9.3-p327
  1.9.3-p362
  1.9.3-p374
  1.9.3-p385
  1.9.3-p392
  1.9.3-p429
  1.9.3-p448
  1.9.3-p484
  1.9.3-preview1
  1.9.3-rc1
  2.0.0-dev
  2.0.0-p0
  2.0.0-p195
  2.0.0-p247
  2.0.0-p353
  2.0.0-preview1
  2.0.0-preview2
  2.0.0-rc1
  2.0.0-rc2
  2.1.0
  2.1.0-dev
  2.1.0-preview1
  2.1.0-preview2
  2.1.0-rc1
  2.2.0-dev
  jruby-1.5.6
  jruby-1.6.3
  jruby-1.6.4
  jruby-1.6.5
  jruby-1.6.5.1
  jruby-1.6.6
  jruby-1.6.7
  jruby-1.6.7.2
  jruby-1.6.8
  jruby-1.7.0
  jruby-1.7.0-preview1
  jruby-1.7.0-preview2
  jruby-1.7.0-rc1
  jruby-1.7.0-rc2
  jruby-1.7.1
  jruby-1.7.10
  jruby-1.7.2
  jruby-1.7.3
  jruby-1.7.4
  jruby-1.7.5
  jruby-1.7.6
  jruby-1.7.7
  jruby-1.7.8
  jruby-1.7.9
  jruby-9000-dev
  maglev-1.0.0
  maglev-1.1.0-dev
  maglev-2.0.0-dev
  mruby-dev
  rbx-1.2.4
  rbx-2.0.0
  rbx-2.0.0-dev
  rbx-2.0.0-rc1
  rbx-2.1.0
  rbx-2.1.1
  rbx-2.2.0
  rbx-2.2.1
  rbx-2.2.2
  rbx-2.2.3
  rbx-2.2.4
  rbx-2.2.5
  ree-1.8.6-2009.06
  ree-1.8.7-2009.09
  ree-1.8.7-2009.10
  ree-1.8.7-2010.01
  ree-1.8.7-2010.02
  ree-1.8.7-2011.03
  ree-1.8.7-2011.12
  ree-1.8.7-2012.01
  ree-1.8.7-2012.02
  topaz-dev

そのまま出力を貼ったが、前に見た時よりか増えた気がするなぁ。maglevとかrbxとかreeとか知らん。今はツッコまないことにしておきます。んで、Ruby2.0でいいかと思っていたけど、なんとなく2.1を入れてみることにした。2.1.0-rc1がdevじゃなくて一番新しそうなので入れてみる。
rbenv install 2.1.0-rc1

あ~結構時間がかかりそうだなぁ。Core2Duoマシンとはいえ、Proxmoxのオーバーヘッドもあることだし。
あれ?devが入ってるんだけど?
rbenv global 2.1.0-rc1
ruby -v
ruby 2.1.0dev (2013-12-20 trunk 44301) [x86_64-linux]

まっさらな無印2.1.0を入れてみる。
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-linux]

ん~こっちのほうがいくらかいいのかなぁ。

思ったよりもrbenvを入れるのは面倒じゃなかった。基本的にインストールは置くだけみたいだし。ただ、rbenv本体がどのバージョンで動くかどこで確かめるのかな。ソースのメモとか見るの面倒なんだけど。とりあえず、この状態でやってみる。





結局やりたいのは、Rubyから採用になったEnumerable#lazyを使いたいってことだけなんですけどね。

ググったら、るびまに出てた。
 http://magazine.rubyist.net/?0041-200Special-lazy=
Enumerable#lazyをまんま説明していますが、るびまは公式サイトのようなものなので、細かい設定の
map はとても便利なメソッドですが、一つ弱点がありました。それは、無限リストを map しようとすると、無限の長さの配列を作ろうとしていつまでも結果が帰ってこないということ

結局、このことに間違いはないんだけど、使う方としてはどのタイミングでどう使うと便利なのか、という事。何かライブラリを使うぶんには、サンプルコードなどを見たり、生成されるコードをいじくるだけで済むのだが、それとは別に自分のところでやることもあろうし。


Enumeratorはクラスで、Enumerableがモジュール。あぁRubyではみんなクラスになっているので(ってザックリだな)、クラスまともに作ったことないよ。そこまでRubyは使ってないし、大体必要なクラスが用意されているので、大規模な開発をしたことがないから、クラスを一部改変するぐらいしかやってない。それもずいぶん前にやったので忘れてる。

ええと、Rubyでクラスとモジュールはどういう位置付けだったっけ?
あぁMix-inっていうので思い出した。今調べなおしてきちんと頭に叩き込んでおこう。

Rubyで多重継承を実現するやり方がMix-inであったが、C++の多重継承はあまり制限しなかったために、問題を引き起こしやすかったわけだ。Javaだとインターフェイスってのがあったけど、これも多重継承の弊害を防ぐための方法の一つだったはず。

 http://www.wakhok.ac.jp/~tatsuo/kougi99/4shuu/implements.html

おぉなんかきっちり設計しないとダメだったような気がした。Javaはほとんど仕事でやってないので、初期のアプレットを作るぐらいの知識しかない。

クラスは書き方は違うけど、本質は他の言語と大して変わらないと思うから深くツッコまない。Rubyでは単一継承が基本で、継承するクラスがたくさんある多重継承ができません。なのでモジュールをMix-inするわけですが、モジュールは、

・モジュール同士やクラスに取り込めるが、継承できない
・単独でインスタンス化できない

という性質があるみたいです。includeでmoduleをMix-inできます。

おんなじメソッド名がクラスとモジュールにあった場合はクラスのほうが優先されるみたい。
あぁ特異クラスとかもあったなぁ。まぁ今回はスルーしとこう。今回のと直接関係ないし。
モジュールのほうが影響が弱いとザックリ考えればいいかな。




ええとEnumeratorですが、Enumerableをincludeしているクラスだって事ですね。ファイルを読み込んでheadコマンドみたいな例を挙げていますが、まずIO#each_lineで遅延してくれていると書いてある。ただ一行ずつ読み込む事を遅延としていいかどうかはよく分からないですが、とにかくそれをmapで使ってしまうとファイル全読み込みになってしまうらしいです。

そもそもmapというのは何かですが、先のリンク先の場合、IO#each_lineでEnumeratorが返ってくるとありますが、これをEnumerator::mapを使って

 http://docs.ruby-lang.org/ja/search/module:Enumerable/query:map/

各要素に対してブロックを評価した結果を全て含む配列を返します。

という事をしているみたい。「すべて含む」ってところが、mapをすると結果的に全部読みに行ってしまう理由になっているわけですね。なのでlazy_mapを使うのはわかった。

やっぱり全体を読みに行ってしまうと時間がかかりすぎたり、そもそもどこが終わりなんだかわからない事項については、最大値を無限大に設定できるのは、コンピュータらしくなくて非常に便利そうだ。これは遅延評価という名前からはすぐ意味的に直結しない気がする。どちらかと言うと、処理を遅延させるというより、全部処理する前提の書き方で、途中で終わらせる事ができるという意味合いが強い。それによって、簡潔に書くことができるので便利だっていう便利さが先のリンクで言われていた。

やっぱり、遅延評価で計算を省けて処理が軽くなるというのは、ごく珍しい事項で、基本的に「無限に続く列」や「巨大な列」、「終わりの分からない列」を今までのスタイルで使えるというのがlazyなメソッドの一番の利点になるんでしょうね。何か遅延評価に必要以上の期待をしすぎたのかもしれない。

タグ:Ruby
コメント(0) 
共通テーマ:資格・学び

コメント 0