おにいちゃんのちえぶくる: おろし金が見つからないときは、しゃもじを使おう
生姜おろそうと思ったら、おろし金がなかった。
昔、おろし生姜使ったおぼえがあるので、どっかにあると思ってたんだけどなー。以前は、チューブのやつを使ってたらしい。
しかたないので、なんか代わりになるものを捜したところ、でこぼこしゃもじが使えそうだったので、やってみたら、ギリギリなんとかなった。若干つらかったけど。
というわけで、でこぼこしゃもじは、おろし金のかわりにもなる大変すばらしい物体ですという話でした。
しかし、そろそろ、おにいちゃんとかいう年齢じゃないよな!キモい死ねよ。
■
http://www.hondanojo.com/emshamoji.html
ふむ。まあ、210円だったら、なんかほほえましいといえる範囲かな…
thisとメソッド
昔よく似た話を書いた↓けど、まあいいや。最近再利用が多いなー
http://d.hatena.ne.jp/w_o/20050824#p2
JavaScriptとかPHPとかのキモいといわれている点に、メソッドがオブジェクトとひとつになってないというのがある。
<script> function Nanika() { this.i = 40; this.method = function() { return this.i; } } var a = new Nanika(); document.write( a.method() + "<br>"); var obj = { i:100 }; obj.method = a.method; document.write( obj.method() + "<br>" ); </script>
a.methodは、a.iを参照してる…と思うのだけど、二回目のdocument.writeでは、objのiの値が表示されてしまう。
なんでこうなるのかというと、このほうが効率よく実装するのが簡単だから。(実装によるけど。多分)
JavaScriptのように、メソッドがthisを保存しないような場合の実装は、
- クラスごとにメソッドを持つ
- オブジェクトが持ってるのは、クラスが持ってるメソッドへのポインタのみ
こんな感じのデータ構造にして、
a.b()
こういう場合の処理を、「aをthisにして、メソッドbを呼び出す」っていう感じで、ひとつにまとめてしまう、と。
仮想マシン命令にすると、
push_global [symbol a] call_method_with_obj [symbol b]
こぉぉーーーんな感じでできあがり…って、どんな説明だよ。
とにかく、それなりに簡単にできる、というわけ。
ところが、「メソッドの呼び出しかたによって、thisが変わる」というJavaScriptみたいな変な挙動を防ぐには、メソッドごとに「そのメソッドのthisはなんなのか」というのを記憶しとかないといけない。
struct method_with_this { struct object *this; struct method_body *body; };
こんなのがいります。と。
これが、「オブジェクトの数 x メソッドの数」だけ必要。4個のメソッドを持つオブジェクトが20個あったら、80個分のmethod_with_thisが必要。ポインタが4byteだとしたら、640byte。
さらに、method_with_thisは、1wordに収まらないので、この640byteは、それぞれ、一個づつヒープから取ってこないといけない。(ほんとかよ。シンボルテーブルが持てる値を2wordにすればいけるよ。なんかうまくやれば良いような気がしてきた)
さて、これは、どのぐらいの無駄だろうか。「オブジェクトから、メソッドだけを取ってくる」っていうのは、たまーに必要だけど、大体の場合で必要じゃないよね。多分。あと、JavaScriptだと、
function new_method( obj, method ) { return function() { return obj.method(); } } var m = new_method( a, a.method ); document.write( m() + "<br>" );
クロージャを使うという手段があるわけで、なんか、まあ、十分許容範囲だよね。と、だから、簡単に実装できるほうでいいや、ということで、上で書いたような言語仕様になってるのだと思う。
ところが、Python。
Pythonは、JavaScriptとかと違ってメソッドがthisを保存してるのである。
class Nanika: def __init__(self): self.i=40 def method(self): return self.i class Nanika2: def __init__(self): self.i=100 a = Nanika() b = Nanika2() b.method = a.method b.method() 40
えー。
def ref_method2(a): return a.method() dis.dis(ref_method2) 0 LOAD_FAST 0 (a) 3 LOAD_ATTR 0 (method) 6 CALL_FUNCTION 0 9 RETURN_VALUE
バイトコード見ても、変なトリックもなく。
というわけで、Pythonのメソッドって大変メモリを使ってそうな気がしたのです。
これのトリックは、Pythonが、参照カウントを使ってるという点。
LOAD_ATTR(method)で、メソッドが参照される。
このとき、メソッドのデスクリプタget(仕組みは知らないけど…__get__が云々)が呼び出されて、新規にメソッドが割り当てられる。(メソッドが割り当てられるのは、オブジェクト生成時ではなく、メソッド参照時になる)
んで、CALL_FUNCTION。CALL_FUNCTIONが終わると同時に、参照カウントが0になって、メソッドは解放される。と、メソッドが生きてる期間が非常に短いので、あんまりメモリの無駄が無いという。
あと、メモリ割り当ても、メソッドだけ特別扱いされてて、
/* Objects/classobject.c */ static PyMethodObject *free_list; PyObject * PyMethod_New(PyObject *func, PyObject *self, PyObject *klass) { register PyMethodObject *im; if (!PyCallable_Check(func)) { PyErr_BadInternalCall(); return NULL; } im = free_list; if (im != NULL) { free_list = (PyMethodObject *)(im->im_self); PyObject_INIT(im, &PyMethod_Type); } ...
なんかこんな。
参照カウントだからできる無茶もあるということで。
■
まあ、でも、640byteぐらいケチるなよって話もある。いやほんと、最近のマシンは大変アレよね。数GByteとかメモリあったら、1KByteとかなんか、あるんだか無いんだかわかんないぐらいの量だよねー。
いや、仕事で、133MHzのCPUやってたんだけど、いや、それが見た感じ遅いコードだったんだけど、なんか、全然関係無いくらいの速さで動いててもう、おっちゃんね、なんか、色んな苦労は全部無駄だったような気がしたのでした。
今のPCって、エディタの一画面に収まるくらいの処理の量だったら、オーダーとか、命令数とかブっ飛んでても、体感としては、全く何も感じないくらいにまでなってると思う。たとえば、手元のプログラムのソートを全部バブルソートに変えるとか、そういうことをやっても、何も変わらないくらいなんじゃないかと。いや、しかし、そういう方向にいってしまうと、アレだなー「あなたは、このマシンパワーを何に使いますか?いつまでも貧弱なUIのままでいいんでしょうか?そろそろ、このマシンパワーを活用したUIを見てみたいものです」プギャー。このパゥワーは、WinnyのためにあるんですYO!