enumerable_lz 0.1.5 公開しました。
なんか今更感ハンパないですけれど。
約 3 年ぶりに、自作の gem の新バージョンを公開しました。
もう誰も使ってないかもしれないにもかかわらずw
enumelable_lz 0.1.5
enumerable_lz (0.1.5): http://t.co/3a62B1Bt8c Add Enumerable#filter, Enumerable#transform and some equivalent methods on En…
— RubyGems (@rubygems) January 8, 2014
簡単に言うと、Enumerable に遅延リストを返すフィルタリングメソッド Enumerable#filter
と変換メソッド Enumerable#transform
を追加する gem です。
Ruby 2.0.0 で導入された Enumerable#lazy
と同じような機能を提供します。
でも Enumerable#lazy
の挙動がどうしてもなじめなくて、メンテナンスリリースの意味も込めてアップデートしちゃいました。
【2014/04/23 23:33 追記あり】
特長
もう今更感満載なので簡単に列挙しておきます。
Enumerable#filter
は、Enumerable#select
やEnumerable#grep
の遅延バージョンです。- Ruby 2.0 以降の
Enumerator::Lazy#select
とほぼ同様の動作をします。
- Ruby 2.0 以降の
Enumerable#transform
は、Enumerable#map
(別名Enumerable#collect
)の遅延バージョンです。- Ruby 2.0 以降の
Enumerator::Lazy#map
とほぼ同様の動作をします。
- Ruby 2.0 以降の
- 少し古い Ruby(1.8.7, 1.9.x)でも動作します。
- cRuby 以外にもいくつか対応しています(テストしているのは JRuby と MacRuby の最新のみ)。
- Ruby 2.0 の
Enumerable#lazy
よりは速いです。 - 《New for 0.1.5》
〜.filter.with_index{|el, i| 〜 }
というメソッドチェインができます。〜.select.with_index{|el, i| 〜 }
と同様に動作し、即実行されて結果の配列を返す代わりに、遅延実行される遅延リストを返します。 - 《New for 0.1.5》
〜.transform.with_index{|el, i| 〜 }
というメソッドチェインができます。〜.map.with_index{|el, i| 〜 }
と同様に動作し、即実行されて結果の配列を返す代わりに、遅延実行される遅延リストを返します。
特に最後の 2 つ。
例えばこんなコードを考えてみてください*1。
require 'enumerable_lz' (1..99999999).select.with_index{|n, i| i.odd?}.take(10) #1 (1..99999999).filter.with_index{|n, i| i.odd?}.take(10) #2 (1..99999999).lazy.select.with_index{|n, i| i.odd?}.take(10) #3
見た目よく似た 3 つのコードですが。
#1 は、先に全要素を算出してしまうので、環境にもよりますが数秒〜数十秒待たされますが、その上で結果としては正しい結果(=[2, 4, 6, 8, 10, 12, 14, 16, 18, 20])が得られます。
それに対して #2 は、遅延リストの特性で必要な要素のみ算出するので、一瞬で正しい結果が得られます。
では、 #3 はというと。なんと ArgumentError
になります(Ruby 2.0.0/2.1.0 ともに)。このようなメソッドチェインが許されていない(より正確には .lazy.select がブロックを省略できない)んですね。
実は今回修正するまで、#2 も正常に動作しませんでした(^-^;
.with_index
が遅延リストにもならなかった上にフィルタリングも動作していなかった(^-^;
#3 を先に試して「これはないな」と思った直後に #2 試したらそのざまだったので、思い立って修正リリースしたというわけです。
ま、どれだけ需要があるか全く期待できませんけれども。
.lazy.select.with_index{ 〜 }
がエラーで動かないことにお困りの方。
.select.with_index{ 〜 }
の便利さを知っていて、その遅延リスト版をお探しの方。
もしよろしければ、enumerable_lz 0.1.5 お試し下さい。
ていうか、.lazy.select
とかtoka .lazy.map
とかが、ブロックが渡されなかったら Enumerator::Lazy
を返すようになれば解決するのかも?
【2014/01/09 18:00 追記】
@cielavenir さんがサンプル作って検証して下さいました。
↓http://qiita.com/cielavenir/items/7c0ba3ab1cd91f06952e
lazy_select.rb
classEnumeratorclassLazyalias:select_without_block:selectdefselect(&block)returnto_enum(:select)if!block_given?select_without_block(&block)endendendp(1..999999999).lazy.select.with_index{|e,i|i.odd?}.take(10).to_a
ruby1.9 -v
ruby 1.9.3p194 (2012-04-20) [i686-linux]
ruby1.9 -rbackports lazy_select.rb
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
ruby2.0 lazy_select.rb[BUG] vm_call_cfunc - cfp consistency error
ruby 2.0.0p247 (2013-06-27 revision 41674) [universal.x86_64-darwin13]
いやですね、本当は http://antimon2.hatenablog.jp/entry/2014/01/08/235706 への肯定的な解答にしたかったんですよ( https://github.com/yhara/enumerable-lazy/blob/master/lib/enumerable/lazy.rb でもantimon2/enumerable_lzに謝辞書かれてますし )。
しかしまさかのクラッシュ…。
PS
Lazy.class_eval{}とすれば、ruby1.9 -renumerable/lazyでも期待通りの動作をします。
(Evernote 貼り付けってばほぼただのテキストになるのね(>_<))
手元の環境でもいろいろ試してみたところ、Ruby 2.0.0 の場合は、Bus Error とか Segmentation Fault とか、名前は違えどやはりとにかくクラッシュすることに変わりは無いようでした(>_<)
なお、Ruby 2.1.0 ではクラッシュせず期待通りに動作することを確認しました。
【2014/04/23 23:33 追記】
Ruby 2.0.0-p451で修正されたそうです。
*1:あまり意味の無いコードですけれど、あくまで結果が良く分かる動作例ということで。