いろいろあって Jython を Ubuntu にインストールしようとしたら、失敗した。
Jython 2.1 on java1.4.2-02 (JIT: null) Exception in thread "main" java.lang.UnsupportedClassVersionError: org/gnu/readline/ReadlineLibrary (Unsupported major.minor version 49.0)
とか言われる。「サポートしてないバージョンだよ〜」っていうエラーだということは分かるけど、どうしたらいいのか分からない。悲しいくて悔しい。

2007.11.29 Thu l Python l COM(0) TB(0) l top ▲

http://www.dodgson.org/omo/t/?date=20070519に書いてあるとおりにやったら、簡単に Mercurial を CGI で動かすことができた。すごい。

英語のマニュアルを見ながらやってて、 push するときにどうやって認証をつけたらいいのかが分からなくて悩んでいたけど、助かった。その記事にしたがって、Basic認証をつけた。ちなみにその手順は version 0.9.5 でもまったく同じだった。

そういうわけで、熊野古道のリポジトリができた。

2007.11.29 Thu l Python l COM(0) TB(0) l top ▲

熊野古道(とYahoo!API)を使って何か(おっぱいとか、おっぱいとか)を集めるスクリプトを書こうと思ったけれど、力尽きた。だれかちゃちゃっと作ってくれないかな〜。とりあえず XPath をもとにして抽出する処理が必要だよな〜。とか思ったけど、眠いから寝る。朝日がまぶしい。

2007.11.22 Thu l Python 熊野古道 l COM(0) TB(0) l top ▲

Plagger っぽいものを Python で。「熊野古道」というエントリーから始まった熊野古道プロジェクトなんですけど、(同時にそのエントリーでオレ様言語プロジェクトで終わったんだけど)その「熊野古道」がちっとも Plagger っぽくなりませんね。残念です。 Plagger っぽくならない理由はいくつかあるんだけど……とりあえず思いつく限りに列挙してみます。

  • 個人的な趣味だけど、並列に計算ができることを重視したい
  • できれば複数のマシンで並列に動作してほしい
  • そもそも Plagger をよく知らない…
  • っていうかそもそも Perl が読めない……

もちろん、原因は小さい順に並んでいます。これじゃぁ Plagger っぽいものはできないはずです。でもそれじゃぁ悔しいので、なんとか Plagger のソースを眺めて、それっぽいものを作ってみました。冗談じゃなくて、ほんとに Perl が読めなくて、なんとなく雰囲気で sub は関数定義だろ?とか、use base なんとか〜は継承だろ?とか、{なんとか => なんとか} はハッシュだろ?とか勘で読めるあたりまでしか読んでないので、内部の構造とかはたぶん全然違います。ごめんなさい。とりあえず分かったことは、 Plagger を起動すると、 bootstrap -> run って順に入っていって、run でいろいろとホックした関数を呼び出してるってことだけです。ほんとごめんなさい。

でも、とりあえずそれっぽいものができたんで、よかったらどうぞ。眠い… http://kawasaq.from.tv/blog/Python/Kumanokodo/kumanokodo.html

2007.11.22 Thu l Python 熊野古道 l COM(0) TB(0) l top ▲

ちょっと寝たら、昨日考えてたのがちょっと解決。関数を入れ子にしてデータを渡せばいいのか。

こんな感じの内容で /home/kawasaq/funcdir/multi.py を作る。
dim = 0

def get_proc(args):
    def times(params):
        params[""] = int(params[""])*args["times"]
        return params
    return dim, times

で、proc("multi", times=2) すればパラメータを2倍、proc("multi", times=3) すればパラメータを3倍する処理ができる。あとは前といっしょ。機能が増えたのにソースがあんまり長くならなくてうれしい。ついでに、文字列を渡した時の処理を追加できるようになった。

ソースはこちら http://d.hatena.ne.jp/kawasaq/20071120

2007.11.20 Tue l Python l COM(0) TB(0) l top ▲

iGoogle で「recommendations」というタブを作ったことある?これを作ったら、自分にあったおすすめの GoogleVideo とか News とかをグーグルが選んで表示してくれるよ。おもしろいからやってみて!

ちなみに、「Python」っていうタグを作ってみたら、表示されたガジェットの中に The Django weblog があったよ。今や Python といえば Django ってぐらいの人気なんだね。Ruby で言うところの Ruby on Rails みたいな感じなのかな。今ためしに「Ruby」ってタグを作ってみたんだけど、表示された9つのガジェットのうち3つのタイトルに Ruby on Rails が含まれていたよ。Ruby の世界での RoR 人気はすごすぎるな〜。

2007.11.19 Mon l ちょっと前に読んだもの l COM(0) TB(0) l top ▲
「なんか便利な言語を作りたいなー」→
「うぅ〜ん。難しい…」→
(友達が実家の和歌山にプチ帰省)→
「和歌山といえば熊野古道だな〜」→
「熊野の神様、アイデアください!」→
「では授けよう。」→「わぁ〜い」
という流れで神様から授かった言葉は、「大社を参拝道でつないで熊野古道しなさい」だけ。
それに基づいて書いたのが「熊野古道」というフレームワークなんだけど、やっぱり基本的を部分を適当に書きすぎたせいで、どうにも使えないツールになってしまっていた。でも自分で作っておいてそういうことを言うのはあまりにも無責任なので、きちんと書き直してみた。

まず、 funcdir/square というファイルに、↓こんな感じで2乗を返す関数を書いておく。
### Kumanokodo Script Version 0.0

### Name
square
### End Name

### Dimension
0
### End Dimension

### Function
def func(params):
params[""] = int(params[""])*int(params[""])
return params
### End Function
### End Kumanokodo Script Version 0.0


で、熊野古道の簡単な文法。
その1 文字列は関数呼び出し。
>>> run("square", "2")
{'': 4}

その2 リストは関数を結合して実行
>>> run(["square", "square"], "2")
{'': 16}

その2の補足 これは上と同じ
>>> run([["square"], ["square"]], "2")
{'': 16}

その3 タプルは関数を並列に実行
>>> run((["square"], ["square", "square"]), "2")
[{'': 4}, {'': 16}]

その4 辞書は、全てのパラメータの引数を変更
 この例では、全てのパラメータに対して parameter["answer"] = parameter['']が実行される
>>> run([(["square"], ["square", "square"]), {"answer": "p['']"}], "2")
[{'': 4, 'answer': 4}, {'': 16, 'answer': 16}]

以上。
そんな熊野古道のソースはこちら
http://d.hatena.ne.jp/kawasaq/20071118/1195408934

run 関数の引数の部分を別のファイルから読み込んで実行できれば、熊野古道スクリプト言語の完成…になるはずだけど、それはそのうち作るとして、今は保留。Plagger だったら、熊野古道で言う関数の部分に、引数に対する関数の振る舞いを変化させるための引数(まどろっこしいな)が渡せるらしいから、それもそのうち作るとしようか。でも YAML にはリストとタプルの区別がないから…また文法のところから考え直し?えぇ〜。熊野の神様、助けてください…。

ま、とりあえず、前より安心して熊野古道できるようになりました。という報告です。
2007.11.19 Mon l Python 熊野古道 l COM(0) TB(0) l top ▲

いま尊敬しているひとランキングNo.1のゆーすけべーさんのブログの過去ログを眺めていたら、すごいことを発見してしまった。すごいことが書かれているのは、この記事の下の方。最初に見た時は、下のスクリーンキャプチャまで見てなくて気付かなかったのだけれど、今気づきました。グーグルで「おっぱい 画像 ダウンロード」を検索すると、私が書いたこの記事が第2位に表示されてます。わぁ〜い!!しかもゆーすけべーさんがクリックしてくれてます!わぁ〜いわぁ〜い!Yahoo API でおっぱいを集めてくるプログラムを書いてよかった。1位はもちろん、ゆーすけべーさんのブログです。すごい!

それに気づいて今、あらためてGoogle Analyticsで見てみたら、この記事が書かれた11月6日以降に「いかにして効率よく大量のおっぱい画像をダウンロードするか」とか「おっぱい 画像 ダウンロード」で検索して来たひとが急増してました。実はひそかに心配していたのです。おっぱい画像を求めている人の邪魔をしてしまったのではないか…と。そういう人にとって私のサイトはスパム以外の何物でもないはずですから。でも、これで安心しました。

まぁおっぱいの話は置いといて、Python は見た目も美しく、いじっていて楽しい言語です。「おっぱいのダウンロードで Python を知りました。」とか、「最初に動かしたのは Hello World じゃなくておっぱいのダウンロードです。」でもいいじゃないですか。口に出しさえしなければ。というわけで、「おっぱい 画像 ダウンロード」で検索した人にも、Python を使ってみてほしいと思う今日この頃です。

2007.11.17 Sat l ちょっと前に読んだもの l COM(0) TB(0) l top ▲

今、自分専用の英単語帳を作っているところ。というか、今ほぼ完成したところ。自分専用の英単語帳がなぜ必要なのか。それは、人によって覚えると便利になる英単語が違うからだ。

当たり前のことだが、英語の文章を速く読むときには、使われている単語についてよく知っていることが重要だ。単語の意味を知っていれば、辞書を引く手間が省けるだけでなく、単語をざっと眺めるだけでどういう内容かが大体わかるようになるからだ。では、どの単語から覚えていけばいいのだろうか。TOEIC で高得点を目指しているなら話は簡単だ。「TOEIC でよく使われる英単語 5,000」みたいな本を買って調べればよい。じゃぁ生物学の論文を読むには、どの単語から覚えていけばよいだろうか。生物学の論文と、NYTのニュースを集めてきて、分子生物学の論文にだけ現れる単語をリストすればいい?たしかにそうだ。でも、このアプローチは、最適ではない。生物学と一口に言っても、生物学はさらに細分化することができる。生物物理学、生物倫理学、分子生物学といった具合に。そしてほとんどの場合、必要なのは細分化されたそれぞれに最適化された単語帳だ。これをすべての分野でやり始めると、とても大変なプロジェクトになるだろう。

な〜んて、海外のブログを翻訳したような文章を書いてみたけど、特に意味はないです。ハイ。でも自分の中で、「自分専用の英単語帳を作る」プロジェクトがスタートしたのはホントです。とりあえず自分が検索した英単語のログを、今日から取り始めることにしました。具体的には、Dictionary Tooltip で調べた単語と、それを調べた日時のログを記録するだけですが…。しばらく自分でやってみて、便利そうだったらそのうち公開します。

それにしても、こういうログを集めるプログラムが30分ちょっとで書けるなんて、Python & MySQL の力はすごい。翻訳っぽい文章を書く方が、よっぽど時間がかかった気がする。Django の力も借りればもっと簡単に書けるんだろうけど、さくらのレンタルサーバだと結局CGIで動かすことになるから、なんかやる気が起きないんだよなぁ…。

2007.11.17 Sat l Python l COM(0) TB(1) l top ▲

私は Python という言語を勉強しています。彼女とは関係ありませんが、私も、集中を改善する必要があると思います(._.')
2007.11.16 Fri l ちょっと前に読んだもの l COM(0) TB(0) l top ▲

帰り支度をするまえにライブドアのページで電車の運行状況を調べておくと幸せになれる気がします。というわけで、席を立つ前にこんなスクリプトを回しています。「熊野古道」風に書くとこんな感じです。まぁ、普通にデータを引っ張ってきてるだけですが。

### Kumanokodo Script Version 0.0

### Name
livedoorTraffic
### End Name

### Dimension
0
### End Dimension

### Function
def func(params):
    url = "http://transit.livedoor.com/traffic_info/kantou"

    from urllib import urlopen
    from lxml import etree

    parser = etree.HTMLParser(remove_comments=True, remove_blank_text=True)
    html_page = etree.parse(urlopen(url), parser)
    root = html_page.getroot()

    trafficinfos = root.xpath(".//*[@class='trafficinfo']")
    res =[]
    while trafficinfos:
        r = {}
        r["date"] = trafficinfos.pop(0).text
        r["line"] = trafficinfos.pop(0).text
        r["news"] = trafficinfos.pop(0).text
        res.append(r)
    return res
### End Function
### End Kumanokodo Script Version 0.0
こういのを作っておいて、この間の熊野古道をインポートして
from kumanokodo import *
res = run(("livedoorTraffic",),{})
for i in filter(lambda x: not x["news"].find(u'ほぼ平常通り運転しています。') , res):
    for key, val in i.items():
        print key,":", val
    print "**********"
っていうスクリプトを動かすと、「ほぼ平常通りに運転しています」じゃない路線について教えてくれます。こういう細々としたスクリプトをうまく熊野古道でまとめたいんだけど、書き直すのは、意外と面倒。
2007.11.15 Thu l Python 熊野古道 l COM(0) TB(0) l top ▲

自己紹介工場ってサイトを mixi のキーワードランキングで知った。「自分がちょっとした目的のために作ったプログラムを日記のように記録していったら、これは実行可能な自己紹介になるな〜」と思っていたところだったので、もうそういうサービスがあるのかと思ってしまった。もちろん違った。残念。

ちなみに、そのサービスによると私は、「遅刻しても気付かれないかわさっくです。よろしく。」

2007.11.15 Thu l Python 熊野古道 l COM(0) TB(0) l top ▲

昨日、半分寝ながら書いた自分のエントリー「日記のようにプログラムを書く」を読み直す。我ながら、馬鹿なことを書いた。「日記のようにプログラムを書く」なんて無理だ。

でも毎日の日記を実行可能にして、「この仕事は昨日やったから、今日からその仕事はコンピュータに任せることにしまいた。詳しくは昨日の日記を見てください」って言えたらステキだよね。プログラミングの原則であるDRY(Don't Repeat Yourself)を人生に適用すると、こんな感じになるのかなぁと思う。そのうち、「その仕事はほげほげさんが先週やってたから、詳しくはほげほげさんの日記を見てください。」って感じでコンピュータが使えるようになったり……したらいいなぁ。

まぁ私だけの力でそういうブログを作ることができるかどうか知らないけれど、なんとかそれっぽいものを書いてみたりしてる。オレ様言語を作ろうと思ったのもその一環だし、この間の「熊野古道」もそう。でも、なかなかハードルが高い。だからこういうことを書いておけば、誰かがインスパイアされて作ってくれたりしないかと期待してみたり。

それはともかく、とりあえず今日作ったものについて。Pickle 化できるもの、できないものに書いたとおり、分散で処理するにはソースコードを渡すしかない。でも、どうせマシン間でソースコードをシェアするのなら、人間同士でもシェアした方がいいんじゃないって思って方向転換中。今考えているのは、こんなの↓

### Kumanokodo Script Version 0.0

### Name
hatenaDiaryList
### End Name

### Dimension
0
### End Dimension

### Function
def func(params):
    from urllib import urlopen
    from lxml import etree

    parser = etree.HTMLParser(remove_comments=True, remove_blank_text=True)
    html_page = etree.parse(urlopen("http://d.hatena.ne.jp/diarylist"), parser)
    root = html_page.getroot()

    list_plain = root.xpath(".//ul[@class='list-plain']")[0]
    res = []
    for li in list_plain.getchildren():
        r = {}
        r["date"] = li.text
        
        if len(li.getchildren()) == 3:
            r["private"] = 1
            img , a , priv = li.getchildren()[:3]
            r["hatenaid"] = img.get("alt")
            r["title"] = a.text
        else:
            r["private"] = 0
            img , a = li.getchildren()[:2]
            r["hatenaid"] = img.get("alt")
            r["title"] = a.text
            
        res.append(r)
    return res
### End Function
### End Kumanokodo Script Version 0.0
を書いて適当な名前(hatenaDiaryList)で保存しておけば、「熊野古道」がその名前のファイルを見つけてきて実行してくれる、というもの。ちなみにこれは、はてなダイアリーの新着ダイアリーのページを見て、

[{"date":(更新時間), "title":(日記のタイトル),"hatenaid":(はてなid)}, ...]

なリストを返してくれる関数です。

「hatenaDiaryListを実行して、結果をくれー!」って叫んだら、自動でこの関数をブログから探してきて実行して結果を返してくれたらいいのに。あ〜でもあんまりセキュアじゃないし、バージョンアップとかやりにくそうだな〜。まぁいっか。ちなみに、上のファイルを /home/kawasaq/kumanokodo/func/hatenaDiaryList に保存しておいて、次のプログラムを実行すると、新着ダイアリーのうちプライベートでないダイアリーの一覧を作ります。ただ、locals()とかの使い方がよくわからないんで、環境によってはもしかしたらうまく動かないかも…。

func_dir = "/home/kawasaq/kumanokodo/func/"

class Task(object):
    def __init__(self, params):
        self.params = params
    def __getitem__(self, key):
        return self.params[key]
    def __getattr__(self, attr):
        return self.params[attr]
    
import re
r_name =  re.compile(r"n#+s*Names*n(.*?)n#+s*End Name", re.DOTALL)
r_function =  re.compile(r"n#+s*Functions*n(.*?)n#+s*End Function", re.DOTALL)
r_dimension = re.compile(r"n#+s*Dimensions*n(.*?n)#+s*End Dimension", re.DOTALL)
r_version = re.compile(r"^#+s*Kumanokodos+Scripts+Version+(d+.d+)n")

def getFuncData(name):
    try:
        func_file = file(func_dir + name).read()
        return Task({"func":r_function.search(func_file).group(1),
                     "dim":int (r_dimension.search(func_file).group(1)),
                     "version":int (r_version.search(func_file).group(1)),
                     "name":int (r_name.search(func_file).group(1)),
                     })
    except:
        raise

def getDim(data):
    if isinstance(data, list) or isinstance(data, tuple):
        return 1 + getDim(data[0])
    else:
        return 0

def run(tasks, params):
    res = params
    for task in tasks:
        if isinstance(task,basestring):
            task_obj = getFuncData(task)
            exec task_obj.func in locals()
            ns = locals()

            def do(d):
                if getDim(d) > task_obj.dim:
                    return filter(lambda x: x, map(do, d))
                else:
                    return ns["func"](d)
            res = do(res)
        else:
            print "Not implemented"
            raise NotImplementedError
    return res

if __name__ =="__main__":
    for diary in run(("hatenaDiaryList",), {}):
        if diary["private"] == 0:
            print '<a href="http://d.hatena.ne.jp/%(hatenaid)s>%(title)s</a><br>' % diary
2007.11.14 Wed l Python 熊野古道 l COM(1) TB(0) l top ▲

今さらだけれど、いろんな人がブログを書いているなぁと思った。アイドルとか、寿司屋のおかみさんとか。そういういろんなブログを読んでいると、人それぞれ伝えたいことや表現したいことは違うんだなぁと、これも今さらだけど思う。とはいえ、それらのほとんどの文章は、ブログ以外のメディアの文章に比べて拙い。でもおもしろい。ブログの文章だと、なぜか書いている人の感情が強く伝わってくる感じがする。

ブログを書いている人の中には、「私はパソコンとかインターネットとかよくわからないけど、(流行ってる|おもしろそうだ|知合いができそうだ|主張したいことがある)からブログやってるだけで、今でもパソコンについてよくわかりません」って人も多いだろうと思う。でも、それでもそんな人が書いたものでも読むことができて、その人について知ることができるっていうのは、すごいことなんじゃないか?

そもそもインターネットが始まったばかりのころって、インターネットを使ってるのはそういう専門家ばっかりだった…と思う。そんな時代だと、ネットを使ってる人が考えていることなんてみんな似たり寄ったりで、そんな人たち同士で情報交換したってしょうがない気がする…。

昨日、徹夜したせいで眠くて全然まとまらないけれど、要するに言いたいのはこういうこと。いまプログラムを書いて公開しているのは、いわゆるギークと言われている人たちだ。これを文章に例えて言うなら、ブログとかが全然まだ世の中になくて、「今、日記文学を書いて公開しているのは、いわゆる貴族の女性だけだ」というような状態だ。誰もがプログラムを書いて公開できるようになるのに、あとどれくらいの時間がかかるのかは分からないけれど、そうなったらきっと楽しい。「うまく書けないけどだけれど、コンピュータについて興味もないけれど、(流行ってる|おもしろそうだ|知合いができそうだ|主張したいことがある)から、とかその他いろんな理由でプログラムを書いて公開しているけれど、今でもパソコンについてよくわかりません」って感じでプログラムを書くような時代。プログラミング言語ほど、日々の生活の中で自動化してほしいことを表現するのに適した言語はない。だからそんな時代がきたら、加速度的に世の中が効率化されていって、面倒なことはこの世からなくなる。すてきね。乾杯。

眠い。寝る。

2007.11.14 Wed l Python 熊野古道 l COM(0) TB(1) l top ▲

このあいだざっと作った「熊野古道」は、基本的にはオブジェクトを map 関数で次々に処理していく感じになるので、複数のマシンを使って分散処理するのも簡単だと思っていた。オブジェクトを Pickle 化して、RPC か何かで別のマシンに送って、そこで実行させて、結果を受け取れば簡単にできると思っていた。

すべて勘違いだった。まず、「Pickle 化できるものとできないものがある」ということを知らなかった。3.14.4 何を pickle 化したり unpickle 化できるのか? - Python ライブラリリファレンスによると、

以下の型は pickle 化できます:
  • None、 True、および False
  • 整数、長整数、浮動小数点数、複素数
  • 通常文字列および Unicode 文字列
  • pickle 化可能なオブジェクトからなるタプル、リスト、集合および辞書
  • モジュールのトップレベルで定義されている関数
  • モジュールのトップレベルで定義されている組込み関数
  • モジュールのトップレベルで定義されているクラス
  • __dict__ または __setstate__() を pickle 化できる上記クラスのインスタンス (詳細は 3.14.5 節を参照してください)

つまり、クラスのインスタンスは Pickle 化できない。クラスのインスタンスなんてハッシュにデータと関数を詰め込んだだけなんだから、ハッシュが Pickle 化できるんならインスタンスも Pickle 化させてくれよ〜とか思っていたら、クロージャは Pickle 化できない。普通に定義した関数は Pickle 化できるけど、

組み込みおよびユーザ定義の) 関数は、値ではなく ``完全記述された'' 参照名として pickle 化されるので注意してください。これは、関数の定義されているモジュールの名前と一緒と併せ、関数名だけが pickle 化されることを意味します。関数のコードや関数の属性は何も pickle 化されません。従って、定義しているモジュールは unpickle 化環境で import 可能でなければならず、そのモジュールには指定されたオブジェクトが含まれていなければなりません。そうでない場合、例外が送出されます 3.5 。

ということなので、普通に定義した関数も、Pickle はできるけれども、それを別の環境に送ってしまうと呼び出せない。リファレンスを読んでいろいろ考えた結果、関数そのものを別の環境へ送るには、ソースを送って実行するしかない。ソースを受け取れば、こんな感じで定義して呼び出せる。

given_func = """def f(n):
    m = n*n
    return m"""
exec(compile(given_func, "", "exec"))
print f(10) # => 100

呼び出せるけれども、でも分散処理をするためにソースをシェアしなくちゃいけないっていうのも、なんだかなぁという感じ。うーん。この方法を使っても、たとえばクロージャを渡して実行することはできない。クロージャを「ローカル変数を値で置き換えた自分自身のソースコードを返す関数」として定義すれば、文字列に変換して送りつけることも可能だ。可能だけれど、やっぱりやっぱり、なんだかなぁという感じはなくならない。なんかいい方法はないかなぁ。うぅ〜ん……。

2007.11.13 Tue l Python 熊野古道 l COM(0) TB(0) l top ▲

追記:ここで書いているプログラムをきれいに書き直した物が、こっちにあります。

LLSpirits 以来、オレ様言語熱が続いていたのだけれど、今日、ついに挫折した。

挫折したというか「わざわざオレ様言語を作らなくても欲しい機能を実現することができるんじゃないか」と思い始めて、その方向で2時間ほどいじってたらそれっぽいものができてきて、「それPlaggerでできるよ(私はPlaggerについてよく知らないけど)」的な物に収束してしまった。(そしてオレ様言語熱も終息してしまった。)だから、とりえあえず今日書いたものをここで公開して、私のオレ様言語プロジェクトは休止ということで。Python で Plagger 的なもの。題して「熊野古道


熊野古道のコンセプト
  • 高機能な( Unix でいう)パイプのようなものが使える(ただし受け渡すデータはリストとハッシュ)
  • 扱うデータの次元を増やしたり減らしたりできる。
  • 並列で処理してくれる(これは今後の目標)

熊野古道の簡単な使い方

「なんでもやってくれるプログラム」を作るのではなく、Unix のシェルのように、コマンドをつなげることで目的の処理をやってくれるプログラムを作ることを目指します。たとえばこんなふうに。

from Kumanokodo import *
chain = (readTextFile, cat)
k = Kumanokodo(chain)
result = k.run(["1.txt", "2.txt"])
print result[""]

このプログラムで Kumanodoko は2つのファイル( 1.txt, 2.txt) をそれぞれ開いて、ファイルの内容を結合して、その結果を result[""] に入れて返します。つまり、裏では

result = cat(readTextFile("1.txt"), readTextFile("2.txt"))

こんな感じで処理しています。ただし、readTextFileはファイルの内容を{"":(ファイルの内容)}という形式で cat に渡し、cat もまた、結合した結果を {"":(結合した結果)}という形式で返します。

次に、1行目が宛先メールアドレス、2行目がタイトル、3行目以降が本文になっているファイルを開いて、それを送信するプログラムを考えます。メールを送信するには sendmail を使います。このコマンドは{"smtpserver":"SMTPサーバ名", "from":"送信元アドレス", "to":"送信先アドレス", "subject":"件名", "":"本文"} というハッシュを引数に要求します。したがってこれを使ってメールを送るには、readTextFile で得られた結果を編集して sendmail に渡せば OK です。結果を編集するには Sampaido コマンドを使います。こんなふうに

chain = (readTextFile,
         Sampaido({"to"     :lambda params: params[""].split("n")[0],
                   "subject": lambda params: params[""].split("n")[1],
                   ""       :lambda params: "n".join(params[""].split("n")[2:])}),
         sendmail({"from":"おれさま@げんごjp", "smtpserver":"めーる。おれさま@げんごjp"}))
k = Kumanokodo(chain)
p = k.run(["1.txt", "2.txt", "3.txt"])

そうそう、コマンドにパラメータを渡す方法は1.コマンド列を作るときに直接指定するか、2.前のコマンドからもらうか、2通りあります。この場合、"from" と "to" の値は直接渡して、"to" と "subject" と ""の値はreadTextFile の出力をSampaidoで編集して渡しています。

なんかソースコード本体より説明の方が長くなってきたので、詳しくはソースを読んでください。と言いたいところだけれど、もうちょっとだけ。コマンドを作るには、Taisha クラスを継承して、run(self, params)に必要な処理を書きます。params は、デフォルトではハッシュを想定していますが、self.dim を変更すればハッシュのリストを受け取ることもできます。ちなみに dim=1 ならハッシュのリスト、 dim=2 ならハッシュのリストのリスト、dim=3 ならハッシュのリストのリストのリスト、という感じに。

というわけで、大社(コマンド)を参拝道(パラメータ編集)でつないで熊野古道しちゃってください。途中で事件や事故に巻き込まれても、私は責任を持ちませんが。

見やすいソースはこちら http://d.hatena.ne.jp/kawasaq/20071110
import copy, types

class Taisha(object):
    dim = 0
    def __init__(self, params = {}):
        self.params = params
    def run(self, params):
        res = copy.copy(params)
        return res
    def _getAttr(self, params, key):
        if params.has_key(key):
            return params[key]
        elif self.params.has_key(key):
            return self.params[key]
        else:
            raise KeyError, key

class Sampaido(Taisha):
    def run(self, params):
        res = copy.copy(params)
        for key, val in self.params.items():
            if isinstance(val, basestring):
                res[key] = params[val]
            elif type(val) is types.FunctionType:
                res[key] = val(params)
            else:
                raise KeyError, key
        return res
    
class Kumanokodo(object):
    def __init__(self, tasks):
        self.chain = tasks

    def _getDim(self, data):
        if isinstance(data, list):
            return 1 + self._getDim(data[0])
        else:
            return 0
    def _run(self, task, data):
        if type(task) is types.TypeType:
            obj = task()
        else:
            obj = task
        def func(d):
            if self._getDim(d) > obj.dim:
                return filter(lambda x: x, map(func, d))
            else:
                return obj.run(d)
        return func(data)
    
    def run(self, data):
        res = data
        for task in self.chain:
            res = self._run(task, res)
        return res
    
class readTextFile(Taisha):
    def run(self, params):
        filename = params if isinstance(params, basestring) else params[""]
        try:
            res = {"": file(filename, "r").read()}
        except:
            res = None
        return res

class cat(Taisha):
    dim = 1
    def run(self, params):
        msg = "".join([unicode(x) if isinstance(params, basestring) else unicode(x[""]) for x in params])
        return {"": msg}

class sendmail(Taisha):
    def _mail(self, params):
        msg = "From: %srnTo: %srnSubject: %srnrn" % (params["from"], params["to"], params["subject"])
        msg += params[""]
        import smtplib
        server = smtplib.SMTP(params["smtpserver"])
        server.sendmail(params["from"], params["to"], msg)
        server.quit()
            
    def run(self, params):
        args = {}
        args["from"] = self._getAttr(params, "from")
        args["to"] = self._getAttr(params, "to")
        args["subject"] = self._getAttr(params, "subject")
        args["smtpserver"] = self._getAttr(params, "smtpserver")
        args[""] = params[""]
        self._mail(args)
        return params
2007.11.10 Sat l Python 熊野古道 l COM(0) TB(1) l top ▲
いつのまにか、はてなダイアリー市民になってました。ただ、だからといって、このブログをやめるつもりはありません。そもそもはてなダイアリー市民になったのだって、偶然です。たまたまです。はてなってどんな感じなのかな〜って試してたら、いつのまにかはてなダイアリー市民にされちゃっただけです。
でもまぁ、せっかくはてなダイアリー市民になったのだから、それっぽいことをやってみたいと考えている今日この頃。まずは、はてなダイアリー市民なる人々が何をやってるのかについて調べてみようと思っています。
ちなみに私はというと、まだ大学の研究室にいます。研究室で、Modeller (タンパク質のモデリングをやってくれるツール。Python 2.5 で動かせるよ)を眺めているところ。こんなんやってるはてな市民って誰か他にいるのかなぁ。
2007.11.07 Wed l 未分類 l COM(0) TB(0) l top ▲
ちょっと間が空いたけど、おっぱい集めスクリプトを、 Threading を使って書き直してみたものがこちら。
# -*- coding:utf8 -*-

from urllib import quote, urlopen
from lxml import objectify
import time, copy

import threading

imageNum = 0
totalResultsAvailable = 2

data = {}
data["appid"] = "****"
data["query"] = "おっぱい"
data["type"]  = "all"
data["results"] = 50
data["format"] = "any"
data["adult_ok"] = 1
data["start"] = 1

class download(threading.Thread):
    def __init__(self, e, n):
        threading.Thread.__init__(self)
        self.e = e
        self.n = n

    def run(self):
        img = urlopen(unicode(self.e.Url))
        if img.info().gettype()[:5] != "image":
            img.close()
            img = urlopen(unicode(self.e.Thumbnail.Url))
        if img.info().gettype()[:5] == "image":
            localfile = file(unicode(self.n)+"."+unicode(self.e.FileFormat),"wb")
            localfile.write(img.read())
            img.close()
            localfile.close()

thread_list = []
try:
    while data["start"] < totalResultsAvailable:
        res = urlopen( "http://api.search.yahoo.co.jp/ImageSearchService/V1/imageSearch?"
                   + "&".join(["=".join([key, quote(str(value))]) for key, value in data.items()]))
        root = objectify.fromstring(res.read())

        totalResultsAvailable = int(root.get("totalResultsAvailable"))
        for e in root.Result:
            print imageNum
            thrd = download(copy.copy(e), imageNum)
            thrd.start()
            thread_list.append(thrd)
            imageNum += 1
        data["start"] += data["results"]
        thread_list = filter(lambda thrd: thrd.isAlive(), thread_list)
except:
    pass

for thrd in thread_list:
    if thrd.isAlive():
        thrd.join()

とくに難しくない。各画像をダウンロードしてローカルに保存する処理をマルチスレッド化して、ダウンロードするおっぱい画像が大きくても待たされないようにしただけ。詳しいことは 7.5 threading -- 高水準のスレッドインタフェース に書いてある。

で、それを参考にして Twisted を使って書き直してみたのが次なんだけど、これはなんか間違ってるっぽい。全ての処理が終わっても、延々と待たされる。しかたがないから kill -9 するしかなかった。ここからどうしていいかわからなくなってしまったせいで、なかなか更新できなかった。まぁ、飽きちゃったっていうのも、ちょっとあるけど。どうしたらいいだろうなぁ…。

それにしても、Twisted のドキュメントってなんか不親切じゃない?そうでもない?英語が苦手な俺のせい?うぅ〜ん。

# -*- coding:utf8 -*-

from urllib import quote, urlopen
from lxml import objectify
import time, copy

from twisted.internet import reactor, threads, defer

imageNum = 0
totalResultsAvailable = 2

data = {}
data["appid"] = "***"
data["query"] = "おっぱい"
data["type"]  = "all"
data["results"] = 50
data["format"] = "any"
data["adult_ok"] = 1
data["start"] = 1

def run(e, n):
    def task():
        img = urlopen(unicode(e.Url))
        if img.info().gettype()[:5] != "image":
            img.close()
            img = urlopen(unicode(e.Thumbnail.Url))
        if img.info().gettype()[:5] == "image":
            localfile = file(unicode(n)+"."+unicode(e.FileFormat),"wb")
            localfile.write(img.read())
            img.close()
            localfile.close()
        return None
    return task

def stop(arg):
    reactor.stop()
    return None

try:
    while data["start"] < totalResultsAvailable:
        res = urlopen( "http://api.search.yahoo.co.jp/ImageSearchService/V1/imageSearch?"
                   + "&".join(["=".join([key, quote(str(value))]) for key, value in data.items()]))
        root = objectify.fromstring(res.read())

        totalResultsAvailable = int(root.get("totalResultsAvailable"))
        for e in root.Result:
            print imageNum
            d = threads.deferToThread(run(copy.copy(e), imageNum))
            d.addCallback(stop)
            imageNum += 1
        data["start"] += data["results"]
except:
    pass

reactor.run()
2007.11.02 Fri l Python l COM(0) TB(0) l top ▲

How To Build a Game In A Week From Scratch With No Budget を読んだ。

この記事は2005年のものらしい。彼がゲームを作るのに使ったのは、

  • Python 2.3
  • PythonWin
  • PyGame
  • Py2Exe
  • Gimp 2.0
  • ペイント
  • ネットで公開されているフリーのテクスチャ
  • Audacity
だけ。これだけのツールと40時間という時間だけで、彼はゲームを作ってしまった。

この記事の最後に筆者がこのプロジェクトをやって学んだことが10個書かれているが、その最後の文章は

The bottom line is this: If you want to develop games, nothing is stopping you. You can find the time. You don't need a big budget or fancy tools. You don't need a team of specialists. You don't need years of training. All you need is the will to make it happen. And that's the most important lesson of all.
で締められている。

2007.11.01 Thu l ちょっと前に読んだもの l COM(0) TB(0) l top ▲