あおきです。議論も好き。

  In mail [ruby-dev:8809]
    Re: [REQ] Array#each{|a,b,...|}, Array#shift/pop(num)
    Kazunori NISHI <kazunori / swlab.csce.kyushu-u.ac.jp> wrote:

> 西@九大です。

> > 2  いま必要とされているのは splice のうち破壊的抽出の機能で
> >    それ以外の機能(あとに値を代入)を導入する理由はない
> 
> これは「2つの機能を持つ(1つの)メソッドより、それぞれの機能を持つメソッ
> ドを(2つ)作った方が汎用的」という意味ですね?それなら理解できます。

いいえ。
西さんが最初に書いておられたのは、「破壊的抽出がほしい」ということ
だったはずです。しかし、splice でそれができるということがわかったからと
いって splice の全機能を実装する理由はありません。
欲しいものだけ(extract)を実装するだけではなぜだめなのか、ということです。

(略……)
> これも、上と同じ疑問を持ちます。「ないので?」という感じです。「ないの
> で、あると大変便利」かもしれない。「他の(Ruby の)メソッドと比べて異質」
> というのであっても、その異端と思われた方が実は「高き良き処」かもしれな
> い。と思うのです。
> 
> 繰り返しになりますが、「抽出と代入」をまとめる方がいい事なのかは自分の
> 中でもまだ答えがでていません。でも、「今はこうだから」という理由でそれ
> 以上考えないのも危険(未定義語)だと思うのです。
> 
> どんどん仕様が変わるのも考え物ですが、「破壊なくして建設なし」とも言い
> ますし(言わない?)、もっとよい物であれば、思いきって変えるのもいいと思
> います。ruby-xxx でのどなたの言葉だったか忘れましたが、それを誇張して、

たしかに、それはその通りです。
しかし、それならばまず「spliceを導入するに足る根拠」を挙げる
べきでしょう。しかし西さんからはまだなにも適切な理由が挙げられて
いないと思います。今のところ西さんが書かれたなかで提案理由らしく
見えるのは「Perl で使われている」というものだけですから、
ぼくも「Rubyではそういう使われ方はない(Perlでは適切でもRubyでは
適切でないかもしれないではないか)」と反論しました。

それに、ぼくは Perl は使わないので事情は知りませんが、
「機能はあるけど結局ある使いかたでしか使われない」ということは
ありえるのではないですか?それはみんな知っていても、互換性が
あるから外せないということは十分考えられます。また、なにより
「Ruby は Perl ではない」ので、Perl で使われているということ
それ自体は強力な根拠とはなり得ないと思います。

が、これだけでは積極性がないので、今回は他の理由をあげまず。

一般性。
上で西さんが書かれているように、メソッドは適度に機能が分割
されているほうがよろしいでしょう。
ただし、今回提案の extract も抽出と削除を同時におこないますから、
この理由はあまり強力な根拠となりえないのではないかとも思えます。

nil の自動変換の問題。
array[ i, len ] = nil を使わなくていいことがあげられます。
他のスレッドでも議論があるように、nil と配列のかかわりは微妙な
ものを含みます。また、「無」やゼロを導入することで特定の状況を
統一的に扱うというのは数学的には美しいですが、人間の日常の
感覚からはなかなかとらえにくいものです
(だから、Hash#[]=nil とは別にHash#delete があるわけですよね)。

# ということは delete_at( i, len ) もあったほうがいいなあ。

splice という名前は不適切。
splice って単語は知らなかったんで辞書でひくと、「継ぎ木をする」と
あります。つまり、意図として splice は後の代入のほうを強く意図して
作られたものと推測されます。返り値が取り除いた値になっているのは
ほとんど偶然のようなもので、名前からは全然想像がつきません。

splice は存在理由が薄い。
西さんも extract には反対でない、とおっしゃっているので、じきに
extract 相当の機能が実装されたとした場合、Ruby には範囲のある
抽出もあるし、破壊的取りだしもある。[]= で範囲のある挿入/交換もできる。
それでは splice はどんな場合に使われるでしょうか?
extract してさらに []= するとき(つまり、splice 本来の目的として)
ですよね。そんな機会がそう頻繁にあるんでしょうか?(いやない。)
そう頻繁に使われるとも思えないメソッドふたつのくみあわせを
ひとつにするために標準の機能としてsplice メソッドを用意する
必要はないと思います。

速度。
返すべき範囲が削除を行うときに自動的に得られますから、再計算
しなくてすみます。また、現在の内部実装では rb_ary_replace を
呼ぶ際に必ず配列が必要ですが、extract を組みこみメソッドとして
実装すればこの時必要な空の配列を全メソッドで共有できます。
Ruby では速度はあまり理由にならないのは確かですが、
標準ライブラリに限ってはできるだけ速いほうがいいでしょう。

それから最後にぼくの個人的経験と感覚としての理由です。
配列から一部をごそっと持ってくる(そして残りがでる)という機能は
絵として想像しやすんです。その後に配列を代入するとなると、少なくとも
ひとつのメソッドとしては想像しにくい。また、実際これまでそれなりに
Rubyスクリプトを書いてきて(CVSのリポジトリで1MBちょっと)、splice の
ようなことをしたいと思ったことは一回もありません。一方、extract の
ような使いかたをしたことはあります。


以上です。
あらためて反論をお待ちしています。


> > pop は「最後部の要素をとりのぞく」という目的を持つメソッドであって、
> > pop(3) というのは「3回最後部の要素をとりのぞく」と解釈するほかありません。
> > しかし、Ruby のライブラリにはイテレータを使わずに「x 回 〜 する」という
> > 意味をもつメソッドは存在せず、pop(3) の働きは非常に想起しにくいからです。
> 
> これに関しても、上と同意見です。従来の Ruby ライブラリの範疇では想起さ
> れ難いものであっても、それが大変便利であれば取り込んで行き、結果的に
> Ruby ライブラリに正のフィードバックが成されれば最高だと思います。

では、こちらに関しても理由を書きます。

まず、西さん提案では pop(3) は配列最後尾からみっつということですが、
「最後からみっつめをとりだす」という解釈も自然ではないですか?
たとえば以下のように。

def mpop( time )
  ret = nil
  time.times { ret = pop }
  ret
end

あるいは、配列最後尾からみっつをとりだすにしても、配列の順序が
逆になっていても自然ではないですか?たとえば以下のように。

def mpop( time )
  ret = []
  time.times { ret.push pop }
  ret
end

ようするに、pop( 3 ) の 3 というのがどういう意味の 3 なのか、
何が返ってくるのか、想像がつきにくいんです。それはなぜかというと
Rubyのライブラリには他に引数をそういうふうに使うメソッドがなく、
かつ、pop という名前とそぐわないからです。

Ruby のよいところのひとつとして、期待した機能が期待した名前で
得られるということがあると思います。これは、これまでのRubyの
範疇うんぬんではなく、Rubyの「教条」だと考えます。
欲しい機能が得られればいいのではなく、それに適した場所と名前が
必要なんです。そしてどのようなものが適するのかと言えば、それは
UNIX や C 言語や Perl やシェルなどの伝統や、Ruby の慣習、自然言語などに
基づいて「自然である」ものがそれに値するわけです。
しかし、今回の pop(3) はそれらのどの根拠からも支持を与えることが
できないと考えますので、導入には賛成しません。


ただ、もしこれが pop でなく last( 3 ) だったらまだ納得できるのですが。
(積極的に賛成はしない。)


> From: nobu.nakada / nifty.ne.jp
> >   array.pop は現状は最後尾にあったオブジェクトそのものを返します
> > が、array.pop(N) だとたぶん Array になると思います。そこのギャッ
> > プはどうしたもんでしょうか。
> 
> こちらに関しては、ギャフン!です。確かに、ギャップがあります。ただ、
> 同じくなかださんの、

実はぼくはなかださんの意見についてはあまり同感でないんです。
というのは、Array#[] も引数によって配列を返したりそうでなかったり
するからです。
ただ、pop(1) と pop() (省略時には 1 ですよね) の返り値が違うのは
たしかに気になりますね。
-------------------------------------------------------------------
あおきみねろう