K.Maebashi's BBS

ご自由に書き込んでください。雑談も可。
テスト書き込みの類はテスト用掲示板にどうぞ

[日付順表示] [日付順インデックス] [スレッド順インデックス]

新規投稿 | 開設者ホームページへ戻る | ヘルプ

[723] Re:オブジェクト指向「初」入門
投稿者:CES
2007/02/20 02:13:25

要点抽出。 >議論が空転している気がするんです。整理しましょう。 >まず(常識的かどうかは置いておくとして)「オブジェクトの is-a」で大まかな関係を作るというのは、どちらも同じことを言ってますね。 >kit さんは、その上で「LSP に照らして破綻したら、継承関係にはしない」と言う。 >俺は「LSP に照らすまでも無く、継承関係にしてはいけない場合がある」と言う。 >結局、どの段階で、最初の「is-a」が間違えていたかを判断するのかが違うだけで、大差ないでしょう。 >問題は「LSP に照らさないと、間違いかどうか判断できないケースはあるのか」だと思うんですよ。 >そのへんどうです? 1:オブジェクトが is-a であると仮定した。 2:LSP に照らし合わせたらうまくいった。 3:継承関係を結んだ。 これに異論を差し挟む余地は無いでしょう。 問題は、そうでないケースですね。 kit さんの場合 1:オブジェクトが is-a であると仮定した。 2:LSP に照らし合わせたらうまく行かなかった。 3:オブジェクトは is-a なんだけど継承関係にはしない。 俺の場合 1:オブジェクトが is-a であると仮定した。 2:LSP に照らし合わせたらうまく行かなかった。   (照らし合わせるまでも無く判断できる場合もある) 3:実はオブジェクトは is-a じゃなかったんだ。だから継承関係にはしない。 結局、違うのは、過程3の前半だけなんですよね。 だから、[712] ではこう書いているんです。 > is-a と LSP をどうしても両立させられない例を一つでも出していただけませんか。 俺の方法ではうまく行かないケース、つまり 「どこからどう見ても疑念を挟む余地無く is-a なんだけど、LSP に照らし合わせたらうまく行かないから、LSP を優先するケース」 があるのか、ということなんです。俺が度々言う「is-a と LSP が矛盾するケース」ってのはこういうことなんです。 「常識的な is-a と LSP が矛盾するケース」ではないですよ。「どこからどう見ても」ってのは、「あらゆる非常識な関係も考慮に入れて」ってことです。 こういう場合があると言えますか? 俺は「あらゆる角度から見る」ことが不可能であるため、そのようなケースは存在しないと思うのですが。
[この投稿を含むスレッドを表示] [この投稿を削除]
[722] Re:オブジェクト指向「初」入門
投稿者:CES
2007/02/20 02:13:25

>「is-a の意味は常識的なものとは限らない。場合によって、適切なもの >を選べ」というのは、実のところ、まったく意味のない言明です。なぜな >ら、CES さんは、is-a という関係が具体的にどうあるべきかを一切述べ >ていないからです。 どう言えばいいですかねぇ。 「クラスはオブジェクトの集合である」と考えた時、「ある集合Aと、その部分集合Bがあったら、B is-a Aである」でいいですか? もっとも、「どのような基準に従ってオブジェクトを集合に分類するか」には、結局、唯一解がないのは同じことなんですが。 >我々は、設計段階で、どういう継承関係にすればうまくいくかを知りたい >ため、設計原則を求めているわけです。 そんな話は今初めて聞きましたがねぇ… >たとえば、「常識的に考えた is-a 関係に対して、継承を当てはめれば良 >い」というのは、正方形と長方形の例のようにうまくいかない例外もあり >ますが、かなり使える原則です。 それでいいと思いますよ。 我々の社会常識が「大多数にとって都合がいい見方」であるように「多くの場合に都合がいいケース」はあってもおかしくありません。 ただし、それがたまたま成り立たないからといって、それだけを槍玉に挙げないでいただきたい。 ちなみに、件の文献の、長方形と正方形の関係の誤りは、LSP を持ち出すまでも無く「間違いだ」と判断できるものです(まぁ、LSP を考えてみて判断しても構わないのですが。別にそれでも is-a と LSP の矛盾は指摘できませんから)。 オブジェクトの is-a よりも LSP を優先すべきだという根拠にはなりません。 >また、「LSPに従うオブジェクト同士に対して、継承を当てはめれば良い」 >というのは、常に利用可能な原則です。 >ところが、CES さんは、「継承してうまく動くような関係が is-a である。 >したがって、継承してうまく動くような関係に対して、継承を当てはめれ >ば良い」と言ってるに過ぎないわけです。これは見て分かるようにトート >ロジーに過ぎず、設計の指針としては、全く役に立たないわけです。 違います。 「どういう関係が is-a であるか」については言及していません。 「is-a である関係が、LSP を満たすように作れ」と言っているのです。 件の文献も言っているではありませんか。 > あるところで、何か、置き換えが必要なところがあるとする。 > 型Sのオブジェクトo1 と、型Tのオブジェクトo2があって、 > プログラムが既に型Tのオブジェクトを使うように構築されているとする。 > o1がo2で置き換えられても、SがTのサブタイプであるならば、 > Pは同じ振る舞いをするべきである。 「サブタイプならば同じ振る舞いをすべきである」と書かれています。 「同じ振る舞いをするならサブタイプである」とは書かれていません。 結局、この文献は「どんなものを継承関係にすべきか」という指針には、一言も言及していないのです(「どんなものを継承関係にしてはいけないか」は、LSP を用いて言及しています。しかし、それは LSP を用いなくても指摘可能であることは度々述べています)。 >> 全く同じ属性と振る舞いを持つクラスは、同じクラスだと考えるべきです。 >> これは、「LSP が成り立つならば is-a も成り立つ」と言い換えることもできます。 > >一つ目の文章は正しいと思います。 >しかし、一つ目の文章から二つ目の文章を導くことはできません。 突っ込まれるかなーと思っていました。 ちょっと拡大しましょう。 全く同じ属性と振る舞いを持つクラスは、同じクラスだと考えるべきです。 そして、あるクラスと互換性のある属性と振る舞いを持つクラスは、そのクラスと何らかの関係があるクラスと見るべきです。 逆転させて言えば「関係の無いクラスに、互換性のある属性と振る舞いを持たせるべきではありません=継承関係にすべきではありません=LSP が成立してはいけません」となるのではないでしょうか。 >なお、Martin 氏の文章を読み返してみて、私がこれまで説明で使って >用語法が、Martin 氏の用語法と異なっているのに気づきました。 >私がこれまで >1. オブジェクトの振舞いに関する is-a >2. オブジェクト自身に関する is-a >という二つの言葉で区別してきたことを、Martin 氏は >1. 振舞いの is-a == オブジェクト(CamelCase にした名詞)の is-a >2. 物(子文字で始まる名詞)の is-a >と呼んでいますね。 >まあ、独立に読んでいれば意味は通じると思いますが、併せて読むと >混乱するのであまり良くありませんね。どうも申し訳ない。 小文字の is-a ってどこで使ってますか? ひょっとして原文? 提示された日本語版には見当たらないのですが… >設計手順としては、まず常識的な is-a を使って継承関係を考えてみた >上で、それを LSP で検証すればいいんですよ。検証に通らない場合には、 >たとえ常識的なis-a 関係であっても、継承関係にはしません。検証に通 >れば、もちろん継承関係にします。 >簡単でしょう? >本来無関係のオブジェクト同士うんぬんなどと悩む必要はありません。 議論が空転している気がするんです。整理しましょう。 まず(常識的かどうかは置いておくとして)「オブジェクトの is-a」で大まかな関係を作るというのは、どちらも同じことを言ってますね。 kit さんは、その上で「LSP に照らして破綻したら、継承関係にはしない」と言う。 俺は「LSP に照らすまでも無く、継承関係にしてはいけない場合がある」と言う。 結局、どの段階で、最初の「is-a」が間違えていたかを判断するのかが違うだけで、大差ないでしょう。 問題は「LSP に照らさないと、間違いかどうか判断できないケースはあるのか」だと思うんですよ。 そのへんどうです? >なお、もし本来無関係だと思っていたオブジェクト同士にLSPが成り立つ >のであれば、実はそのオブジェクト同士は、たぶん無関係じゃないんですよ。 本当に無関係でないのなら構いませんが、「たぶん」というのはいただけません。 実のところ、「本当に無関係か」を判断することはできません。どうにかして関係を見出すことはできます。 しかし、その関係は、設計者の意図した関係で無い可能性が大きいでしょう。 そのような関係に、LSP を成り立たせることは、メリットがあるとは思えません。 >しかし、通常のソフトウェア開発で、そういう発見を必要とすることは >あまりありませんから、無関係だと思うオブジェクト同士について、 >いちいちLSPを検証してみる必要はありません。 LSP は「たまたま成り立っちゃう」ものではなくて「意図して成り立たせる」ものであると考えます。 であるならば、先のようなデメリットを考えた時に「意図して崩す」ことも、当然ありえる話です。 それをせず、「たまたま成り立っちゃう」のを放置しておくのは、バグの温床になりませんか。
[この投稿を含むスレッドを表示] [この投稿を削除]
[721] Re:プログラミングの入門用言語
投稿者:(ぱ)
2007/02/20 02:13:25

>processingのmouseXの代わりに、get_mouse_x()という関数を用意しました。 get_mouse_x()も、ウインドウ内の座標を返すのですから、ウインドウごとにしないと まずいですね。このへん考えるべきことはいろいろありそうですが、まあ方向性として。 ついでに補足しますけど、私は、HSPに関しては「もう21世紀なのにこの言語はないだろう」 というまつもとゆきひろさんの意見に賛成します。 http://www.rubyist.net/~matz/20040827.html#p01 こちらにある、「「初心者(だけ)のための言語」ってのは駄目だと思っている」 という意見にも賛成します。 http://www.rubyist.net/~matz/20040925.html#p02 ただ、 | 初心者と言えども言語の全ての機能を一度にマスターする必要はない。 | よって言語が初心者向きでない機能を持つことは、あまり問題にならない。 これにはいまいち賛同しかねるんですよねえ。サンプルで見かけるプログラムで 「初心者向きでない機能」が使われていると、おっかなく思う初心者は大勢いそうです。 crowbarはどうでしょうか。クロージャは、[717]を見るとわかるように、言語の作者の 私でさえ使いこなせていない。とほほほほ。 しかし、それはそれとして、オブジェクトの概念自体は必要だと思うし、初心者に わからないものでもないと思います。 w = open_window(800, 500); と書いてウインドウが開くのなら、そしてopen_window()を実行するたびに 何個でもウインドウが開けるのなら、そのウインドウに丸を描くのに w.fill_circle(x, y, 10); と書かなければならないのは、いくら相手が初心者でも、自明じゃないのかなあ。 別にfill_circle(w, x, y, 10); でもいいけど。 んで、シューティングゲームを作るのに敵をたくさん出したければ、自分でクラスっぽい ものを作らざるを得ないし、そのmove()メソッドをオーバーライドすることで、 ポリモルフィズムも自然に利用できるんじゃないかと思うんですけど、どうでしょうか。
[この投稿を含むスレッドを表示] [この投稿を削除]
[720] Re:プログラミングの入門用言語
投稿者:(ぱ)
2007/02/20 02:13:25

> if (get_mouse_x() < x) { > x++; あ、条件式逆だった…
[この投稿を含むスレッドを表示] [この投稿を削除]
[719] Re:プログラミングの入門用言語
投稿者:(ぱ)
2007/02/20 02:13:25

>てなことを書かれておられましたが、Processing(http://processing.org/)はどう >でしょうか? 情報ありがとうございます。ちょっと見てみました。 ExamplesのStructureを一通り見ただけですが、 ・やっぱりイベントドリブンなのか。(mouseXという謎の変数が出てきてますが) ・ウインドウをふたつ開くことはできるのか? あたりが感想(と疑問)です。イベントドリブンは、処理が分断されるので、 ゲームとかを作りたい人にはわかりにくいのではないか、と私は思っています。 >これは、言語はJavaそのものですが、IDEによって、クラス定義などの >初心者にとって面倒そうな部分をうまく隠蔽してあって、単なる手続き型言語のように >使えます。これなら初心者にとっても比較的とっつきやすいんじゃないかと思います。 うーん。とっつきやすいかとは思いますが、結構用途が限定された言語にも見えます。 draw()は言語仕様に組み込まれているんでしょうか? たとえば現在私が作っているcrowbarをベースに、ライブラリ関数を追加して、 以下のようなプログラムは作れるようになるのではないでしょうか。 processingのmouseXの代わりに、get_mouse_x()という関数を用意しました。 # マウスを追いかけるボールのプログラム。 w = open_window(800, 500); # 引数はウインドウのサイズ x = 0; y = 0; prev_x = x; prev_y = y; for (;;) { # 直前のボールの消去 w.set_color(BLACK); w.fill_circle(prev_x, prev_y, 10); # 中心のx, y, 半径 if (get_mouse_x() < x) { x++; } elsif (get_mouse_x() > x) { x--; } elsif (get_mouse_y() < y) { y++; } elsif (get_mouse_y() > y) { y--; } w.set_color(WHITE); w.fill_circle(x, y, 10); prev_x = x; prev_y = y; wait(100); # 100msec待ちつつイベントを拾う。最後のイベントで上書き。 } 私が件の雑記で書きたかったのは、「ベーシックマガジン向けプログラミング言語」が あったらいいなあ、ということなんですけど、これぐらい書けたら、汎用性を保ちつつ、 それなりに達成してないでしょうかね? もちろん、fill_circle()だけでなく、 イメージを指定された場所に表示する関数とかも必要ですけれど。 wait()あたりの一等地の名前を使うことについては、名前空間で対処するとして。 でも、processingは、Javaアプレットとして実行できるのはいいと思います。 ゲーム作ったらやっぱり友達に自慢したいと思うので。
[この投稿を含むスレッドを表示] [この投稿を削除]
[718] Re:正規表現関連の関数の質問
投稿者:(ぱ)
2007/02/20 02:13:25

>ご教授ありがとうございます。 >GCに少し興味を持ったのですが、お勧めのサイトなどありますか? 私のお勧めサイトというと、ここの参考URLですかねえ。 http://kmaebashi.com/programmer/devlang/array.html あと、紙ものの資料では、大昔の情報処理学会誌に「ごみ集めの基礎と最近の動向」 というのがあって、とてもよかったのですが、現在では入手不能だと思います。 私はというと会社で該当の情報処理学会誌が捨てられる直前に気が付いて、 コピーしておいたのですが、その後数回の引越しで現在行方不明です…だめだめ。 >以下の本を購入しようと思ったのですが、(ぱ)さんは読んだことありますか? > >http://www.amazon.co.jp/exec/obidos/ASIN/0471941484/qid=1136566955/sr=1-1/ref=sr_1_10_1/250-0589622-2949018 すみません、読んでないです。
[この投稿を含むスレッドを表示] [この投稿を削除]
[717] Re:イテレータ(とCPS)
投稿者:(ぱ)
2007/02/20 02:13:25

土曜はとあるイベントにて大阪で飲んだくれておりまして、ずいぶん返事が遅くなりまして すみません。 それプラス、私自身、クロージャに慣れておらず継続もわかっておらず、今回のNykRさんの サンプルはずいぶん荷が重いものでした。とはいえ放り出していては勉強にならんので、 私なりに解釈してみました。間違い等ありましたらご指摘ください。 さて、単に木をイテレートするだけなら、 tree.each(closure(item) { # このitemについてなんかする }); という形のeachメソッドをツリーに装備するのは簡単で、ツリーの中で再帰して、 要素ごとに、渡されたクロージャを呼び出せばよいのですが、こういう形式(内部イテレータ) だと、あるツリーについて、「ここまで走査した」という状態を保存しておけないのが 問題になるわけですよね。その点、NykRさんの提示されたイテレータでは、こういう 書き方も許される。 # ふたつのイテレータでツリーを並列に走査する。 for (ite1 = iterator_GoF(foreachTree_CPS(tr)), ite2 = iterator_GoF(foreachTree_CPS(tr)); !ite1.isDone() && !ite2.isDone(); ite1.next(), ite2.next()) { print(" " + ite1.currentItem()); print(" " + ite2.currentItem()); } これができるのが外部イテレータの強みです。 Cなどで、再帰を使ってツリーを辿る場合、「ここまで走査した」という情報がスタック上に ありますから、スタックが1本しかない以上、ふたつのイテレータを並行して使うことは できないわけです(片方が片方の繰り返しに完全に含まれるなら可能)。 tr = {13,{5,{3,{},{}},{13,{12,{},{}},{}}},{16,{16,{15,{},{}},{}},{17,{},{}}}}; この例では、nodeの[0]にそのノードの値が、[1]に左の子が、[2]に右の子が 入っていますから、再帰で間順で走査するなら以下のようになります。 function internalIterate(node) { if (node.size() == 0) { } else { internalIterate(node[1]); # ...(a) print(" " + node[0]); # とりあえず表示しておく internalIterate(node[2]); } } (a)の箇所で左の子について再帰呼び出しを行い、それが戻ってきてから続きの処理を やっています。「戻ってきてから続きの処理をやる」ことができるのが再帰の嬉しい ところですが、それを期待していては、外部イテレータは作れない。 そこで、「戻ってきてから続きの処理をする」のではなく、「戻ってきてから やってほしい続きの処理をクロージャとして渡す」とすれば、関数から戻ってこなくても よくなる。その変換をしたのがこれ。 function internalIterate(node, cont) { if (node.size() == 0) { cont(); } else { internalIterate(node[1], closure () { # 続きをクロージャで渡す。 print(" " + node[0]); internalIterate(node[2], cont); }); } } # 第2引数には「処理の続きを渡す」のだから、最後に「おしまい」と表示される。 internalIterate(tr, closure() {print("おしまい");}); さて、この状態では、要素ごとに行う処理がprint()に固定されていますから 汎用性がありません。では引数でなにか関数を渡せば、とこうしても、 internalIterate(node[1], closure () { # 続きをクロージャで渡す。 proc(node[0]); # 処理を引数procで渡す internalIterate(node[2], cont); }); これでは内部イテレータと同じですから意味がありません。 そこで、このproc()にも、続きの処理をクロージャで渡してやることにします。 function foreachTree_CPS(proc) { return closure internalIterate(node, cont) { if (node.size() == 0) { cont(); } else { internalIterate(node[1], closure () { proc(node[0], closure() { internalIterate(node[2], cont); }); }); } }; } 外部イテレータの場合、このprocの第2引数をnext()の際に呼び出せばよいわけですね。 なぜならそれが「処理の続き」だから。 イテレータはこうなります。 function iterator_GoF(foreach_CPS, collection) { this = new_object(); this.first = closure () { this.isDone = closure () { return false; }; foreach_CPS(closure (item, cont) { this.currentItem = closure () { return item; }; this.next = closure () { cont(); }; })(collection, closure(){ # ..(b) this.isDone = closure () { return true; }; }); }; this.first(); return this; } イテレータのfirst()が呼び出されたときに、foreach_CPS()を呼び出して、 internalIetrate()を取得し、それに対しcollectionとクロージャを渡しています(b)。 これがつまり元のソースの internalIterate(tr, closure() {print("おしまい");}); に相当するわけですが、「おしまい」と表示する代わりに、isDoneについて、 trueを返すよう関数を差し替えています。 んで、internalIterate()に渡しているproc()では、 this.currentItem = closure () { return item; }; this.next = closure () { cont(); }; currentItemを設定後、nextに対し、contすなわち「処理の続き」をセットしています。 これで、次に利用者がnext()を呼び出したとき、internalIteratorの続きが実行される、と。 NykRさんのソースとは微妙に変わっていますが、こんな解釈でよいでしょうか? # いやあ、実に勉強になりました。
[この投稿を含むスレッドを表示] [この投稿を削除]
[716] プログラミングの入門用言語
投稿者:みずしま
2007/02/20 02:13:25

こんにちは。 大分前の雑記「プログラミングの入門用言語(2003/5/1)」の最後の方で、 > C, Javaライクな文法で、こういう方向性の言語って、現在あるんでしょうか。 てなことを書かれておられましたが、Processing(http://processing.org/)はどう でしょうか?これは、言語はJavaそのものですが、IDEによって、クラス定義などの 初心者にとって面倒そうな部分をうまく隠蔽してあって、単なる手続き型言語のように 使えます。これなら初心者にとっても比較的とっつきやすいんじゃないかと思います。
[この投稿を含むスレッドを表示] [この投稿を削除]
[715] Re:オブジェクト指向「初」入門
投稿者:kit1
2007/02/20 02:13:25

> 唯一の正解は無いでしょう。場合によって、適切なものを選べば、それ > が正解です。 これが CES さんのやり方の問題なんです。 「is-a の意味は常識的なものとは限らない。場合によって、適切なもの を選べ」というのは、実のところ、まったく意味のない言明です。なぜな ら、CES さんは、is-a という関係が具体的にどうあるべきかを一切述べ ていないからです。 我々は、設計段階で、どういう継承関係にすればうまくいくかを知りたい ため、設計原則を求めているわけです。 たとえば、「常識的に考えた is-a 関係に対して、継承を当てはめれば良 い」というのは、正方形と長方形の例のようにうまくいかない例外もあり ますが、かなり使える原則です。 また、「LSPに従うオブジェクト同士に対して、継承を当てはめれば良い」 というのは、常に利用可能な原則です。 ところが、CES さんは、「継承してうまく動くような関係が is-a である。 したがって、継承してうまく動くような関係に対して、継承を当てはめれ ば良い」と言ってるに過ぎないわけです。これは見て分かるようにトート ロジーに過ぎず、設計の指針としては、全く役に立たないわけです。 結局、CES さんの主張は、LSP どころか、(常にうまくとは限らない)常識的 な is-a 関係による継承関係の設計よりも、さらに劣るものにしか見えません。 また、CES さんは下記のように書いていますが、これはそもそも意味不明です。 > 全く同じ属性と振る舞いを持つクラスは、同じクラスだと考えるべきです。 > これは、「LSP が成り立つならば is-a も成り立つ」と言い換えることもできます。 この二つの文章のうち、一つ目の文章は、クラスとクラスの等価関係に 関する文章です。 二つ目の文章は、クラスとクラスの包含関係に関する文章です。 等価関係に関する文章と、包含関係に関する文章が、同一の意味である という発想は、まったくもって非論理的だと私は思います。 なぜ、この二つの文章が等価だと発想されたのか、残念ながら、私には 理解できません。 一つ目の文章は正しいと思います。 しかし、一つ目の文章から二つ目の文章を導くことはできません。 なお、Martin 氏の文章を読み返してみて、私がこれまで説明で使って 用語法が、Martin 氏の用語法と異なっているのに気づきました。 私がこれまで 1. オブジェクトの振舞いに関する is-a 2. オブジェクト自身に関する is-a という二つの言葉で区別してきたことを、Martin 氏は 1. 振舞いの is-a == オブジェクト(CamelCase にした名詞)の is-a 2. 物(子文字で始まる名詞)の is-a と呼んでいますね。 まあ、独立に読んでいれば意味は通じると思いますが、併せて読むと 混乱するのであまり良くありませんね。どうも申し訳ない。 > オブジェクト指向の第一目的は「わかりやすくすること」であると(今 > は)思っています > 本来、無関係のオブジェクト同士を継承関係にするのがわかりやすいで > すか? 私が欲しいのは良い設計指針であって、オブジェクト指向の第一目的は 何かという問いの答は実はどうでもいいんですが(なぜなら、人によって 答がそれぞれ違うので、そんなことの統一見解を決めても実益がないから です)、それは置いておいて、かなり誤解されていると思います。 設計手順としては、まず常識的な is-a を使って継承関係を考えてみた 上で、それを LSP で検証すればいいんですよ。検証に通らない場合には、 たとえ常識的なis-a 関係であっても、継承関係にはしません。検証に通 れば、もちろん継承関係にします。 簡単でしょう? 本来無関係のオブジェクト同士うんぬんなどと悩む必要はありません。 なお、もし本来無関係だと思っていたオブジェクト同士にLSPが成り立つ のであれば、実はそのオブジェクト同士は、たぶん無関係じゃないんですよ。 しかし、通常のソフトウェア開発で、そういう発見を必要とすることは あまりありませんから、無関係だと思うオブジェクト同士について、 いちいちLSPを検証してみる必要はありません。
[この投稿を含むスレッドを表示] [この投稿を削除]
[714] Re:オブジェクト指向「初」入門
投稿者:CES
2007/02/20 02:13:25

>> 正方形は長方形のサブクラスであるとしても成り立つ場合は既に >> 書きました。is-a と LSP をどうしても両立させられない例を一 >> つでも出していただけませんか。 > >CES さんご自身が、正方形を長方形のサブクラスに『しない』方が >いい場合があると、[709]で認めているじゃないですか。常識的に >は、正方形 is-a 長方形ですから、当然、正方形は長方形のサブク >ラスとして継承関係を結ぶべきですし、Robert C. Martin 氏のエッ >セイでも、その方法をまず勧めています。しかし、そうしない方が >いい場合もあるわけですから、オブジェクト自身の is-a は必ずし >も判断基準に使えません。 俺はそういうことを言っているのではありません。 LSP なんか関係なく、「正方形 is a 長方形にならない場合もある」ということです。 その場合は、正方形が長方形でないのは何ら問題ではないのですから、「is a と LSP が矛盾する」とは言えません。 「常識的には」と書かれていますが、それが間違いの元です。 世の中に客観的な視点など存在しないというのは以前に書きました。 正方形 is a 長方形というのは、客観的に見えますが、「数学的に都合がいい見方」に過ぎません。常識と言うのも、大多数に都合がいい見方に過ぎません。真理ではないのです。 件の文献の勘違いは「長方形」の定義が曖昧なことです。 「正方形 is a 長方形」は数学に都合がいい見方、「縦と横の長さが独立していなければならない」は、このプログラムに都合がいい見方であって、数学に都合がいい見方ではない。 どちらの見方が間違いというのではありません。どちらも場合によっては正解なのですが、その「場合」が統一されていないのが間違いなのです。 このプログラムが「長方形」に何を期待しているのかが明確になっていないのがいけないのです。 大本の問題は、何のためのプログラムなのかを明確にしないまま、コードだけ示して煙に巻こうとしたことですね。 仮に、「常識的に考えて、正方形 is a 長方形なのだから、当然サブクラスにすべきだ」というのが正しいとして、では「りんご」は何のサブクラスにすべきですか? 常識的に考えて「果物」のサブクラス? それとも「バラ科の植物」? スーパーマーケットのシステム開発だったら「商品」のサブクラスにすべきではないですか? 唯一の正解は無いでしょう。場合によって、適切なものを選べば、それが正解です。 「正方形 is not a 長方形」にすべき場合は確かにあるでしょう。 それは、LSP に都合が悪いからそうなったのではなく、作りたいソフトの目的に合致しないからそうなったのです。 「is-a は LSP と両立できなかったので泣く泣く諦めた」のではなく「そもそも両立する必要が無かった」のです。 現在は is-a が推奨されています。百歩譲って has-a もまぁ認めるとしましょう。 問題は、「LSP さえ成り立つのなら、まったく無関係のクラス同士を継承関係にしていいのか」ということです。 (どういう設計になるのか知りませんが)LSP が成り立つのなら、車を鳥のサブクラスにするのもアリなのですか? オブジェクト指向の第一目的は「わかりやすくすること」であると(今は)思っています(以前、この掲示板に長期滞在したときは「バグを減らすこと」だと思っていました。考えは変わる可能性があります)。 本来、無関係のオブジェクト同士を継承関係にするのがわかりやすいですか? >> よろしければ、おすすめの本を紹介していただけないでしょうか。 > >今回紹介したエッセイ自身を含んだ、Robert C. Martin 氏の >「アジャイルソフトウェア開発の奥義」はいかがでしょう? >http://hamasyou.com/archives/System/aeeoeeueaconoeoeass.php >http://hamasyou.com/archives/System/aeeoeeueaoeeoeaass.php >に長めの紹介があります。 持ってますけど読んでませんでした。 OCP や LSP の評判を聞いて買ったのですが、買ったきりで。 そのうち読むことにしましょう。 こちらにも一点、考え直したことがあったので、最後にそれについて言及します。 オブジェクト指向において、オブジェクトを特徴付けるものは、属性と振る舞いです(プロパティとメソッドとか、メンバ変数とメンバ関数とか、言語によって呼び方はいろいろあるでしょうが)。 つまり、全く同じ属性と振る舞いを持つクラスは、同じクラスだと考えるべきです。 これは、「LSP が成り立つならば is-a も成り立つ」と言い換えることもできます。 しかし、逆に言えば「違うクラスに、全く同じ属性と振る舞いを持たせるべきではない」「関係の無いクラスに、LSP を成り立たせるべきではない」とも言えます。 そう考えれば、「is-a が成り立つのならば LSP が成り立つのは当然」「is-a が成り立たないのならば、LSP も成り立たせるべきではない」のではないでしょうか。
[この投稿を含むスレッドを表示] [この投稿を削除]
[713] Re:オブジェクト指向「初」入門
投稿者:kit
2007/02/20 02:13:25

> 俺がプログラミングを初めてまだ7年、オブジェクト指向が分かっ > てきたのはここ1、2年ほどのことですのでね。 意外と長いんですね。 私はプログラミング歴は24年ぐらい、オブジェクト指向に関しては プログラミング言語C++第1版の和訳や、Smalltalk-80のオレンジブッ クの和訳が出た頃からですから、20年弱くらいですかね。 その前に、先輩から米国byte誌のSmalltalk-80特集号を見せてもらっ たこともありましたが。 > 正方形は長方形のサブクラスであるとしても成り立つ場合は既に > 書きました。is-a と LSP をどうしても両立させられない例を一 > つでも出していただけませんか。 CES さんご自身が、正方形を長方形のサブクラスに『しない』方が いい場合があると、[709]で認めているじゃないですか。常識的に は、正方形 is-a 長方形ですから、当然、正方形は長方形のサブク ラスとして継承関係を結ぶべきですし、Robert C. Martin 氏のエッ セイでも、その方法をまず勧めています。しかし、そうしない方が いい場合もあるわけですから、オブジェクト自身の is-a は必ずし も判断基準に使えません。Robert C. Martin 氏が、エッセイの 「本当の問題」「何が悪いのか?」「Design By Contract」のとこ ろで説明しているのは、そういうことです。オブジェクト自身の is-a で考えるのは誤りであり、オブジェクトの振舞いに関する is-a で考えないといけないのです。前に出た円と楕円の話も同じ です。 このあたりは、和訳を読むよりも、原文 http://www.objectmentor.com/resources/articles/lsp.pdf の "What Went Wrong" のところを読んだ方が、むしろ理解しやす いかもしれません、「a Square object is definitely not a Rectangle object.」と書いてありますから。和訳の「決定的な違 いがあるのです。」という表現では、残念ながら、ここの is-a と is-not-a のニュアンスが消えてしまっています。私は原文の方を 先に読んだので、ここのところが非常に強く印象に残りました。 なぜ、オブジェクト自身の is-a 関係が継承関係の設計に使えない 場合があるのか、そのことを説明しているのが LSP です (ただし、 正方形や円のケースでは、LSP 以外にも、メモリ効率という別の理 由もあります)。継承関係の設計において、オブジェクト自身の is-a 関係は使えない場合がある弱い判断基準なわけですが、LSP は常に使える絶対的な判断基準なわけです。実はLSP(オブジェクト の振舞いに関する is-a) の方が、オブジェクト自身のis-aよりも むしろ本質的な原則なわけです。 現在では、has-a の関係は、継承よりも委譲を使うのが良い解だと されていますが、昔は has-a でも継承を使うことが良くありまし た。今でもたまにそういうプログラムを見ることがあると思います。 これも、アプリケーションを限定すれば、has-a で LSP を満たす 場合があることから来ているわけです。has-a で継承して問題のな いアプリケーションは、オブジェクト自身の is-a 関係で継承して 問題のないアプリケーションよりも、はるかに少ないので廃れてき ているわけですが。 has-a が廃れてきているのに is-a がそうではないのは、オブジェ クト自身についてis-a の関係が成り立つ場合のほとんど(ただし全 てではない)で、同時に、オブジェクトの振舞いに関する is-a 関 係(すなわちLSP)も満たすからです。 > よろしければ、おすすめの本を紹介していただけないでしょうか。 今回紹介したエッセイ自身を含んだ、Robert C. Martin 氏の 「アジャイルソフトウェア開発の奥義」はいかがでしょう? http://hamasyou.com/archives/System/aeeoeeueaconoeoeass.php http://hamasyou.com/archives/System/aeeoeeueaoeeoeaass.php に長めの紹介があります。 ただ、和訳だと上述したように、ニュアンスが失われる部分はある でしょうね。また、data中心でモデリングした結果の扱いについて は、この本の主張に異論がある場合もあるでしょう。この本では常 にOOAを優先すべしという方向ですが、data中心でモデリングした 結果の方を優先した方がいい場合もある筈です。 私は、Robert C. Martin 氏自身の名前は、亡くなられた石井勝さ んのページで知りました。 http://www.objectclub.jp/community/memorial/homepage3.nifty.com/masarl/article/oo-principles.html 石井さんがこのページを書かれたのは1999年ですね。 今ではLSPは有名な原則ですが、これが広まったのは Liskov 氏自 身の功績というよりは、Robert C. Martin 氏の功績の方が大きい と思います。私がこの法則を知ったのも今回のエッセイを読んだ からですし、google で The Liskov Substitution Principle を 検索して最初に出てくるのも、このエッセイですし。
[この投稿を含むスレッドを表示] [この投稿を削除]
[712] Re:オブジェクト指向「初」入門
投稿者:CES
2007/02/20 02:13:25

>もちろん、「オブジェクトが is-a であるか」と「オブジェクトの『振舞い』 >が is-a であるか」の二つが矛盾しない場合は問題ありません。しかし、矛盾 >した場合に優先すべきなのは、LSP の方です。 >しかしどうやら CES さんは、矛盾した場合、LSP よりも前者を優先すべきだ >とお考えのようですね。 どちらかと言えばそうかもしれません。 LSP を守ることも重要ですが、まずはオブジェクトの is-a を優先し、その上で LSP が守られるように都合をつけるべきだと思います(結果的には、両方守るべきです)。 >Robert C. Martin がこの文書を発表してから、もう10年近く、Liskov から >数えれば、もう15年以上経っており、私は既に常識的な知識だと思っていまし >たが、驚かれたということは、どうやらそうではなかったようですね。 俺がプログラミングを初めてまだ7年、オブジェクト指向が分かってきたのはここ1、2年ほどのことですのでね。 >そもそも LSP や、この文書が有名になったのは、「オブジェクトが is-a である >か」という判定条件が成り立たない場合があるということを、はっきりと示 >しているからだと思うのですが。 >is-a だけで済むのであれば、わざわざ原則として掲げる必要も、文書で解説 >する必要もありません。 >失礼ながら、御自身の考えを他人の掲示版で開陳されるよりも先に、もう少し >オブジェクト指向関係の良書で勉強された方が良いのではないでしょうか。 よろしければ、おすすめの本を紹介していただけないでしょうか。 >少なくともこれまでのところ、私は CES さんの意見に説得される可能性は >ありません。CES さんの考えを証明する具体的な例を一切目にしていません >ので。前の投稿でも書きましたが、私は机上の空論は嫌いですし、具体的な >例のない議論に説得されることは決してありません。 >(ぱ)さんも同様ではないかと思います。 その言葉はそっくりお返しします。 正方形は長方形のサブクラスであるとしても成り立つ場合は既に書きました。 is-a と LSP をどうしても両立させられない例を一つでも出していただけませんか。
[この投稿を含むスレッドを表示] [この投稿を削除]
[711] Re:オブジェクト指向「初」入門
投稿者:kit
2007/02/20 02:13:25

> いや恐れ入った。 > 「オブジェクトが is-a であるか」は守られなくてもいいのか。 その通りです。 もちろん、「オブジェクトが is-a であるか」と「オブジェクトの『振舞い』 が is-a であるか」の二つが矛盾しない場合は問題ありません。しかし、矛盾 した場合に優先すべきなのは、LSP の方です。 しかしどうやら CES さんは、矛盾した場合、LSP よりも前者を優先すべきだ とお考えのようですね。 Robert C. Martin がこの文書を発表してから、もう10年近く、Liskov から 数えれば、もう15年以上経っており、私は既に常識的な知識だと思っていまし たが、驚かれたということは、どうやらそうではなかったようですね。そも そも LSP や、この文書が有名になったのは、「オブジェクトが is-a である か」という判定条件が成り立たない場合があるということを、はっきりと示 しているからだと思うのですが。 is-a だけで済むのであれば、わざわざ原則として掲げる必要も、文書で解説 する必要もありません。 失礼ながら、御自身の考えを他人の掲示版で開陳されるよりも先に、もう少し オブジェクト指向関係の良書で勉強された方が良いのではないでしょうか。 あるいは、もし、「オブジェクトが is-a であるか」は LSP よりも優先する という考えをどうしても主張されたいのであれば、掲示版はそもそも不向きな メディアだと思います。 なぜ LSP よりも優先するのかを、適切な例を含めた上で、ご自分の Web ペー ジで解説された方が良いと思いますよ。 もし、Barbara Liskov や Robert C. Martin も優れた考えであるのなら、 独立した文書として公開する価値が十分あると思います。 少なくともこれまでのところ、私は CES さんの意見に説得される可能性は ありません。CES さんの考えを証明する具体的な例を一切目にしていません ので。前の投稿でも書きましたが、私は机上の空論は嫌いですし、具体的な 例のない議論に説得されることは決してありません。 (ぱ)さんも同様ではないかと思います。 > 「正方形は長方形のサブクラスであるか?」という質問に、唯一の正解はあり > ません。答えは、数学に厳密である必要がある場合には YES 、そうでない場 > 合には NO になります。 あるアプリケーションが、Robert C. Martin が例に示しているような 条件と、数学的に厳密であるという必要があるという条件のどちらも 同時に満たしている必要がある場合はどうすれば良いのでしょうか? もちろん、答えは NO です。 従って、上に書いた CES さんの答え「数学に厳密である必要がある 場合には YES」は、条件に抜けがある誤った答であるということに なります。 私には、CES さんが LSP の本質を理解していないように見えますね。 理解しているなら、上のような文章が出てくる筈はありません。
[この投稿を含むスレッドを表示] [この投稿を削除]
[710] Re:オブジェクト指向「初」入門
投稿者:CES
2007/02/20 02:13:25

#ちょっと落ち着いて整理しましょう… >だったら最初からそう書けばいいのに。 >つか、それを読み取れというのは無茶と言うものです。 これは、(お互いに)「自分ではそう言ったつもりだったけど、相手には伝わらなかった」ということで傷み分けにしましょうや。 >私が最初から言っているのは、 > >「クラスを辞書で引いて分類と書いてあるからといって、プログラムの設計の際に、 >クラスを分類として捉えることが正しいことになるわけではない」 > >ということです。 なるほど。 俺は「辞書的用語」と「術語」の問題(あくまで用語の問題で、実際の設計はまた別問題)だと思っていました。 前橋さんは「どう呼ぶか」よりも「実際にどう設計するか」を問題視されていた。そこが齟齬の原因。 ちなみに、それに関する俺の答えは [706] > もちろん、実装の都合上、必ずしもそうとは言えないクラスが出てくることはあり得るでしょう。しかし、それらは例外として考えるべきであり、基礎としては無駄ではありません です。一言で言えば「(例外はあるかもしれないが)原則的に、辞書的意味と同じと考えていい」ということ。 後半は感情的になって煽ってしまったことはお詫びいたします。 #最後の最後で何とかまとまってよかった。
[この投稿を含むスレッドを表示] [この投稿を削除]
[709] Re:オブジェクト指向「初」入門
投稿者:CES
2007/02/20 02:13:25

> リンク先の文章で示されている「正方形を長方形のサブクラスにするのが誤り」という話が的外れであることは、ずいぶん前に書いてますよ。 と言うだけでは何なのでちょっと補足。 「正方形は長方形のサブクラスである」というのは、数学的に見れば、確かにそうです。 しかし、長方形の数学的な性質とは、「4つの角が全て直角である四角形」というだけであって「縦と横の長さが独立していなければならない」という要件はありません。 リンク先の文章では、その数学的には必要とされていない要件を要求しているのですから、もはや「数学的には正方形は長方形のサブクラスだ」という前提は成り立たないのです。 では、正方形クラスを長方形クラスのサブクラスとして設計するのは誤りなのでしょうか? そうとは限りません。 数学的に正しいプログラムを書く必要があって、長方形の縦と横の長さが独立していなければならないという要件が無いのなら、この設計は全く妥当なものです。 「正方形は長方形のサブクラスであるか?」という質問に、唯一の正解はありません。 答えは、数学に厳密である必要がある場合には YES 、そうでない場合には NO になります。 提示された文章では、それがちぐはぐだからおかしなことになっているのです。 [653] あたりで言っているのはそういうことです。
[この投稿を含むスレッドを表示] [この投稿を削除]
[708] Re:オブジェクト指向「初」入門
投稿者:CES
2007/02/20 02:13:25

>> 「サブクラスの方が機能が上」という考え方だと、is-a の関係を考えた時 >> に「機能が豊富なものは機能がショボいものの一種」ということになって変 >> だよね。 > >変じゃないですよ? >Robert C. Martin が、Liskovの置換原則に関する有名な文書 >http://www2.ocn.ne.jp/~yamagu/object/LSP-J.pdf >で述べた通り、継承の際に使う is-a の関係は、「オブジェクトが is-a で >あるか」ではなく、「オブジェクトの『振舞い』が is-a であるか」で判断 >すべきです。 >「機能が豊富なものは機能がショボいものの一種」というのは、この原則を >平易な言葉で言い替えたものに過ぎません。 >オブジェクト指向設計の大原則です。 いや恐れ入った。 「オブジェクトが is-a であるか」は守られなくてもいいのか。 LSP を守るべきであるのは当然だけど、リンク先の文章で示されている「正方形を長方形のサブクラスにするのが誤り」という話が的外れであることは、ずいぶん前に書いてますよ。
[この投稿を含むスレッドを表示] [この投稿を削除]
[707] Re:オブジェクト指向「初」入門
投稿者:kit
2007/02/20 02:13:25

抽象論は苦手なので、このスレッドには書かないようにしようと思ってたの ですが、堂々めぐりしているように見えるので。 > 「サブクラスの方が機能が上」という考え方だと、is-a の関係を考えた時 > に「機能が豊富なものは機能がショボいものの一種」ということになって変 > だよね。 変じゃないですよ? Robert C. Martin が、Liskovの置換原則に関する有名な文書 http://www2.ocn.ne.jp/~yamagu/object/LSP-J.pdf で述べた通り、継承の際に使う is-a の関係は、「オブジェクトが is-a で あるか」ではなく、「オブジェクトの『振舞い』が is-a であるか」で判断 すべきです。 「機能が豊富なものは機能がショボいものの一種」というのは、この原則を 平易な言葉で言い替えたものに過ぎません。 オブジェクト指向設計の大原則です。 > 集合論(というか論理学のベン図)で考えれば、スーパークラスはサブクラ > スよりも一段階抽象的なので、直接比較できないことはわかるよね。 これは、「オブジェクトが is-a であるか」を継承関係の設計の判断基準に 使うべきだと述べているように見えます。これは、第一近似として使いやすい 方法ではありますが、Robert C. Martin の文書で示されている通り、厳密に 考えると誤りであり、Liskovの置換原則の方を優先すべきです。
[この投稿を含むスレッドを表示] [この投稿を削除]
[706] Re:オブジェクト指向「初」入門
投稿者:CES
2007/02/20 02:13:25

>>「術語としてのクラス」に「辞書的な『分類』という意味」を期待する > >ことについてとやかく言った覚えはありません。 >いや、とやかく言っている、というのならその部分を引用して示してください。 このへん@684 > でも、「クラス」という言葉の辞書的定義が「分類」だったからといって、 > 術語として使われるときそのままの意味とは限らないし、「クラス」という > 言葉を選んだ人が間違えたのかもしれない。だから、その言葉の意味に > 過剰な期待をしてもしょうがないのでは、と言っているのです。 >私が最初から言っているのは、 > >「クラスを辞書で引いて分類と書いてあるからといって、プログラムの設計の際に、 >クラスを分類として捉えることが正しいことになるわけではない」 > >ということです。 だったら最初からそう書けばいいのに。 つか、684 からそれを読み取れというのは無茶と言うものです。 >[684]での引用を再度引用しますが、 > >>この議論の最初のほうで、CESさんは >>>例えば、「クラス」という用語の意味は「分類」であって、「原型」ではありませんから、 > >と書いています。「ありません『から』」なんだというのでしょうか。 >もちろん、術語は用途をちゃんと表しているのにこしたことはないけれど、 >術語の方から、設計や分析についてのあるべき姿を導くことはできない。 「クラス」という用語の意味は「分類」であって、「原型」ではありませんから、「クラスはオブジェクトを作る原型になるもの」ではなく「クラスはオブジェクトを分類したもの」だと捉えるべきだ、ということです。 そして、そう捉えることは、まったく自然なように思われるのです(もちろん、実装の都合上、必ずしもそうとは言えないクラスが出てくることはあり得るでしょう。しかし、それらは例外として考えるべきであり、基礎としては無駄ではありません)。 >だから私は、術語なんかに期待するのではなく、 > >>クラスを分類として考えるのがよいとCESさんがお考えなら、それが具体的に >>有効であるケースを、例示して主張するのがよいのではないでしょうか。 > >と、具体的な例示をするのがよいのでは、と言っているのです。 「クラス」という言葉の「辞書的用法」と「述語的用法」に関しての議論だとばかり思っていたので、「クラスを分類だと考えていない」とはまさか思いませんでした。 こうも書かれているのに。 ># クラスを分類とする考え方を否定しているわけではありません。 > 「そのへんの入門書にある考え方だと、こういう例ではこういうモデリングを > してしまいがちだけど、分類として考えればこんなモデリングになって、 > こういう拡張をするときのことを考えたらこっちのほうが修正箇所が > はるかに少なくてすむよね」とか。 「サブクラスの方が機能が上」という考え方だと、is-a の関係を考えた時に「機能が豊富なものは機能がショボいものの一種」ということになって変だよね。 集合論(というか論理学のベン図)で考えれば、スーパークラスはサブクラスよりも一段階抽象的なので、直接比較できないことはわかるよね。 「サイヤ人とスーパーサイヤ人はどっちがすごいか」は、暗黙のうちに「サイヤ人=スーパーじゃないサイヤ人」という仮定を置かず、「サイヤ人はスーパーサイヤ人のスーパークラス」とする限り、比べられない(作中ではこの仮定が暗黙のうちに成立している)。 「スーパーじゃないサイヤ人とスーパーサイヤ人」ではスーパーサイヤ人の方がすごいけど、スーパーじゃないサイヤ人はスーパーサイヤ人のサブクラスではない。 …でいい? 「こう考えるといいよね」というよりも「こう考えないと明らかに変だよね」という感じなので、修正工数云々で対比することはできない。
[この投稿を含むスレッドを表示] [この投稿を削除]
[705] Re:多態性(ポリモーフィズム)について
投稿者:CES
2007/02/20 02:13:25

ふと思ったのは、「オブジェクト指向データベース」っていう言葉からして破綻している気がするなぁ…というものでした。 「オブジェクト指向データベース」って何なんだ…と思って、易しい入門を読んでみましたが、そこでは結局、「データを隠蔽し、getter と setter を持っているものがオブジェクトである」という程度でした。 結局のところ、「データベース」である以上は「オブジェクト指向言語と相性のいい永続化ストア」を脱し切れなくて、一歩踏み出すならば「オブジェクトベース」にならなければいけないような気がします(その具体的な形がどうなるかまでは分かりませんが)。 #まぁ、汎用機時代からデータスキーマを使いまわすような運用では、そうそうパラダイムシフトも起こせないでしょうけど。
[この投稿を含むスレッドを表示] [この投稿を削除]
[704] Re:オブジェクト指向「初」入門
投稿者:(ぱ)
2007/02/20 02:13:25

>まだ酔っ払ってますか? 何故そんな言葉を持ち出されたのかさっぱり分かりません。 スーパークラスやサブクラスは術語であり、悟空やベジータはスーパーサイヤ人です。 単純に置き換えただけですが何か? >「術語としてのクラス」に「辞書的な『分類』という意味」を期待するのは、 >まったく過剰ではないと思える、ということです。 だったら最初からそう書けばいいのに。 つか、 >>「クラス」も「スーパークラス」も、全く過剰だとは思えないので。 これからそれを読みとれというのは無茶というものです。 で、私は、 >「術語としてのクラス」に「辞書的な『分類』という意味」を期待する ことについてとやかく言った覚えはありません。 いや、とやかく言っている、というのならその部分を引用して示してください。 私が最初から言っているのは、 「クラスを辞書で引いて分類と書いてあるからといって、プログラムの設計の際に、 クラスを分類として捉えることが正しいことになるわけではない」 ということです。 [684]での引用を再度引用しますが、 >この議論の最初のほうで、CESさんは >>例えば、「クラス」という用語の意味は「分類」であって、「原型」ではありませんから、 と書いています。「ありません『から』」なんだというのでしょうか。 もちろん、術語は用途をちゃんと表しているのにこしたことはないけれど、 術語の方から、設計や分析についてのあるべき姿を導くことはできない。 だから私は、術語なんかに期待するのではなく、 >クラスを分類として考えるのがよいとCESさんがお考えなら、それが具体的に >有効であるケースを、例示して主張するのがよいのではないでしょうか。 と、具体的な例示をするのがよいのでは、と言っているのです。 私がどのような例示を求めているのかについて、誤解があるといけないので テンプレまで書いてあげました。 >「そのへんの入門書にある考え方だと、こういう例ではこういうモデリングを >してしまいがちだけど、分類として考えればこんなモデリングになって、 >こういう拡張をするときのことを考えたらこっちのほうが修正箇所が >はるかに少なくてすむよね」とか。 ここまでやっているのにさ。 回答はこれだぜ。 >例示が必要でしょうか? 「is-a」「汎化-特化」の考えは、まさに分類だと >思うのですが(「汎化」「特化」という用語を誰が考えたかは知りませんが、 >いくらこれが術語だからと言って「低機能」「高機能」という意味でないのは >明らかでしょう)。 (特に自分とこの掲示板で)こんな終わり方をするのは私も嫌なんですが、 意思の疎通をする気がないようなので、これまでとしたいと思います。
[この投稿を含むスレッドを表示] [この投稿を削除]
[703] Re:オブジェクト指向「初」入門
投稿者:CES
2007/02/20 02:13:25

>>何が過剰なのかわからないのです。 > >[684]に書いたことを繰り返すつもりはありません。 #こういう終わり方は大嫌いなのですが 「意思の疎通ができてないみたいなので、これまでとしましょう」って打ち切っていいですか? >>「クラス」も「スーパークラス」も、全く過剰だとは思えないので。 > >もはや日本語むちゃくちゃになっていませんか? > >「スーパーサイヤ人に過剰な期待をするのはいかがなものか」 >「悟空もベジータも、全く過剰とは思えない」 > >意味不明です。 まだ酔っ払ってますか? 何故そんな言葉を持ち出されたのかさっぱり分かりません。 最初の文の意味が通じなかったのならば補足します。 「術語としてのクラス」に「辞書的な『分類』という意味」を期待するのは、まったく過剰ではないと思える、ということです。 何が「過剰な期待」で、何が「過剰でない期待」なのか。術語には何なら期待していいのか。 [684] を何度読み返しても、それがわからないんですよ。
[この投稿を含むスレッドを表示] [この投稿を削除]
[702] crowbar ver.0.4のバグを修正しました
投稿者:(ぱ)
2007/02/20 02:13:25

># つhttp://www.shiro.dreamhost.com/scheme/docs/cont-j.html 紹介していただいたshiroさんのページは、以前読んだことはあったのですが、 あまり理解できずにそのままになっていました。 そこで今回、以下のページのActionScript版をベースに、crowbar版を書いて 試してみたところ… クラッシュしました。 http://torus.jp/memo/x200403/nandemo_keizoku_as.rd.html 調べてみたら、「配列リテラルの要素にオブジェクトを格納すると、一時的に スタックからの参照が切れることがあり、そのタイミングでGCすると死ぬ」という 潜在バグが(おそらくver.0.2から)あり、かつ、ver.0.4では「毎回GCする」という テスト用の状態でリリースしてしまったために発覚しました。 取り急ぎ修正版をリリースしました。毎度テストが甘くすみません。 力尽きたので今日はここまでです。
[この投稿を含むスレッドを表示] [この投稿を削除]
[701] Re:正規表現関連の関数の質問
投稿者:タイガー
2007/02/20 02:13:25

>crowbarでも、CRB_dev.hにあるオブジェクト確保系の関数群において、自動的に >参照をスタックに積んでしまうことは可能です。ネイティブ関数実行後スタックを >戻す処理も、処理系側で苦もなくできます。 >なぜ今そうなっていないかというと… うーん、何故なんでしょう? (^^; >たとえば、オブジェクトを確保して、すぐに既存の配列の要素にセットする、 >という場合、特にスタックに積む必要はないのでその辺の無駄を嫌ったような >気がしますが、現状の仕様は、「いつGCが起きる可能性があるか」という点について >crowbarの内部実装をネイティブ関数の作者に晒していることになりますから、 >大変よろしくないですね。次バージョンでは修正します。 > >ネイティブ関数側で、大量のオブジェクトの確保/破棄を繰り返すような処理が >あるとすると、スタックが大量に無駄になりますが、そんなことわざわざ >ネイティブ関数で書かないですよねえ… あるいはその場合でも、 >ネイティブ関数用を呼び出したときのCRB_LocalEnvironmentに >local referenceの表を持ち、スタックではなくそちらに参照を入れるようにして >おけば、ネイティブ関数のプログラマに手で解放させることも不可能ではないですし。 ご教授ありがとうございます。 GCに少し興味を持ったのですが、お勧めのサイトなどありますか? 以下の本を購入しようと思ったのですが、(ぱ)さんは読んだことありますか? http://www.amazon.co.jp/exec/obidos/ASIN/0471941484/qid=1136566955/sr=1-1/ref=sr_1_10_1/250-0589622-2949018
[この投稿を含むスレッドを表示] [この投稿を削除]
[700] Re:多態性(ポリモーフィズム)について
投稿者:happie
2007/02/20 02:13:25

>そちらのblogも拝見しました。 ありがとうございます。 >「業務アプリケーションにはオブジェクト指向は >向かない」というのは同意見です。まあ業務アプリでも、フレームワークや、 >アプリケーションの「機能」の側でOOを使うところはたくさんありますが、 >「データ」に処理は結びつかないと思います。 そうなんですね。処理系、GUIアプリだとOOは相性はいいのですが、多くの本で、その辺の区別をせずに何でもOOがいいとして、で業務アプリを例にとって解説しているのですが、構造を見るとあまりOOっぽくない。最近流行のDIも、シングルトンを使ってかつインターフェースに対する実装クラスは一つが基本なので、これってOOなのって思ってしまいます。 >ということで、総論では賛成ですが、「メモリ中心では無理だから」という方の >理由付けはいらないんじゃないでしょうか。メモリに載ろうが載るまいが、 >あるいはオブジェクト指向データベースがディスク上のデータを透過的に見せて >そのへんの問題をすべてクリアしてくれようが、データに処理が結びついていない以上、 >やっぱりオブジェクト指向には合わないのだと思います。 確かにそのとおりですね。データと処理が結びつかないという議論とは別でした。 形上、オブジェクト指向的に作ったところでデータベースとの絡みでいろいろと問題が出てくるし、データベース中心に考えていった方がいいんじゃない、ということで、業務アプリケーションはオブジェクト指向に向いていないということを言おうとしていました。現在のORマッピングツールでは駄目ですね。おっしゃるとおり、たとえオブジェクト指向データベースで完全に透過的になったとしても、やっぱり「オブジェクト指向的」としか言えないでしょうね。
[この投稿を含むスレッドを表示] [この投稿を削除]
[699] Re:多態性(ポリモーフィズム)について
投稿者:(ぱ)
2007/02/20 02:13:25

>はじめまして。happieと申します。 はじめまして。 >また『ポインタの完全制覇』や「疑り深いあなたのためのオブジェクト指向再入門」 >については、本質的なところを突いているため大変興味深く読ませていただきました。 ありがとうございます。 >(ぱ)さんは、ポリモーフィズムに関してあまり記述がなかったように思いますが、 >どのようにお考えですか。 ポリモルフィズムは重要な概念だと思います。 「疑り深い~」でポリモルフィズムについて触れていないのは、今のところまで書いて 力尽きたというか、優先度がそれほど高くないと思ったからです。 マルチプルインスタンスについては、オブジェクト指向を普通に使っている人なら 誰でも普通に使っているにもかかわらず、そして初心者は結構そこでつまずくにも 関わらず、ほとんど誰もそれを重要であると声に出して言わないので、 これはまずいだろ、と思いあれを書いたわけですが、インタフェースと ポリモルフィズムに関しては、私は重要だと思いますし、誰もが重要だと言いますから、 特に声を上げる必要もないかなあ、と。 ただ、ポリモルフィズムが実際にどういう場面でどこまで使えるか、という点に ついては注意を払う必要があるとは思います。 最近、この掲示板の[649]にも書いていますが、「CADを作るとき図形にdraw() メソッドを付けるのは正しいのか」という話を、私はしょっちゅう出しています。 そちらのblogも拝見しました。「業務アプリケーションにはオブジェクト指向は 向かない」というのは同意見です。まあ業務アプリでも、フレームワークや、 アプリケーションの「機能」の側でOOを使うところはたくさんありますが、 「データ」に処理は結びつかないと思います。 業務アプリでは、たくさんのテーブルがあり、また、さらにたくさんの「機能」が あります。そして、しょっちゅう機能追加を行っても、そうそうテーブル定義には 変更を加えません。データが何よりえらいのであって、処理がデータに依存しても、 データが処理に依存してはいけない。これは基本的には「CADで図形にdraw()メソッドを 付けるべきではない」というのと同じことだと思います。 ということで、総論では賛成ですが、「メモリ中心では無理だから」という方の 理由付けはいらないんじゃないでしょうか。メモリに載ろうが載るまいが、 あるいはオブジェクト指向データベースがディスク上のデータを透過的に見せて そのへんの問題をすべてクリアしてくれようが、データに処理が結びついていない以上、 やっぱりオブジェクト指向には合わないのだと思います。
[この投稿を含むスレッドを表示] [この投稿を削除]
[698] Re:正規表現関連の関数の質問
投稿者:(ぱ)
2007/02/20 02:13:25

>>これは、Cの関数を抜けた後の話ですよね。レジストリに登録するという。 >>crowbarでは、Cの関数の実行中でも、ヒープ関連の関数を呼び出すと >>GCが動く可能性がありますから、ひとまずスタックに積まなければなりません。 > >私はよく分かっていないのですが、このcrowbarのアプローチは >メジャーなのですか? たぶん、メジャーでも、望ましいものでもないと思います。 Rubyだと、GCがCスタックをスキャンするから何もしなくてよく、 Pythonだと、参照カウンタを上げたり下げたりを手でやらなければいけなかったと 思いますが(過去形?)、たとえばJavaのJNIでは、別にCスタックをスキャンする わけでもないのに、確保したオブジェクトについては特に何もする必要はありません。 JavaHouseにある首藤さんの記事によれば、 http://java-house.jp/ml/archive/j-h-b/013314.html | JNI: native method のフレームごとに local references (の表) を用意、 | local references から辿れるオブジェクトは回収しない。 とのことです。 crowbarでも、CRB_dev.hにあるオブジェクト確保系の関数群において、自動的に 参照をスタックに積んでしまうことは可能です。ネイティブ関数実行後スタックを 戻す処理も、処理系側で苦もなくできます。 なぜ今そうなっていないかというと… うーん、何故なんでしょう? (^^; たとえば、オブジェクトを確保して、すぐに既存の配列の要素にセットする、 という場合、特にスタックに積む必要はないのでその辺の無駄を嫌ったような 気がしますが、現状の仕様は、「いつGCが起きる可能性があるか」という点について crowbarの内部実装をネイティブ関数の作者に晒していることになりますから、 大変よろしくないですね。次バージョンでは修正します。 ネイティブ関数側で、大量のオブジェクトの確保/破棄を繰り返すような処理が あるとすると、スタックが大量に無駄になりますが、そんなことわざわざ ネイティブ関数で書かないですよねえ… あるいはその場合でも、 ネイティブ関数用を呼び出したときのCRB_LocalEnvironmentに local referenceの表を持ち、スタックではなくそちらに参照を入れるようにして おけば、ネイティブ関数のプログラマに手で解放させることも不可能ではないですし。 >>ただ、たぶんこの場合、グローバル変数を共有して動かしたいのだと思います。 >>そうだとすれば、外部crowbarスクリプトのトップレベルを動かす方法は >>ありません。ただし、外部crowbarスクリプト中の関数であれば、 >>CRB_compile()とCRB_call_function()を使えば実行可能です。 > >外部crowbarスクリプト中の関数を呼べるということは、 >データを引数でC側からcrowbarの関数側にプッシュしてあげて、 >crowbarスクリプトのglobal変数に保存しておけば、 >データの共有はできると思います。 誤解させる書き方をしてしまったかもしれませんが、CRB_compile()と CRB_call_function()を使う方法なら、インタプリタを共有できますから、 グローバル変数によるデータ共有が可能です(まあ、引数で受け渡しするほうが よいのはよいでしょうけど)。 >私は、(ぱ)さんの本はほとんど持っていますが、もう少し内容を濃くして >「プログラミング言語を作る」もぜひ出版して欲しいです。 >早くていつ頃になりそうですか? いやあ、それはさっぱりわかりません。 まだ企画が動き出したわけですらないですし。
[この投稿を含むスレッドを表示] [この投稿を削除]
[697] Re:オブジェクト指向「初」入門
投稿者:(ぱ)
2007/02/20 02:13:25

>何が過剰なのかわからないのです。 [684]に書いたことを繰り返すつもりはありません。 >「クラス」も「スーパークラス」も、全く過剰だとは思えないので。 もはや日本語むちゃくちゃになっていませんか? 「スーパーサイヤ人に過剰な期待をするのはいかがなものか」 「悟空もベジータも、全く過剰とは思えない」 意味不明です。
[この投稿を含むスレッドを表示] [この投稿を削除]
[696] 昨晩は酔っ払ってました
投稿者:(ぱ)
2007/02/20 02:13:25

 えー、すみません、興味深い投稿がたくさんされてますが、昨晩は酔っ払って帰って きてそのまま寝てしまいました (^^;  お返事は今晩ということでよろしくお願いします。
[この投稿を含むスレッドを表示] [この投稿を削除]
[695] イテレータ(とCPS)
投稿者:NykR
2007/02/20 02:13:25

http://kmaebashi.com/programmer/devlang/regexp.html > Java流の、要素を取り出すだけで強制的にポインタを進めてしまう仕様は、 > ちょっとどうかと思うんですがねえ。 同感です。マージとかやりにくそう。 というわけでどちらか片方にするなら、GoFの方が良いな、と思ってたりするのですが、以下のような関数を用意すれば、GoF方式とJava方式の両方をあまり手間をかけずに作ることができます。(両方欲しい、という訳ではありませんが) function iterator_GoF(foreach_CPS) { this = new_object(); this.first = closure () { this.isDone = closure () { return false; }; foreach_CPS(closure (item, cont) { this.currentItem = closure () { return item; }; this.next = closure () { cont(null); }; }, closure (ret) { this.isDone = closure () { return true; }; }); }; this.first(); return this; } function iterator_Java(foreach_CPS) { this = new_object(); closure () { this.hasNext = closure () { return true; }; foreach_CPS(closure (item, cont) { this.next = closure () { cont(null); return item; }; }, closure (ret) { this.hasNext = closure () { return false; }; }); }(); return this; } function foreach(foreach_CPS, proc) { # ついでに作った foreach_CPS(closure (item, cont) { cont(proc(item)); }, closure (ret) {}); } # foreach_CPSは CPS(continuation passing style, 継続渡し形式)で書かれたforeachです。 # つhttp://www.shiro.dreamhost.com/scheme/docs/cont-j.html これらの関数に対し、コレクション側でforeach_CPSを用意して渡せば それぞれの関数に対応したイテレータが返されます。 例えば以下のような2分木があったとすれば tr = {13,{5,{3,{},{}},{13,{12,{},{}},{}}},{16,{16,{15,{},{}},{}},{17,{},{}}}}; function foreachTree_CPS(tree) { return closure (proc, fin) { closure internalIterate(node, cont) { if (node.size() == 0) { cont(null); } else { internalIterate(node[1], closure (ret) { proc(node[0], closure (ret) { internalIterate(node[2], cont); }); }); } }(tree, fin); }; } という関数を1つ定義するだけで print("foreach:"); foreach(foreachTree_CPS(tr), closure (item) { print(" " + item); }); print("\n"); print("GoF :"); for (i = iterator_GoF(foreachTree_CPS(tr)); !i.isDone(); i.next()) { print(" " + i.currentItem()); } print("\n"); print("Java :"); for (i = iterator_Java(foreachTree_CPS(tr)); i.hasNext();) { print(" " + i.next()); } print("\n"); のように、3種類のイテレータが使えてまあ便利。 でもシーケンシャルなコレクションだとそれほど嬉しくはありません。 CPSではforやwhileがまともに使えないので、例えば配列用のforeach_CPSは function foreachArray_CPS(array) { return closure (proc, fin) { closure loop(index, cont) { if (index == array.size()) { cont(null); } else { proc(array[index], closure (ret) { loop(index + 1, cont); }); } }(0, fin); }; } なんてことになって、要素数が多いとforeachに渡したときにクラッシュするなあ、とか、初期値と変数が遠いなあ、とか。 今の実装だと末尾再帰の最適化は物凄く大変そうですしね。 # でもこの形式のループは個人的にはむしろ欲しかったりします、crowbar ver.0.3.02にcall/ccを付けたので。ちなみに、この時点で末尾再帰の最適化はやりやすくなったのですが、CRB_Objectが増えたので先にGCを改造しなければならないのでした(どうでもいい報告)。 この手法は、 ・外部イテレータを直接作るのが難しくて ・ループによらないアクセスが簡単な データ構造(木とか) *では* 役に立ちます。 # ライブラリが簡単に書けます。というのはそれほど嬉しいことではない気がする
[この投稿を含むスレッドを表示] [この投稿を削除]
[694] Re:正規表現関連の関数の質問
投稿者:タイガー
2007/02/20 02:13:25

>LuaやJavaScriptの方法だと、名前空間が動的になるんですよね。 >環境作ってないので試してませんけど、Luaで「hoge = string」と書くと、 >hoge.sub()で文字列置換ができるようになるんじゃないでしょうか。 試してみたらできました。 静的、動的な違いというのも理解しました。 ありがとうございます。 >また、単なるテーブル(crowbarならオブジェクト)を名前空間に使うと、 >ライブラリ関数が壊せてしまうというのも、問題だと思います。 >Luaでは、string.subに代入できるんですよね? こちらも試してみたらできました。 確かに問題になりそうですね。 >と言いつつ、実は今crowbarに予約語finalを持ち込んで定数を作れるように >したりしてるので、ライブラリ関数のメンバとか名前空間オブジェクトをfinalに >してしまえば、これでもいい気がしてきました。うーん。 crowbarでfinalで定数作れるようにしてたんですね。 よくimmutableなクラスを作るときにはfinalにしないといけないと 書いてあるので、そういう意味ではfinalは必須な気もします。 Luaには定数にする方法はなかったような…。 >これは、Cの関数を抜けた後の話ですよね。レジストリに登録するという。 >crowbarでは、Cの関数の実行中でも、ヒープ関連の関数を呼び出すと >GCが動く可能性がありますから、ひとまずスタックに積まなければなりません。 私はよく分かっていないのですが、このcrowbarのアプローチは メジャーなのですか? >Luaの仮想スタックって、まさにcrowbarのスタックのような独自スタックでは? >crowbarのような手間がなさそうなところからして、Cの関数の部分だけ、 >Cスタックをスキャンするconservative GCなのかなあ、とも思ったのですが、 >テーブルを確保すると勝手にスタックに積まれること、他の参照型というと >文字列くらいですがCRB_Stringのような型では扱っていないことからして、 >コンサバGCではないようですね。 Luaの仮想スタックは、確かに独自スタックです。 しかし、私の知識不足とLuaのGCのメカニズムは記述がなかったため よく分かりません。 >うーん、CRB_add_native_function()は別にinterface.cの中で呼ぶ必要はないんですが >(実際今はnative.cとかの中で呼んでますし)。実行中でも呼べますから、 >ネイティブ関数内でも登録可能です。 実行中に呼べるのは応用範囲が広がりそうですね。 >これに近いことをするのであれば、(ver.0.4で新設された)CRB_create_closure()で >クロージャ作って、CRB_add_assoc_member()でオブジェクトに登録するなり >CRB_add_global_variable()でグローバル変数に登録するなりもできます。 crowbarのように実際のデータを直接扱えるとコードが分かりやすいと 思います。 Luaみたいに仮想スタック上にデータを作っておいて…みたいのは 分かりにくいです。 >crowbarスクリプトをまったく独立して動かせばよいのなら、インタプリタを >もうひとつ作って実行すればよいです。 >ただ、たぶんこの場合、グローバル変数を共有して動かしたいのだと思います。 >そうだとすれば、外部crowbarスクリプトのトップレベルを動かす方法は >ありません。ただし、外部crowbarスクリプト中の関数であれば、 >CRB_compile()とCRB_call_function()を使えば実行可能です。 外部crowbarスクリプト中の関数を呼べるということは、 データを引数でC側からcrowbarの関数側にプッシュしてあげて、 crowbarスクリプトのglobal変数に保存しておけば、 データの共有はできると思います。 C側に戻したいときもC側から渡す引数につめてあげれば 戻せます。 私は、この(例えば)Cとスクリプトとのデータの共有が一番 重要と思っているのですが、crowbarでも簡単にできそうですね。 だとすれば、既に組み込み言語として十分使えるレベルだと思います。 実は、Luaではこの一番重要な部分が一番面倒くさいです。 >やりたいこと・やるべきことはいろいろあって、その中にはcrowbar以外のことも >含まれます。もう少ししたら、crowbarほっぽりだして別の言語を作り始める >可能性も(かなり高い確率で)あります。企画の趣旨的には、crowbarを実用言語に >するよりも、バイトコードインタプリタのような違う実行形態の言語をもうひとつ >作る、という方が合っている気がしますし。 バイトコードインタプリタはかなり興味深いです。 楽しみにしています。 >ということで、crowbarに期待してくださるのは大変嬉しいことですし、 >私としても励みになるのですが、私の時間が有限である以上お応えできるかどうかは >わからない、ということで、すみませんがご理解ください。 私は、(ぱ)さんの本はほとんど持っていますが、もう少し内容を濃くして 「プログラミング言語を作る」もぜひ出版して欲しいです。 早くていつ頃になりそうですか? >あと、今だと鬼車のライセンス表記をどこかに含めなければいけませんよね。 >次バージョンで検討します。 ソースの理解は、まず動作が分かってからだと思うので、 とりあえず簡単に動かしてみたいという気持ちはあります。 あと、Cとの連携方法など、ユーザの視点でのcrowbarの使い方 みたいな文章も欲しいところです。 時間がないでしょうが、いつも楽しみにしていますので 頑張ってください。
[この投稿を含むスレッドを表示] [この投稿を削除]