From: Shin-ichiro Hara Date: 1998-12-18T18:34:33+09:00 Subject: [ruby-dev:3865] Re: List() 原です。 In message "[ruby-dev:3855] Re: List()" on 98/12/17, MAEDA Shugo writes: > 前田です。 > class StringScanner > include Enumerable > > def initialize(str, pat) > @str = str > @pat = pat > end > > def each(&block) > @str.scan(@pat, &block) > end > end > > scanner = StringScanner.new("a1b2c3", /[a-z][0-9]/) > scanner.my_each_with_index {|x| ... } なるほど、/[a-z][0-9]/ じゃなくて /([a-z])([0-9])/ みたいにした ときですね。またまた一本とられました。というか、どうしても前田さ んの主張で分からないことがあったんですが今これで理解できました。 もっと早く分かるべきだった。つーか、何でもっとこの例を早く出して くれなかったのー、と開き直ったりして、、(^^;;; この StringScanner#my_each_with_index は Stirng#scan に依存する A-L(Array-List) sensitive(今作った造語)なメソッドなわけですね。 この例は、ユーザーが意識しなくてもインクルードするライブラリが A-L sensitive なのでユーザーの作るメソッドが A-L sensitive に なってしまった例です。 [ruby-dev:3789] で前田さんはこう言いました。 >僕が考えていたのは、基本的に配列とリストを区別しないというモデル >が前提で|*x|の時だけ区別することになると、yield([1, 2])として複数の >値を渡すつもりだった人にとっては、上のeach_with_indexのような >場合に挙動が変わってきて困惑することになるし、逆に受け取る方として >はリストではなく配列を受け取ったとしても、yieldした側が複数の値と >して渡したかったのかもしれないというリスクがつきまとってしまう >ということです。 で、私は scan の利用者は scan の仕様について良く知っているから、そ のリスクというものは無いのではないか?という風に反論したのですが、 これは考えが足りなかったと思います。今回の例の様に scan の仕様に詳 しくない StringScanner のユーザーあるいは StringScanner の作者とい うのはありえるわけですね。その人にとっっては StringScanner の my_each_with_index の挙動は「やって見ないと分からない」と感じられる でしょう。 そして、その状態を避けるために、今まで scan などのブロックパラメー タに配列を渡していたイテレータに対する対処の仕方としては、次の様な ものが考えられます。 (A)今まで通り配列を渡す。 これはせっかくリストを渡せるようになったのに、(前田さんが言っ ていましたが)そもそも何のためのモデル変更か意義が疑われてもし かたがない。(まあそれでもいいや、というのが私の意見だった。) (B)Hash#each はリストにするが String#scan などは配列のまま。 マニュアルで全てきちんとリストを渡しているのか配列を渡しているの か明示しないと利用者が困る。とても似ているのに異なる、そこが好ま しくない。 (C)複数の値という事を意図して配列を渡していたものは全てリス トに変える。 やはりこれも前田さんが [ruby-dev:3789] で、 (私の案に従うならば) >少なくとも標準ライブラリについては、配列で複数の値を渡すことを >期待したコードをすべて書き直す必要があるように思います。 と指摘したまさにその通りです。 で、現在の私の提案は(C)です。なぜなら、変更が必要な標準ライブラ リのメソッドは (Hash|Dbm|ENV).(each|delete_if?) と String#scan だ けみたいだからです。他にもあるかもしれないけどそう多くはないのでやっ てしまえ、という事です。非互換性もほとんどない。RAA も気になります が。 どうでしょうか? Enumerable の仕様の方に話を移します。 > > (あ) > > def collect > > ary = [] > > each { |x| > > ary.push yield(x) > > } > > ary > > end > > > > という定義ですが、List 方式が導入されたらやはり > > > > (い) > > def collect > > ary = [] > > each { |*x| > > ary.push yield(*x) > > } > > ary > > end > > Cだと(い)は書きにくいので、Cレベルでは直接Listが見えるようにした > 方がいいかもしれませんね。 > そうすると(あ)の書き方で(い)と同じ意味になると思います。 C のレベルで List が見えるのはいいと思います。(あくまで Ruby の レベルでの言葉遣いをすることにすると、、) Enumerable#collect な どは(い)の動作にしてしまうのが自然なのですね。それならそれがい い思います。 というわけで、Enumerable のイテレータは each_with_index 以外は each に関して A-L sensitive なものすなわち |*x| で受けて *x と 送る様なものに書き換える、ということでいいでしょうか? [ruby-list:11043] の enumerable.rb を見ながら考えるに、find_all はちょっと注意が必要かな。 #しかしこれだけ議論して、現実に役に立つ A-L sensitive なメソッ #ドって my_each_with_index ぐらいしか出てないのが恐ろしい。:-)