From: MAEDA Shugo Date: 1998-12-11T03:33:58+09:00 Subject: [ruby-dev:3791] Re: List() 前田です。 In message "[ruby-dev:3790] Re: List()" Shin-ichiro Hara wrote: > 現在の仕様において、複数の値を yield に渡す基本形は > 配列を作って引数にする方法である。yield(x, y) は > yield([x, y]) の省略形である。 はい。 > > 僕が先のメールで示したeach_with_indexでは、Hash#eachで > > yield([key, val])としても、yield(key, val)としても、現在の仕様では > > 前者の動作になりますよね? > > 逆に原さんの提案が採用されるとyield([key, val])の場合は後者の動作に > > ると思います。 > > 全くその通り。変だなあ、同意見が反論されている。(^^;; > > 私の望みは、each_with_index に前者の様な自然な動作をして欲しい > のではなくて、前者の様な自然な動作をするような each_with_index > が定義可能でありながら後者の様な動作をする定義になっている状態、 > です。(ひねくれてるんじゃなくて互換性のため。)|(k,v),i| とい > う受け方もあるし。 なるほど。 でも、そのケースは互換性という問題があるので、かなり特殊ですよね。 前者のようなeach_with_indexの定義が可能であれば、モデルとしては 十分だと思います。 問題はどうやら、それが無理らしいということですが(^^; > > 現在の仕様でも先の例の前者の挙動にしたければ、先のメール > > のようにeach_with_indexを変更すれば良いわけですし。 > > ここは反論がはっきりできると思うのだけど。先の、というのは > [ruby-dev:3743] で前田さんが書いた each_with_index2 の事 > ですよね。でもこれは [[1]].each_with_index2 { |x| p x } > が [1, 0] になるのでまずいんじゃないかと、後で私は書きまし > た。それについては回答が無かったんだけど、重要な問題だとは > 思われなかったんかな。改めて、、 あ、読み逃していました、すみません。 > 問題:次の例のような動作するな Enumerable のメソッド > each_with_index3 の定義は何? > > [1, 2].each_with_index3 { |x| #=> x = [1, 0] > x = [2, 1] > [[1], [2]].each_with_index3 { |x| #=> x = [[1], 0] > x = [[2], 1] > [[1,2], [3,4]].each_with_index3 { |x| #=> x = [[1,2], 0] > x = [[3,4], 1] >  {1=>2, 3=>4}.each_with_index3 { |x| #=> x = [1, 2, 0] > x = [3, 4, 2] > > 私は、現在の仕様では、レシーバ self の形による場合分けでしか > 書けないと思います。 同意します。 問題は実はyield(1, 2)とした場合ではなくて、yield(1) とした場合なんですね。 yieldを本来のapply的な意味に限定すると、Array#eachは、 class Array def each for i in 0..self.length-1 yield([self[i]]) end end end と書いて、 [1, 2, 3].each { |x,| p x } とするべきなんですよね。 # これでは使いにくいので現在の定義になっているわけですが。 Array#eachをこのように定義すれば、each_with_indexの定義は each_with_index2で十分なわけですが、この答案では原先生の 授業の単位は取れませんね(^^; > > 僕が考えていたのは、基本的に配列とリストを区別しないというモデル > > が前提で|*x|の時だけ区別することになると、yield([1, 2])として複数の > > 値を渡すつもりだった人にとっては、上のeach_with_indexのような > > 場合に挙動が変わってきて困惑することになるし、逆に受け取る方として > > はリストではなく配列を受け取ったとしても、yieldした側が複数の値と > > して渡したかったのかもしれないというリスクがつきまとってしまう > > ということです。 > > ここははっきり意見が分かれました。私はその困惑とリスクは無いと > 考えています。まず前者ですが、配列を複数の値で渡すつもりだった > 人は、つまり funcall 的に使った人はパラメータを |x, y| と複数 > で受けるか、|x| と一つで受けて x[i] を利用するでしょう。なぜな > ら、funcall 的であるときパラメータの数は決まっているか、さもな > ければ何番目の値が何を意味しているかそれぞれ理解しているはずだ > からです。つまり funcall 的に利用される yield を |*x| で受け > ることは現状では殆どないと考えます。 > > 後者の受け手にリスクがあるという件ですが、イテレータの利用者は > そのイテレータについて知っているので問題ないのでは?従って複数 > の値を配列で渡す状況では今でも私の案でもかえってまちがいが少な > い気がします。ただ、Enumerable の様なイテレータからイテレータ > を作るいわばメタ・イテレータの様な物はその限りにあらずで、相手 > もが誰だかわからない。その場合は素通しできる私の案の仕様が有利 > でしょう。 僕がいいたかったのは、原さんの仕様で、 class Enumerable def each_with_index i = 0 each { |*x| yield(*(x+i)) i+=1 } end end class Hash def each each_keys {|key| yield([key, self[key]]) end end end と定義した時に、each_with_indexの挙動は、Hashを書いた人の意図と 異なるのではないかということです。 > > 原さんのように配列を複数の値として渡せるのは互換性のためだけだと > > して、yieldで配列を渡しても複数の値と解釈されない場合があること > > を仕様で明示して、そのような動作を期待してはいけないことにすれば > > 問題ないと思いますが、そうすると配列で複数の値を渡せるという面 > > は、メリットというよりデメリットになりますね。 > > 互換性だけでなくそれは便利だから、、、 > > わからないなあ。仕様を明示する必要があるのはその通りだけど。複数 > の値 1, 2, 3 を yield [1, 2, 3] と渡すのは全くOKですよ。受け > 側がそれを知っていればね。現状でそれ(複数の値を配列で渡した事) > を知らずにイテレータを利用する例ってありますか?現状の方が仕様の > 明示はより強く求められると思うけど。ただ、私の仕様ではもはや、 > yield(1, 2, 3) は yield [1, 2, 3] の省略形ではないということは > はっきりさせておくべきですね。 受け手と送り手の間に了解があれば、配列で複数の値を渡すことに 問題がないのには僕も同意します。 ただ、もともとモデル変更の話が出てきたのは、この了解が成立しない ケースを解決するためですよね。 > 哲学を言いますとね(前田さん相手になんと言うことを(^^;) Array これは哲学なのでしょうか(^^; # プラグマティズムかな? > 私には前田さんは List プロトコルの |*x| による復号は失敗して低 > 層がむき出しになるから危険だと言っている様に感じます。私はそれは > Array プロトコルより解像度が上がったのでノイズが目立つ様に感じ > ているだけで、それはノイズではなくて本来の姿です。気になるなら > フィルターをかければいい。つまり |x| や |x, y,..| で受ければい > い。 Array プロトコルで、yieldをapply的な意味に完全に限定した場合 (つまりyield(i)をやめてyield([i])にして|i,|で受ける)は、より 解像度が高いですよね。 # 上記のeach_with_index/Hashのケースです。 ただ実際には互換性の問題があるので、現状のArray プロトコル (yield(i)を許す)を用いるより、List プロトコルを導入した方が 解像度は高くなる、と。 > > 少なくとも標準ライブラリについては、配列で複数の値を渡すことを > > 期待したコードをすべて書き直す必要があるように思います。 > > 変だなあ。私は書き直す必要があるライブラリは殆どないと思うのです > が、例えばどんな例がありますか?もちろんコンパイルし直す必要はあ > るでしょうが。:-) 少なくとも、Hash#eachをyield(key, val)に書き直さないと、 module Enumerable def each_with_index2 i = 0 each { |*x| yield(*(x + i) i += 1 } end end が期待通りに動かないと思います。 これをレアケースだから問題ないとすると、そもそもモデル変更の 必然性が疑われてしまいますよね? > > 現状のモデルを変更しないとしても、each_with_indexの変更は > > できますよね? > > 難しいんじゃないかなあ。(each_with_index だけの問題ではないのだ > けど)ええとさしあたってさっきの問題に答えてもらえます?(^^; > #お暇な時で結構ですから。(^^;; とりあえず、現状のyield=applyモデルではArray#eachの定義は、 例外的(なのにもっとも多く使われている(^^;)ケースなので考慮 しなくてよい、したがって def each_with_index i = 0 each { |x| yield(x + i) i += 1 } end という定義が正しい。 という答案にしておきます。 # これでは落第だ(^^; -- 前田 修吾 (mailto:shugo@aianet.ne.jp) P.S. ちなみにこんな時間まで起きてるのは卒論を来週先生に見せなければ、 いけないからで、Rubyで遊んでたわけではないです:-( # なのに、こんなメールを書いてていいのだろうか。