JavaScript ではよくある、こういう書き方。

一応説明すると、これは実行時に外側の無名関数を実行します。外側の無名関数は返り値として、内側の無名関数を返し、変数 counter には内側の無名関数が代入されます。

これの何が嬉しいのか。

  • counter 関数が内部に持っている変数 i は隠蔽されており、外から変更できない。
  • グローバル空間の汚染は counter 1 つだけ。

といったところでしょうか。とにかく、 JavaScript 脳の人はこういう感じのコードをよく書いている気がします。 JavaScript にはオブジェクト指向によくある protected や private といったアクセスレベルを制御する機能が無いため、これの応用で内部の変数やメソッドを隠蔽することが多いです。 Prototype や jQuery でもそういった使い方がされています。

このように、 JavaScript では無名関数の中にスコープを閉じ込めることができ、これをクロージャと呼びます。

で、最近久しぶりに勉強している Perl 。 Plack のコードを読んでいる中で、 Perl にも無名関数とクロージャがあるということを知ったので、試してみました。

上部のおまじない的なものを除けば、やってることは本質的に何も変わりません。

ついでに Ruby も。これもやっていることは特に変わらないと思います。

Python にも無名関数 (ラムダ) はあるので、同じようなことができそうな気がしたのですが、うまくいかず、代わりにこんな書き方に。

これはクロージャではなく、ジェネレータという機能を使っています。内部の変数が隠蔽されており、名前空間の汚染も 1 つだけですが、最初は関数だった counter にジェネレータをぶち込むというのは、お行儀的にはよくないかもしれません。

で、 PHP でも 5.3 からは無名関数・クロージャが使えるようになったので、試してみたのですが、以下の JavaScript 的な記述では parse error となりました …

以下のように書き換えると動きますが、これでは名前空間の汚染が 2 つです。

そして、何かもう方向を見失っている例。

変数の名前空間汚染は 1 つだけど、クラス名の名前空間まで汚染してしまっている上、返り値が文字列になっています。 (__toString はそのように定義しないといけないため)

そして、一応数値を返すようにしたものが以下。

なんか、 JavaScript と Perl の話だったはずが、暗に PHP を Dis る結果になってしまったので、今日はこの辺で。

, , , , , ,

Google App Engine (以下 GAE) 上で Amazon の Product Advertising API を扱うべく、Python の XML ライブラリについて調べていました。どうも Pythonista の間では lxml というのがメジャーらしいので、さっそく組み込んでみたのですが、どうも動かない

調べて見たところ、GAE 上では、セキュリティの関係上、C 拡張による Python ライブラリは動かないとのことで、lxml は使えないようです。

代替ライブラリを探す

C 拡張がダメということは、つまり Pure Python なライブラリなら OK、という解釈でいいのでしょうか。探してみたところ、ちょうど先日購入した [エラー: isbn:4873112761:n というアイテムは見つかりませんでした] の p.479 に「XML 文書を Python のオブジェクトツリーに変換」というレシピが載っていました。

写経、そして修正

さっそく写経してみたのですが、動かない。しばらく調べた結果、誤植によるものと思われるバグを発見。その他いくつかの修正を加えたものを GitHub に公開しました。

主な変更点は以下の通りです。

  • Xml2Obj.EndElement が上手く動かないのを修正
  • UTF-8 に対応
  • ファイルからではなく、文字列を XML として読み込むよう変更
  • __getattr__ メソッドにより、Element.getElements() のショートカットを作成 (詳細は後述)

ただし、あくまでも習作であり、エラー処理等もかなり甘いので、実用的とは言えません。
その代わり、ちょっと遊ぶ程度にはちょうどいい手軽さかも。

コード

テストコード

__getattr__ による Element.getElements() へのショートカットについて

__getattr__ というのは、インスタンスに存在しないプロパティを呼び出そうとしたときに呼び出されるもので、PHP でいうと、マジックメソッドの __get にあたるものと言えるでしょう。

これにより、以下のようなショートカットを実現しています。意味はどちらも同じです。

終わりに

もともと GAE に組み込むことを目的として上記ライブラリを修正したわけですが、実はまだ組み込めてません・・・。ただ、expat を GAE に組み込んでいる例はネット上に転がっているので、まぁこれもいけるのではないかと。真相はまた今度。

関連リンク

, , ,

例えば memoize しながらフィボナッチ数列を求める場合。

class Fibonacci
  def initialize
    @memo = [0, 1]
  end

  def fibonacci(n)
    @memo[n] || @memo[n] = fibonacci(n - 2) + fibonacci(n - 1)
    @memo[n] # この行は省略しても結果は同じ
  end
end

fib = Fibonacci.new
10000.times { |n| fib.fibonacci(n) }

これで何が嬉しいのかというと、プロファイリングしてみるとこんな結果が現れます。

fibonacci メソッドの最後の行を省略しなかった場合

$ time ruby -rprofile fibonacci.rb
  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 67.92     3.24      3.24    29996     0.11     0.21  Fibonacci#fibonacci
 16.98     4.05      0.81    59992     0.01     0.01  Array#[]
  6.29     4.35      0.30        1   300.00  4770.00  Integer#times
  3.56     4.52      0.17    19996     0.01     0.01  Fixnum#-
  2.73     4.65      0.13     9954     0.01     0.01  Bignum#+
  2.52     4.77      0.12     9998     0.01     0.01  Array#[]=
  0.00     4.77      0.00        2     0.00     0.00  Module#method_added
  0.00     4.77      0.00        1     0.00     0.00  Fibonacci#initialize
  0.00     4.77      0.00       45     0.00     0.00  Fixnum#+
  0.00     4.77      0.00        1     0.00     0.00  Bignum#coerce
  0.00     4.77      0.00        1     0.00     0.00  Class#inherited
  0.00     4.77      0.00        1     0.00     0.00  Class#new
  0.00     4.77      0.00        1     0.00  4770.00  #toplevel

real    0m6.005s
user    0m4.776s
sys     0m1.184s

fibonacci メソッドの最後の行を省略した場合

$ time ruby -rprofile fibonacci_fast.rb
  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 66.39     2.39      2.39    29996     0.08     0.14  Fibonacci#fibonacci
  8.06     2.68      0.29    29996     0.01     0.01  Array#[]
  8.06     2.97      0.29    19996     0.01     0.01  Fixnum#-
  6.94     3.22      0.25        1   250.00  3600.00  Integer#times
  5.56     3.42      0.20     9998     0.02     0.02  Array#[]=
  5.00     3.60      0.18     9954     0.02     0.02  Bignum#+
  0.00     3.60      0.00        1     0.00     0.00  Fibonacci#initialize
  0.00     3.60      0.00        1     0.00     0.00  Class#inherited
  0.00     3.60      0.00        2     0.00     0.00  Module#method_added
  0.00     3.60      0.00        1     0.00     0.00  Class#new
  0.00     3.60      0.00       45     0.00     0.00  Fixnum#+
  0.00     3.60      0.00        1     0.00     0.00  Bignum#coerce
  0.00     3.60      0.00        1     0.00  3600.00  #toplevel

real    0m4.516s
user    0m3.600s
sys     0m0.904s

実行時間が約 25% 短縮されました。

注目するポイントは 2 行目の Array#[] メソッド、つまり配列から値を引き出す処理の回数が半分になっているというところ。どうしてこうなるかは、コードの意味を考えながら読めばすぐにわかると思います。ただ、こういう最適化によって、有意な効果を得られるケースがあるのかは疑問ですが・・・。

でもせっかくなので、代入の返り値はどうなってるのか、言語ごとに調べてみました。

Ruby

p (foo = "bar")
# => "bar"

JavaScript (Rhino)

var foo;
print(foo = "bar");
// => bar

Perl

my $foo;
print $foo = "bar";
# => bar

PHP

var_dump($foo = "bar");
// => string(3) "bar"

MySQL (これはちょっと番外編?)

mysql> SELECT @today := CURDATE();
+---------------------+
| @today := CURDATE() |
+---------------------+
| 2010-02-09          |
+---------------------+
1 row in set (0.00 sec)

と、ここまでは、どれも代入値が返ってくるようですが・・・

Python

print(foo = "bar")
# => SyntaxError: invalid syntax

Python では Syntax Error となります。

これを知って何の役に立つのかイマイチわかりませんが、こういうことになっていますよ、ということで。

,