この「ジェネレータを使ったバージョン」がようやく理解できた。
def fg(args): if not args: yield "" return for i in args[0]: for tmp in fg(args[1:]): yield i + tmp return
このreturnに何か意味があるのかと思って苦戦。少なくともここでは意味は無いようなのでとっぱらって、さらにどこでgeneratorが「凍結」しているのか分かりやすくするために、for in文内の暗黙的なものを明示的に書いてみた。
def h(args):
if not args:
yield ""
else:
for i in args[0]:
gen = h(args[1:])
itr = gen.__iter__()
while 1:
try:
tmp = itr.next()
except StopIteration:
break
yield i + tmp
この関数hは、評価されるとgeneratorオブジェクト(gen)を返す。で、genのnextメソッドが呼ばれた時(gen.__iter__()は恐らく自分自身を返しているので、gen.__iter__().next() は gen.next()と同じ)、初めて関数の中身を評価するのだけれども、yield文(returnのように値を返す)を実行した時点の状態で凍結し、次回にnextメソッドが呼ばれるまでその状態を保持している。だから、StopIteration例外が発生するまではwhile 1:ループの中にいることになる。
nextメソッドの呼び出しと例外の発生が隠蔽されて、頭の中でループを追いかけている最中にどこで凍結しているのか分からなくなっていたのが理解しづらかった原因らしい。で、rdflibのソースコードが異常にシンプルなのにさっぱり意味不明だったのはこのgeneratorが原因だったわけで、ようやく色々弄れるようになった。
というか検索用にPython。