Project

General

Profile

Actions

Feature #4653

closed

[PATCH 1/1] new method Enumerable#rude_map

Added by shyouhei (Shyouhei Urabe) almost 13 years ago. Updated about 12 years ago.

Status:
Rejected
Target version:
[ruby-dev:43476]

Description

From e6dd7bd9b5769bae5d81416da7a2b4003a43ba06 Mon Sep 17 00:00:00 2001
Message-Id:
From: URABE, Shyouhei
Date: Sun, 8 May 2011 22:51:28 +0900
Subject: [PATCH 1/1] new method Enumerable#rude_map

時としてmapが配列を返さない方がいいのにと思うことがあります。

例としてはlzmaで圧縮されており解凍するととても大きくなるテキストファ
イルがあったとして、それを解凍して行番号をふってからHTML escapeして

ではさんでから連結したものをlzmaで圧縮しなおす必要があっ

たとします(注:実話)。ここで「行番号をふって」と「HTML escapeして」を
素直に考えると、lzcatをpopenしたものに対して
io.each_line.map.with_index.map. ... とかいう構造がRubyとしては自然
かと思います。が、mapが配列を作ってしまうというのがメモリ消費量的に
よくありません。できればEnumeratorですっきりと処理したいところです。

というわけでmapなんだけど配列じゃなくてEnumeratorを返すmapの変種があ
るといいとおもうのですがどうでしょうか。1.9にはもうflat_mapがあるの
でなんとか_mapが増える心理的抵抗は少ないかなと思うので新規メソッドに
してみました。いきなりmapの戻り値の型が変わるのでも私はいいですけど
ちょっとやりすぎかとも思います。

Signed-off-by: URABE, Shyouhei

diff --git a/enum.c b/enum.c
index 584b838..449406b 100644
--- a/enum.c
+++ b/enum.c
@@ -462,6 +462,41 @@ enum_flat_map(VALUE obj)
return ary;
}

+static VALUE
+rude_map_ii(VALUE i, VALUE y, int c, VALUE *v)
+{

  • return rb_funcall(y, rb_intern("<<"), 1, enum_yield(c, v));
    +}

+static VALUE
+rude_map_i(VALUE y, VALUE i, int c, VALUE *v)
+{

  • return rb_block_call(i, id_each, 0, 0, rude_map_ii, y);
    +}

+/*

    • call-seq:
    • enum.rude_map {| obj | block }  -> enumerator
      
    • enum.rude_map                   -> enumerator
      
    • Identical to Enumerable#map, except that it returns an enumerator
    • rather than an array.
    • Without a block it is just another Object#to_enum.
  • */

+static VALUE
+enum_rude_map(VALUE obj)
+{

  • VALUE ret;
  • RETURN_ENUMERATOR(obj, 0, 0);
  • ret = rb_obj_alloc(rb_cEnumerator);
  • rb_block_call(ret, rb_intern("initialize"), 0, 0, rude_map_i, obj);
  • return ret;
    +}

/*

  • call-seq:
  • enum.to_a      ->    array
    

@@ -2679,6 +2714,7 @@ Init_Enumerable(void)
rb_define_method(rb_mEnumerable, "collect", enum_collect, 0);
rb_define_method(rb_mEnumerable, "map", enum_collect, 0);
rb_define_method(rb_mEnumerable, "flat_map", enum_flat_map, 0);

  • rb_define_method(rb_mEnumerable, "rude_map", enum_rude_map, 0);
    rb_define_method(rb_mEnumerable, "collect_concat", enum_flat_map, 0);
    rb_define_method(rb_mEnumerable, "inject", enum_inject, -1);
    rb_define_method(rb_mEnumerable, "reduce", enum_inject, -1);
    --
    1.7.0.4

Related issues 2 (0 open2 closed)

Related to Ruby master - Feature #708: Lazy Enumerator#select, Enumerator#map etc.Rejectedmatz (Yukihiro Matsumoto)Actions
Related to Ruby master - Feature #4890: Enumerable#lazyClosedyhara (Yutaka HARA)06/16/2011Actions

Updated by shyouhei (Shyouhei Urabe) almost 13 years ago

  • Status changed from Open to Assigned

Updated by mame (Yusuke Endoh) almost 13 years ago

遠藤です。

2011年5月8日23:01 Shyouhei Urabe :

というわけでmapなんだけど配列じゃなくてEnumeratorを返すmapの変種があ
るといいとおもうのですがどうでしょうか。

賛成です。以下のようなコードを書くときいつも気になってました。

big_ary.map {|x| ... }.max

ただ、map だけでなく、select や take にも適用できる convension
を決めるべきだと思います。

「遅延評価」の英語は、普通は lazy か delay だと思います。
rude というのを聞いたことがありません。ただいずれにしても 5 文字
以上の追加は長すぎるように思います。

遠藤はかつて -er (-or) の接尾辞を提案しました (つまり mapper) 。
http://d.hatena.ne.jp/ku-ma-me/20091111/p2

以下の方は _lz の接尾辞を提案されています。
https://github.com/antimon2/enumerable_lz/tree/master/lib/enumerable_lz

いきなりmapの戻り値の型が変わるのでも私はいいですけど
ちょっとやりすぎかとも思います。

2.0 でなら、これもいいと思います。

--
Yusuke Endoh

Updated by mame (Yusuke Endoh) almost 13 years ago

遠藤です。

2011年5月8日23:01 Shyouhei Urabe :

というわけでmapなんだけど配列じゃなくてEnumeratorを返すmapの変種があ
るといいとおもうのですがどうでしょうか。

賛成です。以下のようなコードを書くときいつも気になってました。

big_ary.map {|x| ... }.max

ただ、map だけでなく、select や take にも適用できる convension
を決めるべきだと思います。

「遅延評価」の英語は、普通は lazy か delay だと思います。
rude というのを聞いたことがありません。ただいずれにしても 5 文字
以上の追加は長すぎるように思います。

遠藤はかつて -er (-or) の接尾辞を提案しました (つまり mapper) 。
http://d.hatena.ne.jp/ku-ma-me/20091111/p2

以下の方は _lz の接尾辞を提案されています。
https://github.com/antimon2/enumerable_lz/tree/master/lib/enumerable_lz

いきなりmapの戻り値の型が変わるのでも私はいいですけど
ちょっとやりすぎかとも思います。

2.0 でなら、これもいいと思います。

--
Yusuke Endoh

Updated by shyouhei (Shyouhei Urabe) almost 13 years ago

(05/08/2011 11:21 PM), Yusuke ENDOH wrote:

遠藤です。

2011年5月8日23:01 Shyouhei Urabe :

というわけでmapなんだけど配列じゃなくてEnumeratorを返すmapの変種があ
るといいとおもうのですがどうでしょうか。

賛成です。以下のようなコードを書くときいつも気になってました。

big_ary.map {|x| ... }.max

たぶん def _map; Enumerator.new {|y| each {|i| y << yield(i) } }; end
はみんな一回は書いたことがあると思うので、需要はほぼ問題ないかと思います

ただ、map だけでなく、select や take にも適用できる convension
を決めるべきだと思います。

まあ、そうなればいいのは間違いないですけども、話を大きくしすぎるのも発散して終
了するいつものパターンになりそうな気もするので。堅実に攻めたいと思います。

「遅延評価」の英語は、普通は lazy か delay だと思います。
rude というのを聞いたことがありません。ただいずれにしても 5 文字
以上の追加は長すぎるように思います。

遅延評価であるとみなせるのは事実としても遅延評価っていう言い方をしなきゃいけな
いことはないですよね。特に私の用途の場合は途中で配列を作らないというだけの話で
あって、結局全部評価するわけですし。

あとまあ、とりあえず名前がしっくり来ないときは長めの名前にしといて後から短いや
つを考えるというのは定石かと。べつにどうしてもrudeじゃなきゃいかんという信念が
あるわけではないです。

いきなりmapの戻り値の型が変わるのでも私はいいですけど
ちょっとやりすぎかとも思います。

2.0 でなら、これもいいと思います。

mathnみたいにrequireすると挙動が上書きされて、いやでもそれは状態がグローバルだ
からよくなくてselector namespaceがないと、でもそれはやっぱ話が大きくなりすぎだ
なあ。

やっぱmapがいきなり変わるのは堅実ではないので、実装としては今回提案したものを
推していきたい(+名前変更とか)ですね。

Updated by shyouhei (Shyouhei Urabe) almost 13 years ago

(05/08/2011 11:21 PM), Yusuke ENDOH wrote:

遠藤です。

2011年5月8日23:01 Shyouhei Urabe :

というわけでmapなんだけど配列じゃなくてEnumeratorを返すmapの変種があ
るといいとおもうのですがどうでしょうか。

賛成です。以下のようなコードを書くときいつも気になってました。

big_ary.map {|x| ... }.max

たぶん def _map; Enumerator.new {|y| each {|i| y << yield(i) } }; end
はみんな一回は書いたことがあると思うので、需要はほぼ問題ないかと思います

ただ、map だけでなく、select や take にも適用できる convension
を決めるべきだと思います。

まあ、そうなればいいのは間違いないですけども、話を大きくしすぎるのも発散して終
了するいつものパターンになりそうな気もするので。堅実に攻めたいと思います。

「遅延評価」の英語は、普通は lazy か delay だと思います。
rude というのを聞いたことがありません。ただいずれにしても 5 文字
以上の追加は長すぎるように思います。

遅延評価であるとみなせるのは事実としても遅延評価っていう言い方をしなきゃいけな
いことはないですよね。特に私の用途の場合は途中で配列を作らないというだけの話で
あって、結局全部評価するわけですし。

あとまあ、とりあえず名前がしっくり来ないときは長めの名前にしといて後から短いや
つを考えるというのは定石かと。べつにどうしてもrudeじゃなきゃいかんという信念が
あるわけではないです。

いきなりmapの戻り値の型が変わるのでも私はいいですけど
ちょっとやりすぎかとも思います。

2.0 でなら、これもいいと思います。

mathnみたいにrequireすると挙動が上書きされて、いやでもそれは状態がグローバルだ
からよくなくてselector namespaceがないと、でもそれはやっぱ話が大きくなりすぎだ
なあ。

やっぱmapがいきなり変わるのは堅実ではないので、実装としては今回提案したものを
推していきたい(+名前変更とか)ですね。

Updated by mame (Yusuke Endoh) almost 13 years ago

遠藤です。

2011年5月9日0:07 Urabe Shyouhei :

遅延評価であるとみなせるのは事実としても遅延評価っていう言い方をしなきゃいけな
いことはないですよね。

絶対にいけないことはないですが、一般的な言葉があるならなるべく合わせる
べきでしょう。

特に私の用途の場合は途中で配列を作らないというだけの話で
あって、結局全部評価するわけですし。

中間データ生成の省略は遅延評価のありがたみの 1 つです。
「結局全部評価する」かどうかは関係ないと思います。

いきなりmapの戻り値の型が変わるのでも私はいいですけど
ちょっとやりすぎかとも思います。

2.0 でなら、これもいいと思います。

mathnみたいにrequireすると挙動が上書きされて、いやでもそれは状態がグローバルだ
からよくなくてselector namespaceがないと、でもそれはやっぱ話が大きくなりすぎだ
なあ。

2.0 なら selector namespace (というか refinement) が入っているはず、
とか。
互換性が問題なら、Enumerator の #[] とかを呼び出したら勝手に Array に
化ける、とか。

とはいえとりあえず 1.9 の間で凌ぐ術がほしい気持ちもわかるので、うーん、
-er がダメなら接尾辞 L はどうでしょう。mapL とか selectL とか。
2.0 までに投げ捨てたくなる名前、という意味では rude_ もいいのかなあ。

--
Yusuke Endoh

Updated by mame (Yusuke Endoh) almost 13 years ago

遠藤です。

2011年5月9日0:07 Urabe Shyouhei :

遅延評価であるとみなせるのは事実としても遅延評価っていう言い方をしなきゃいけな
いことはないですよね。

絶対にいけないことはないですが、一般的な言葉があるならなるべく合わせる
べきでしょう。

特に私の用途の場合は途中で配列を作らないというだけの話で
あって、結局全部評価するわけですし。

中間データ生成の省略は遅延評価のありがたみの 1 つです。
「結局全部評価する」かどうかは関係ないと思います。

いきなりmapの戻り値の型が変わるのでも私はいいですけど
ちょっとやりすぎかとも思います。

2.0 でなら、これもいいと思います。

mathnみたいにrequireすると挙動が上書きされて、いやでもそれは状態がグローバルだ
からよくなくてselector namespaceがないと、でもそれはやっぱ話が大きくなりすぎだ
なあ。

2.0 なら selector namespace (というか refinement) が入っているはず、
とか。
互換性が問題なら、Enumerator の #[] とかを呼び出したら勝手に Array に
化ける、とか。

とはいえとりあえず 1.9 の間で凌ぐ術がほしい気持ちもわかるので、うーん、
-er がダメなら接尾辞 L はどうでしょう。mapL とか selectL とか。
2.0 までに投げ捨てたくなる名前、という意味では rude_ もいいのかなあ。

--
Yusuke Endoh

Updated by shyouhei (Shyouhei Urabe) almost 13 years ago

卜部です。

(05/09/2011 12:35 AM), Yusuke ENDOH wrote:

いきなりmapの戻り値の型が変わるのでも私はいいですけど
ちょっとやりすぎかとも思います。

2.0 でなら、これもいいと思います。

mathnみたいにrequireすると挙動が上書きされて、いやでもそれは状態がグローバルだ
からよくなくてselector namespaceがないと、でもそれはやっぱ話が大きくなりすぎだ
なあ。

2.0 なら selector namespace (というか refinement) が入っているはず、
とか。
互換性が問題なら、Enumerator の #[] とかを呼び出したら勝手に Array に
化ける、とか。

とはいえとりあえず 1.9 の間で凌ぐ術がほしい気持ちもわかるので、うーん、
-er がダメなら接尾辞 L はどうでしょう。mapL とか selectL とか。
2.0 までに投げ捨てたくなる名前、という意味では rude_ もいいのかなあ。

さすがにキャメルケースはないんじゃないですかねえ(何がないって、前例が...)。個
人的にはmapperはそんなにダメじゃないとおもうんですけど、まあまつもとさんがダ
メって言ってる以上はめがないですよね。

でまあやはりflat_mapがある以上なんとか_mapがいちばん通りやすいと思うんですけど
どうですかね。lazy_mapとか。

Updated by shyouhei (Shyouhei Urabe) almost 13 years ago

卜部です。

(05/09/2011 12:35 AM), Yusuke ENDOH wrote:

いきなりmapの戻り値の型が変わるのでも私はいいですけど
ちょっとやりすぎかとも思います。

2.0 でなら、これもいいと思います。

mathnみたいにrequireすると挙動が上書きされて、いやでもそれは状態がグローバルだ
からよくなくてselector namespaceがないと、でもそれはやっぱ話が大きくなりすぎだ
なあ。

2.0 なら selector namespace (というか refinement) が入っているはず、
とか。
互換性が問題なら、Enumerator の #[] とかを呼び出したら勝手に Array に
化ける、とか。

とはいえとりあえず 1.9 の間で凌ぐ術がほしい気持ちもわかるので、うーん、
-er がダメなら接尾辞 L はどうでしょう。mapL とか selectL とか。
2.0 までに投げ捨てたくなる名前、という意味では rude_ もいいのかなあ。

さすがにキャメルケースはないんじゃないですかねえ(何がないって、前例が...)。個
人的にはmapperはそんなにダメじゃないとおもうんですけど、まあまつもとさんがダ
メって言ってる以上はめがないですよね。

でまあやはりflat_mapがある以上なんとか_mapがいちばん通りやすいと思うんですけど
どうですかね。lazy_mapとか。

Updated by shyouhei (Shyouhei Urabe) almost 13 years ago

(05/09/2011 05:21 PM), Akinori MUSHA wrote:

At Mon, 9 May 2011 16:35:31 +0900,
Urabe Shyouhei wrote:

でまあやはりflat_mapがある以上なんとか_mapがいちばん通りやすいと思うんですけど
どうですかね。lazy_mapとか。

ベタ路線なら map_enum がいいと思います。動作を表す語が複数並んで
いたら、返り値は最後の語から判断するものだと思うからです。

rudeやlazyは動作なのかという疑問(形容詞ですからねえ)をさておくとすると、それな
りに説得力があるように思いました。

Updated by shyouhei (Shyouhei Urabe) almost 13 years ago

(05/09/2011 05:21 PM), Akinori MUSHA wrote:

At Mon, 9 May 2011 16:35:31 +0900,
Urabe Shyouhei wrote:

でまあやはりflat_mapがある以上なんとか_mapがいちばん通りやすいと思うんですけど
どうですかね。lazy_mapとか。

ベタ路線なら map_enum がいいと思います。動作を表す語が複数並んで
いたら、返り値は最後の語から判断するものだと思うからです。

rudeやlazyは動作なのかという疑問(形容詞ですからねえ)をさておくとすると、それな
りに説得力があるように思いました。

Updated by sorah (Sorah Fukumori) almost 13 years ago

共通なconvensionを用意すると発散すると書かれていたのにやってしまいましたが、

Enumerator#deferを定義してこのような形にすると良いのではと思いました。

個人的に見栄えはこれが一番しっくりくるなあ、と思います。

[1,2,3].map.defer{|x| x*2 }.each_with_index.map.defer do |x,i|
"#{x}: #{i}"
end.each {|x| puts x }

いかがでしょうか。

パッチ: https://gist.github.com/964510

Updated by mame (Yusuke Endoh) almost 13 years ago

遠藤です。

2011年5月10日23:09 Shota Fukumori :

共通なconvensionを用意すると発散すると書かれていたのにやってしまいましたが、

Enumerator#deferを定義してこのような形にすると良いのではと思いました。

見栄えはいいですが、残念ながら共通の convension にはなりません。

$ ./ruby -e 'p [1, 2, 3].select {|x| x.even? }'
[2]

$ ./ruby -e 'p [1, 2, 3].select.defer {|x| x.even? }.to_a'
[false, true, false]

この問題は、map やら select やらが Array を返すこと前提で、内部的に
rb_ary_push を呼んでしまっているため、中間データ生成を回避しようが
ないということです。なので mapL 、selectL というように各メソッドごとに
対応していかざるを得ません。

Enumerator から Array を得るのは to_a 一発なので、デフォルトで
Array ではなく Enumerator を返してくれれば解決なんですが。
互換性以外は。。

--
Yusuke Endoh

Updated by mame (Yusuke Endoh) almost 13 years ago

遠藤です。

2011年5月10日23:09 Shota Fukumori :

共通なconvensionを用意すると発散すると書かれていたのにやってしまいましたが、

Enumerator#deferを定義してこのような形にすると良いのではと思いました。

見栄えはいいですが、残念ながら共通の convension にはなりません。

$ ./ruby -e 'p [1, 2, 3].select {|x| x.even? }'
[2]

$ ./ruby -e 'p [1, 2, 3].select.defer {|x| x.even? }.to_a'
[false, true, false]

この問題は、map やら select やらが Array を返すこと前提で、内部的に
rb_ary_push を呼んでしまっているため、中間データ生成を回避しようが
ないということです。なので mapL 、selectL というように各メソッドごとに
対応していかざるを得ません。

Enumerator から Array を得るのは to_a 一発なので、デフォルトで
Array ではなく Enumerator を返してくれれば解決なんですが。
互換性以外は。。

--
Yusuke Endoh

Updated by sorah (Sorah Fukumori) almost 13 years ago

2011/5/11 Yusuke ENDOH :

この問題は、map やら select やらが Array を返すこと前提で、内部的に
rb_ary_push を呼んでしまっているため、中間データ生成を回避しようが
ないということです。なので mapL 、selectL というように各メソッドごとに
対応していかざるを得ません。
たしかに。実装みたらそんな感じでした… 残念。

別のクラスを作って、defer.map{|x|...}とかto_lazy.map{|x|...}とかかなあ。

こういうインターフェースでスパッと出来る方が見栄えはいいんだけどなあ…

--
Shota Fukumori a.k.a. @sora_h - http://codnote.net/

Updated by sorah (Sorah Fukumori) almost 13 years ago

2011/5/11 Yusuke ENDOH :

この問題は、map やら select やらが Array を返すこと前提で、内部的に
rb_ary_push を呼んでしまっているため、中間データ生成を回避しようが
ないということです。なので mapL 、selectL というように各メソッドごとに
対応していかざるを得ません。
たしかに。実装みたらそんな感じでした… 残念。

別のクラスを作って、defer.map{|x|...}とかto_lazy.map{|x|...}とかかなあ。

こういうインターフェースでスパッと出来る方が見栄えはいいんだけどなあ…

--
Shota Fukumori a.k.a. @sora_h - http://codnote.net/

Updated by shyouhei (Shyouhei Urabe) almost 13 years ago

(05/10/2011 11:09 PM), Shota Fukumori wrote:

共通なconvensionを用意すると発散すると書かれていたのにやってしまいましたが、

まあそういうわけで見事に発散しているので。やっぱりとりあえずmapだけに注力した
ほうがいいと思うわけです。とりあえず実装はもう今のでいいんじゃないですかねえ。

名前に関しては今のところlazy_map案とmap_enum案がありますがどうでしょうか。個人
的にはlazy_enumが好きかなと思いますが。

Updated by shyouhei (Shyouhei Urabe) almost 13 years ago

(05/10/2011 11:09 PM), Shota Fukumori wrote:

共通なconvensionを用意すると発散すると書かれていたのにやってしまいましたが、

まあそういうわけで見事に発散しているので。やっぱりとりあえずmapだけに注力した
ほうがいいと思うわけです。とりあえず実装はもう今のでいいんじゃないですかねえ。

名前に関しては今のところlazy_map案とmap_enum案がありますがどうでしょうか。個人
的にはlazy_enumが好きかなと思いますが。

Updated by mame (Yusuke Endoh) almost 13 years ago

遠藤です。

2011年5月13日3:44 Urabe Shyouhei :

名前に関しては今のところlazy_map案とmap_enum案がありますがどうでしょうか。個人
的にはlazy_enumが好きかなと思いますが。

lazy_enum は lazy_map の typo ですよね?
どうしてもそこから選べと言われたら遠藤も lazy_map に一票です。

ちなみに、

http://d.hatena.ne.jp/ku-ma-me/20091111/p2#c1258760977

では matz は enum_map を提案していました。
さらになかださんは -ing という案をあげていました。
個人的には

mapper >= mapping >> mapL >= map_lz >> lazy_map > enum_map > map_enum

です。mapE 、map_e とかでもいいかも。

--
Yusuke Endoh

Updated by mame (Yusuke Endoh) almost 13 years ago

遠藤です。

2011年5月13日3:44 Urabe Shyouhei :

名前に関しては今のところlazy_map案とmap_enum案がありますがどうでしょうか。個人
的にはlazy_enumが好きかなと思いますが。

lazy_enum は lazy_map の typo ですよね?
どうしてもそこから選べと言われたら遠藤も lazy_map に一票です。

ちなみに、

http://d.hatena.ne.jp/ku-ma-me/20091111/p2#c1258760977

では matz は enum_map を提案していました。
さらになかださんは -ing という案をあげていました。
個人的には

mapper >= mapping >> mapL >= map_lz >> lazy_map > enum_map > map_enum

です。mapE 、map_e とかでもいいかも。

--
Yusuke Endoh

Actions #21

Updated by naruse (Yui NARUSE) almost 13 years ago

  • Target version changed from 1.9.3 to 2.0.0

Updated by mame (Yusuke Endoh) about 12 years ago

  • Status changed from Assigned to Rejected

まつもとさんが #4890 のやり方がいいと言ったので (#708) 、
2/8 は Enumerable::Lazy 記念日。

--
Yusuke Endoh

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0