Scala-hack-a-thon#1に行ってきた
Scala Hack-a-thon #1 : ATND
Scalaやるやる言いつつ全然やれてなかったので、行ってきました。
会場の様子
- ほぼ満席であったが、恐ろしいほど無言。
- ただしtwitterは大にぎわい。
やったこと
Emacs scala-mode
急いでメモ。
ダウンロード
svn co http://lampsvn.epfl.ch/svn-repos/scala/scala-tool-support/trunk/src/emacs/ scala-mode
設定
;; elispを溜め込んでいるディレクトリが ~/lisp ;; scalaはMacportsでインストール ;; scala-mode (add-to-list 'load-path "~/lisp/scala-mode") (add-hook 'scala-mode-hook '(lambda () (yas/minor-mode-on))) (setq yas/scala "~/lisp/scala-mode/contrib/yasnippet/snippets") (yas/load-directory yas/scala) (require 'scala-mode-auto) (setq scala-interpreter "/opt/local/bin/scala")
キーバインド
- Evaluate Buffer
- C-c C-b
- Evaluate Region
- C-c C-r
- Load file in Interpriter
- C-c C-l
ファイルオープン
C-x C-f hoge.scala
ReflectionClassでメソッドを動的に呼ぶ&ベンチマーク
以前に変数を使って、クラスに対して動的にプロパティを追加してみた。
Objectに動的にプロパティを追加する - うっかりプログラミング日誌
今度は動的にメソッドを呼んでみる。*1
<?php class Test1 { public function hoge() { return 1 + 1; } } $test = new Test1(); $method = 'hoge'; $test->$method(); //=> 2
簡単!
ReflectionClassを使う
とはいえ、この方法だと引数が固定数しか取れない。
そこでReflectionMethod#invokeArgsを使う。
<?php class Test2 { public function hoge($a, $b) { return $a + $b; } } $test = new Test2(); $method = 'hoge'; $args = array(1, 3); $ref = new ReflectionClass(get_class($test)); $met = $ref->getMethod($method); // ReflectionMethodクラスが返される $met->invokeArgs($test, $args); //=> 4
このReflectionClassが結構便利。
複数のクラスを受け取れるメソッドを用意しておいて、
ReflectionClass#hasMethod()と組み合わせたりすると良い。
ちなみにReflectionMethod#invoke()だと上記の変数呼び出しと同じ使い方になる。
で、遅くないの?
実はここが本題。
動的にメソッドを呼び出すのはいいけど、一体どっちが早いのか?
メソッド呼び出しを100万回くらいやれば速度差が出るかと思い、やってみた。
PEAR::Benchmarkを使っています。
<?php class Test1 { public function hoge() { return 1 + 1; } } require_once('Benchmark/Timer.php'); $timer = new Benchmark_Timer(); $test = new Test1(); $method = 'hoge'; $max = 1000000; $timer->start(); // 変数呼び出し for($i=0;$i < $max; $i++) { $test->$method(); } $timer->setMarker('Variable'); // ReflectionClass for($i=0;$i < $max; $i++) { $ref = new ReflectionClass(get_class($test)); if ($ref->hasMethod($method)) $met = $ref->getMethod($method); $met->invoke($test); } $timer->setMarker('Reflection'); $timer->stop(); $timer->display(); /* result --------------------------------------------------------- marker time index ex time perct --------------------------------------------------------- Start 1251959593.35938300 - 0.00% --------------------------------------------------------- Variable 1251959594.44166300 1.082280 5.83% --------------------------------------------------------- Reflection 1251959611.91642100 17.474758 94.17% --------------------------------------------------------- Stop 1251959611.91648500 0.000064 0.00% --------------------------------------------------------- total - 18.557102 100.00% --------------------------------------------------------- */
newを含めると、なんと17倍の差が。
純粋にinvokeとの速度差を調べてみました。
<?php class Test1 { public function hoge() { return 1 + 1; } } require_once('Benchmark/Timer.php'); $timer = new Benchmark_Timer(); $test = new Test1(); $method = 'hoge'; $max = 1000000; $timer->start(); for($i=0;$i < $max; $i++) { $test->$method(); } $timer->setMarker('Variable'); $ref = new ReflectionClass(get_class($test)); if ($ref->hasMethod($method)) $met = $ref->getMethod($method); for($i=0;$i < $max; $i++) { $met->invoke($test); } $timer->setMarker('Reflection'); $timer->stop(); $timer->display(); /* --------------------------------------------------------- marker time index ex time perct --------------------------------------------------------- Start 1251959636.48438300 - 0.00% --------------------------------------------------------- Variable 1251959637.56774800 1.083365 33.69% --------------------------------------------------------- Reflection 1251959639.69969100 2.131943 66.30% --------------------------------------------------------- Stop 1251959639.69976100 0.000070 0.00% --------------------------------------------------------- total - 3.215378 100.00% --------------------------------------------------------- */
それでもReflectionの方が倍くらいの時間がかかる。
- クラスやメソッドを解析して、厳密にやるならReflection。
- チェックとか特に必要なく呼び出せるなら変数。
という感じで使い分けると良さそう。
*1:これの呼び方がよくわからない
array_mapでstaticメソッドを呼び出す時に、selfが使えない
ここのところPHPばかりやっているので、小ネタ。
PHP 5.2.8 for Windows での話。
array_mapで関数名を指定する時、通常は文字列で指定する。
<?php function _add3($a) { return $a + 3; } $array = array(1,2,3); print_r(array_map('_add3', $array));
クラス内の関数の場合はarrayで指定する。
<?php class Test { public function _add3($a) { return $a + 3; } public function hoge($array) { return array_map(array($this, '_add3'), $array); } } $test = new Test(); $array = array(1,2,3); print_r($test->hoge($array));
インスタンスメソッドが$thisで指定できるなら、staticメソッドはselfでいける!と思ったら・・・
<?php class Test { public static function _add3($a) { return $a + 3; } public function hoge($array) { return array_map(array(self, '_add3'), $array); } } $test = new Test(); $array = array(1,2,3); print_r($test->hoge($array));
なぜかselfが定義されていない定数というNoticeが出る。
Notice: Use of undefined constant self - assumed 'self' in test.php on line 6
s/self/get_class($this)/
s/self/__CLASS__/
でNoticeは出なくなる。selfの実体は'self'という文字列なので、クラス名の文字列を渡す必要があるコールバック関数では使えないってことかしら。
まあ、E_STRICTを切ればいいんだけど・・・ねえ。
参考
静的なクラスメソッドの場合、 0 番目の要素としてオブジェクトを渡す代わりにクラス名を渡すことにより、 オブジェクトのインスタンスを作成せずに渡すことができます。
PythonのDecorator
きっかけはwycatsのこの記事
Python Decorators in Ruby « Katz Got Your Tongue?
「RubyでPythonのDecoratorを実装してみたよー」ってことらしいが、Pythonなんてprintしか知らない僕にはDecoratorがなんだかわからない。
元が何者だかわかっていないので、Rubyでの実装コードを見てみても、いまいち掴めない。
というわけで、手を動かしてPython書いてみた。
下記ページのサンプルを動かしてみた程度。
python のデコレーターは closure に syntax sugar を かぶせたものだと言われます。
とのことです。
元の関数をすり替えてしまえるし、Closureの引数として元の関数自体も呼び出せる。
パッと使い道は出てこないけど、便利そうな感じがしますね!
ここまでやったところで、1年前にRubyで実装されていたのを見つけた。
http://d.hatena.ne.jp/technohippy/20080723#1216808401
http://yugui.jp/articles/792
とりあえず、Decoratorがちょっとわかった。
あとは自分で実装してみる!・・・かどうかはわからない。
Python3.0で変わった事2つ
Decoratorのサンプルを書いている時に、Python2.6と 3.1 の両方でやってみた。
その時にはまった変更点を2つメモ。
- print が構文から関数に変更されている
#python 3.1 print 'hoge' #=> Error! print('hoge') #=> hoge
- func_X の関数が __X__に変更されている
unc_Xとは関数オブジェクトのinspectを行う関数の集まり。
func_Xと名づけられていた関数の属性は、__X__というフォームに改称されました。古いfunc_X形式の名前は関数の属性の名前空間は、ユーザが定義する属性(名)として解放されました。即ちfunc_closure、func_code、func_defaults、func_dict、func_doc、func_globals、func_nameはそれぞれ__closure__、__code__、__defaults__、__dict__、__doc__、__globals__、__name__に改称されました。
だそうな。
Python 3.0 での変更点全訳はこちら
http://text.world.coocan.jp/TSNET/?What%27sNewInPython3.0
複数のRubyを切り替えられるようにする
Ruby1.9をメインで使いつつ、1.8系も準備しておきたかったので併存させられる環境を作ってみた。
OS: Ubuntu 9.04 Server Edition
Rubyのソースを用意する
Subversionで落としてくる。当然trunk。安定とか知りません。
$ svn co http://svn.ruby-lang.org/repos/ruby/trunk ruby
$ svn co http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8 ruby1.8
必要なライブラリを入れる
OpenSSLとconfigure作成用のautoconf
bisonがどうとかいうエラーが出るので、それも入れておく
$ sudo apt-get install libssl-dev $ sudo apt-get install autoconf $ sudo apt-get install bison
makeする
/local/ruby の下にバージョン番号のディレクトリを作成して、そこに入れる。
プログラムにはバージョン番号を付けておく
$ cd ruby $ autoconf $ ./configure --prefix=/local/ruby/<VERSION> --program-suffix=<VERSION> --with-readline-dir=/local/ruby/<VERSION> $ make $ make test $ sudo make install
シンボリックリンクを張る
aliasを付けてもいいけど、gemとか他のプログラムが /usr/bin/ruby とかを見ている場合があるので、シンボリックリンクを作っておく。
投げやりなシェルスクリプトにしてみる。
$ less switch_ruby.sh #!/bin/sh ORG_PATH=/local/ruby/$2/bin PREFIX=/usr/local/bin for NAME in ruby gem irb rdoc ri do ln -s $ORG_PATH/$NAME$1 $PREFIX/$NAME echo "/usr/bin/$NAME linked @ $ORG_PATH/$NAME$1" done $ ./switch_ruby.sh 1.9 1.9.1