2008年4月30日水曜日

Project Eulerをやってみた

このブログ記事をはてなブックマークに追加

ちょっと流行っているということで、Project Euler(プロジェクト・オイラー)(日本語Wiki)をやってみた。とりあえず3日で50問以上解いてみたが、結構面白いかも。問題自体は今までのところどれも簡単でコーディングを含めて1問につき数分もあれば解けるものばかり。しかし、すぐに解けてしまうので止め時が難しい。今後、難しくなっていくのかもしれないが。ただし、最適なアルゴリズムを考えて解こうとすると難易度は上がる。

これはプログラミングの勉強や教育にはいいかも。以前、学生にダイクストラ法を教えたことがあったけど、まさにそれを使う問題(Problem 67)も出てきた。

使用言語はC++にしている。しかし、1~2割程度はPythonで解いた。Pythonのワンライナーで解けるような問題だとついつい楽してしまうなぁ。一方、C++だと力技が結構効いたりと、使い分けの重要性を再認識した。

少しコメント。Problem 14はここのブログの自己紹介にあるコードが使える。Problem 19は、C言語のFAQで有名な以下のコードが使える。

int day_of_week(int y, int m, int d) // 0 = Sunday { static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; y -= m < 3; return (y + y / 4 - y / 100 + y / 400 + t[m - 1] + d) % 7; }

あとは、素数を扱う問題が多いからエラトステネスの篩を使うとちょっと楽になるかもしれない。

続きを読む...

2008年4月24日木曜日

Python: クイックソート

このブログ記事をはてなブックマークに追加

Pythonクックブックに載っている3行クイックソート。

def qsort(L): if len(L) <= 1: return L return qsort([lt for lt in L[1:] if lt < L[0]]) + L[0:1] + qsort([ge for ge in L[1:] if ge >= L[0]])

これは美しい。ぱっと見て(自分にとっては)意味がすぐに読み取れるところも素晴らしい。

さらに短く1行に。

q=lambda x:(lambda o=lambda s:[i for i in x if cmp(i,x[0])==s]:len(x)>1 and q(o(-1))+o(0)+q(o(1)) or x)()

打って変わって邪悪感が漂うコード。これは(自分にとっては)一目では意味が掴めない…。

ただし、どちらも実際のコーディングには用いるべからず。高速な組み込みのsortedを使うこと。

これらはすべてPythonクックブックから。こんな珠玉のコードのちりばめられたPython クックブック 第2版はPythonista必携の書といえる。



ところで自分はPythonクックブックの初版を(ついでに英語版も)持っているが、3行クイックソートのコードに意味不明な Ä という文字が入っている。これは \ の誤植。今の版では直っているのかな。

続きを読む...

2008年4月23日水曜日

Python: PaSoRiでSuicaの履歴を読み出す・その後

このブログ記事をはてなブックマークに追加

以前、PaSoRiでSuicaの履歴を読み出すという記事を書いたけど、酷いバグを見つけた。物販で購入時の時刻を間違えるというもの。言い訳になるが、つい最近まで古いSuicaを使っていて、駅の自販機などでSuicaが使えず、テストしていなかったのだ。申し訳ない。

と云うわけで、修正したソースを公開しておく。バグだけ修正しても面白くないので、少し改良した。以前は残高しか出なかったが、使用した金額も併せて表示するようにした。また、以下に示す出力のように日時が先頭に来るように変更した。等々。

2008年xx月xx日 ○○駅 ××駅 540円 4160円 改札機 運賃支払(改札出場) 2008年xx月xx日 ○○駅 +2000円 6160円 券売機等 チャージ 2008年xx月xx日 xx時xx分xx秒 買物 120円 6040円 自販機 物販

以下、ソースコード。まだテストが足りないのでバグがあるかもしれない。見つけ次第、訂正する。

#!/usr/bin/env python # -*- coding: shift_jis -*- """ read_felica.py version 0.02 by nox Suicaの履歴を出力するプログラム. """ from ctypes import * POLLING_ANY = 0xffff POLLING_SUICA = 0x0003 POLLING_EDY = 0xfe00 SERVICE_SUICA = 0x090f SERVICE_EDY = 0x170f # 端末種 TERMINAL = {3: "精算機", 4: "携帯型端末", 5: "車載端末", 7: "券売機", 8: "券売機", 9: "入金機", 18: "券売機", 20: "券売機等", 21: "券売機等", 22: "改札機", 23: "簡易改札機", 24: "窓口端末", 25: "窓口端末", 26: "改札端末", 27: "携帯電話", 28: "乗継精算機", 29: "連絡改札機", 31: "簡易入金機", 70: "VIEW ALTTE", 72: "VIEW ALTTE", 199: "物販端末", 200: "自販機" } # 処理 PROCESS = { 1: "運賃支払(改札出場)", 2: "チャージ", 3: "券購(磁気券購入)", 4: "精算", 5: "精算 (入場精算)", 6: "窓出 (改札窓口処理)", 7: "新規 (新規発行)", 8: "控除 (窓口控除)", 13: "バス (PiTaPa系)", 15: "バス (IruCa系)", 17: "再発 (再発行処理)", 19: "支払 (新幹線利用)", 20: "入A (入場時オートチャージ)", 21: "出A (出場時オートチャージ)", 31: "入金 (バスチャージ)", 35: "券購 (バス路面電車企画券購入)", 70: "物販", 72: "特典 (特典チャージ)", 73: "入金 (レジ入金)", 74: "物販取消", 75: "入物 (入場物販)", 198: "物現 (現金併用物販)", 203: "入物 (入場現金併用物販)", 132: "精算 (他社精算)", 133: "精算 (他社入場精算)" } def read_station_code(fname): global STATION_CODE STATION_CODE = {} data = [l.strip().split(",") for l in file(fname) if l[0] in ("0", "1", "2")] for d in data: STATION_CODE[tuple(map(lambda x: int(x, 16), d[0:3]))] = (d[4], d[5]) def read_felica(): flib = cdll.felicalib flib.pasori_open.restype = c_void_p pasori = flib.pasori_open() flib.pasori_init(pasori) flib.felica_polling.restype = c_void_p felica = flib.felica_polling(pasori, POLLING_SUICA, 0, 0) # Suicaを読む. # 履歴の読み出し. data = [] d = create_string_buffer(16) i = 0 while flib.felica_read_without_encryption02(felica, SERVICE_SUICA, 0, i, d) == 0: data.append(string_at(pointer(d), 16)) i += 1 flib.pasori_close(pasori) return data def parse_data(d, prev=-1): term = ord(d[0]) # 端末種. proc = ord(d[1]) # 処理. date = map(ord, d[4:6]) # 日付. year = (date[0] >> 1) + 2000 month = ((date[0] & 1) << 3) + (date[1] >> 5) day = date[1] & (1<<5) - 1 in_line = ord(d[6]) # 入線区. in_sta = ord(d[7]) # 入駅順. out_line = ord(d[8]) # 出線区. out_sta = ord(d[9]) # 出駅順. balance = map(ord, d[10:12]) # 残高. num = map(ord, d[12:15]) # 連番. region = ord(d[15]) # リージョン. print "%4d年%02d月%02d日" % (year, month, day), if proc in (70, 73, 74, 75, 198, 203): # 物販. hour = in_line >> 3 min = ((in_line & 7) << 3) + (in_sta >> 5) sec = (in_sta & 0x1f) << 1 print "%02d時%02d分%02d秒" % (hour, min, sec), print "買物", elif proc in (13, 15, 31, 35): # バス. out_line = map(ord, d[6:8]) out_sta = map(ord, d[8:10]) print "バス", else: if region == 0: if in_line < 0x80: area = 0 # JR線. else: area = 1 # 関東公営・私鉄. else: area = 2 # 関西公営・私鉄. if in_line not in (0xc7, 0xc8, 0x05): if STATION_CODE.has_key((area, in_line, in_sta)): print "%s駅" % STATION_CODE[(area, in_line, in_sta)][1], else: print "不明", if STATION_CODE.has_key((area, out_line, out_sta)): if not (area == 0 and out_line == 0 and out_sta == 0): print "%s駅" % STATION_CODE[(area, out_line, out_sta)][1], else: print "不明", account = (balance[1] << 8) + balance[0] charge = prev - account if prev < 0: print "---円", elif charge > 0: print "%d円" % charge, elif charge < 0: print "%+d円" % -charge, print "%d円" % account, if TERMINAL.has_key(term): print TERMINAL[term], else: print "不明", if PROCESS.has_key(proc): print PROCESS[proc], else: print "不明", print return account def main(): read_station_code("StationCode.csv") data = read_felica() prev = -1 for d in data[::-1]: prev = parse_data(d, prev) if __name__ == "__main__": main()

続きを読む...

2008年4月20日日曜日

Python: モンティ・ホール問題

このブログ記事をはてなブックマークに追加

モンティ・ホール(Monty Hall)問題とは以下のような問題のことだ。

「プレイヤーは、三つのドアを見せられる。ドアの一つの後ろにはプレイヤーが獲得できる車(アタリ)があり、一方、他の二つのドアには山羊(ハズレ)が入っている。ホストであるモンティは、それぞれのドアの後ろに何があるか知っているのに対し、もちろんプレイヤーは知らない。

プレイヤーはまず三つのドアの一つを選ぶ。次にモンティは他の二つのドアのうち一つを開け、山羊をみせる。そしてモンティはプレイヤーに、初めの選択のままでよいか、もう一つの閉じているドアに変更するか、どちらかの選択権を提供する。プレイヤーは、選択を変更すべきだろうか?」

(モンティ・ホール問題より一部修正)

続きを読む前に、まずは自分で正解を予想して頂きたい。


正解は、「ドアを変更すべき」なのだが、これに引っかかる人は多いらしい。10年ほど前にこの問題を聞いたときは、変更したほうが当たる確率が上がることに気が付いたが、ちょっと考えればそれは当たり前のようにも感じた。一緒に聞いていた人の一人は最後まで納得いかない様子で、こちらが即席で作ったのCのプログラムを実行してみせても信じようとはしなかった。

以下、Pythonでのプログラムを示す。

#!/usr/bin/env python # -*- coding: utf-8 -*- from random import choice DOORS = [1, 2, 3] # ドアの数. N = 1000 # 試行回数. win_picked, win_switch = 0, 0 for i in range(N): # ドアの一つに車を入れる. car = choice(DOORS) # ドアを選ぶ. picked_door = choice(DOORS) # 残りのドアで山羊の入っている方を開ける. open_door = choice(list(set(DOORS) - set([picked_door, car]))) # ドアを変更した場合. switch_door = choice(list(set(DOORS) - set([picked_door, open_door]))) # 車を当てた回数を数える. if picked_door == car: win_picked += 1 elif switch_door == car: win_switch += 1 print u"%d回ゲームを行い、車を当てた割合:" % N print u" ドアを変更しなかった場合: %f %% (%d)" % (100.0 * win_picked / N, win_picked) print u" ドアを変更した場合: %f %% (%d)" % (100.0 * win_switch / N, win_switch)

で、以下のような出力を得る。

1000回ゲームを行い、車を当てた割合: ドアを変更しなかった場合: 33.400000 % (334) ドアを変更した場合: 66.600000 % (666)

続きを読む...

2008年4月18日金曜日

Python: 続・簡単ステガノグラフィ…画像を画像に埋め込む

このブログ記事をはてなブックマークに追加

前回のなんちゃってステガノグラフィはあまりにも酷い出来だと思ったので、少しばかり改良してみた。画像データのアルファ値を利用していたのをRGBに置き換え、最下位ビットを利用する方法に変更した。アルファ値だと生データを見れば一発で怪しいと分かるが、RGBだと余程注意深くないと分からないだろう。また、入力データにはテキストだけではなく、バイナリファイルも指定できるようにした。ファイル名は保存されるので、隠したデータを取り出す際もファイル名の指定が不要だ。

使い方は前回と一緒。ただし、上記の理由で出力ファイルの指定はない。

で、上のネコの画像(kitten01.png)に下のモルモットの画像(guinea01.jpg)を隠してみる。

covgraph.py kitten01.png kitten01g.png guinea01.jpg

画像を隠し入れた画像(kitten01g.png)を下に示す。最初の画像と見た目はほとんど変わらない。

取り出すときは以下のようにする。

covgraph.py kitten01g.png

ステガノグラフィについての知識なんてほとんど持ち合わせていないので、これがどの程度有効かなんて分からないし、きっと世の中にはもっと効率の良いアルゴリズムなんかがあるのだと思うけど、気にしない。だってお遊びのプログラミングだから。作る過程でいろいろ考えることが楽しいのだ。

追記: 出力画像ファイルにはPNGかBMPを利用するのが良い。JPEGは非可逆圧縮により埋め込むデータが壊れるし、GIFはパレットの制限に引っ掛かるためだ。因みに、入力画像ファイルはどのフォーマットでも問題ないはず。また、このサイトはアップロードした画像を非可逆圧縮するので本記事の画像をそのまま利用してもデータを取り出すことはできません。

以下、ソース(covgraph.py)。

#!/usr/bin/env python # -*- coding: utf-8 -*- """covgraph.py by nox, 2008.4.17""" import sys, os, bz2, zlib from PIL import Image def embed_data(data, in_data, offset=0): for i in range(len(in_data)): for j in range(8): n = (i + offset) * 8 + j data[n] = chr((ord(data[n]) & ~1) | (ord(in_data[i]) >> j & 1)) return offset + len(in_data) def cover(input_image, output_image, hidden_data): """データを画像ファイルに埋め込む. input_image: 入力画像ファイル. output_image: 出力画像ファイル. hidden_data: 埋め込むデータもしくはデータファイル. """ filename = zlib.compress("") if os.path.isfile(hidden_data): filename = zlib.compress(hidden_data) hidden_data = file(hidden_data, "rb").read() bz2_data = bz2.compress(hidden_data) im = Image.open(input_image).convert("RGB") data = list(im.tostring()) size = 5 + 1 + len(filename) + len(bz2_data) if len(data) < size * 8 + 1: print >>sys.stderr, "Error: input data too large." sys.exit(1) offset = embed_data(data, "".join([chr((size >> 8 * i) & 0xff) for i in range(5)])) # length = 5 offset = embed_data(data, chr(len(filename)), offset) # length = 1 offset = embed_data(data, filename, offset) # length = len(filename) offset = embed_data(data, bz2_data, offset) # length = len(bz2_data) im.fromstring("".join(data)) im.save(output_image) def bin_to_string(data): return "".join([chr(sum([data[i+j] << j for j in range(8)])) for i in range(0, len(data), 8)]) def uncover(image_file): """画像ファイルからデータを抜き出す. image_file: データが埋め込まれた画像ファイル. 戻り値: ファイル名, データ. """ im = Image.open(image_file) im_data = list(im.tostring()) size = sum([ord(x) << 8 * i for i, x in enumerate(bin_to_string([ord(d) & 1 for d in im_data[:5*8]]))]) name_len = ord(bin_to_string([ord(d) & 1 for d in im_data[5*8:6*8]])) filename = zlib.decompress(bin_to_string([ord(d) & 1 for d in im_data[6*8:(name_len+6)*8]])) bz2_data = bin_to_string([ord(d) & 1 for d in im_data[(name_len+6)*8:size*8]]) hidden_data = bz2.decompress(bz2_data) return filename, hidden_data def main(args): if len(args) < 2 or len(args) > 4: print >>sys.stderr, u"画像ファイルにデータを埋め込む:" print >>sys.stderr, u"Usage: %s <input image> <output image> <data or data-file>" % os.path.basename(args[0]) print >>sys.stderr, u"input image: 入力画像ファイル" print >>sys.stderr, u"output image: 出力画像ファイル" print >>sys.stderr, u"data: 埋め込むデータ" print >>sys.stderr, u"data-file: 埋め込むデータファイル" print >>sys.stderr print >>sys.stderr, u"画像ファイルからデータを抜き出す:" print >>sys.stderr, u"Usage: %s <input image>" % os.path.basename(args[0]) print >>sys.stderr, u"input image: 入力画像ファイル" sys.exit(1) # 画像ファイルにデータを埋め込む. if len(args) == 4: cover(args[1], args[2], args[3]) # データが埋め込まれた画像からデータを取り出す. if len(args) < 4: filename, data = uncover(args[1]) if filename: # データをファイルに書き出す. print "Filename: %s" % filename file(filename, "wb").write(data) else: # データを標準出力に書き出す. print data if __name__ == "__main__": main(sys.argv)

続きを読む...

2008年4月16日水曜日

Python: 簡単ステガノグラフィ…テキストを画像に埋め込む

このブログ記事をはてなブックマークに追加

Pythonでなんちゃってステガノグラフィを作ってみた。画像データにテキストを埋め込んで、メッセージを隠してしまうというわけだ。お遊びで適当に作っただけなので、本格的な使用に耐えうるものではないことをお断りしておく。

上に示す画像は何も埋め込まれていないもの。下に示す画像にはPython: PaSoRiでSuicaの履歴を読み出すの全文が埋め込まれている。

これなら変更したことを誰にも気づかれないだろう。使用したソース(covgraph.py)は下に示す。使い方は、

covgraph.py cover.png stego.png input.txt

とする。cover.pngにinput.txtの中身を埋め込み、stego.pngとして出力する。input.txtに代えて直接文字列を指定することもできる。

covgraph.py cover.png stego.png "hello, world"

とすれば、stego.pngには"hello, world"が埋め込まれることになる。

埋め込まれた画像からテキストを取り出す場合は、

covgraph.py stego.png

もしくは、

covgraph.py stego.png output.txt

とする。前者は標準出力、後者はファイルに書き出す。

追記: 今回のソースは書き殴りで汚いしアルゴリズムとしてもよくないと思うので、修正版のPython: 続・簡単ステガノグラフィ…画像を画像に埋め込むの方がましだと思う。

#!/usr/bin/env python # -*- coding: utf-8 -*- """covgraph.py by nox, 2008.4.15""" import sys, os, bz2, base64 from PIL import Image def cover(input_image, output_image, text_data): """テキストデータを画像ファイルに埋め込む. input_image: 入力画像ファイル. output_image: 出力画像ファイル(PNG等、アルファ画像が扱えること). text_data: 埋め込むテキストもしくはテキストファイル. """ if os.path.isfile(text_data): text = file(text_data).read() else: text = text_data bz2_text = bz2.compress(text) b16_text = base64.b16encode(bz2_text) im = Image.open(input_image).convert("RGB").convert("RGBA") data = list(im.getdata()) if len(data) < len(b16_text) * 4: print >>sys.stderr, "Error: text too large." sys.exit(1) for i in range(len(b16_text)): for j in range(4): n = i * 4 + j data[n] = (data[n][0], data[n][1], data[n][2], 255 - (int(b16_text[i], 16) >> j & 1)) data[n+1] = (data[n+1][0], data[n+1][1], data[n+1][2], 253) im.putdata(data) im.save(output_image) def uncover(image_file): """画像ファイルからテキストデータを抜き出す. image_file: テキストが埋め込まれた画像ファイル. 戻り値: テキストデータ. """ im = Image.open(image_file) data = list(im.getdata()) end = [d[3] for d in data].index(253) data = [hex((255 - a[3]) + ((255 - b[3]) << 1) + ((255 - c[3]) << 2) + ((255 - d[3]) << 3)).upper()[-1] for a, b, c, d in zip(data[:end:4], data[1:end:4], data[2:end:4], data[3:end:4])] b16_text = "".join(data) bz2_text = base64.b16decode(b16_text) text = bz2.decompress(bz2_text) return text def main(args): if len(args) < 2 or len(args) > 4: print >>sys.stderr, u"画像ファイルにテキストを埋め込む:" print >>sys.stderr, u"Usage: %s <input image> <output image> <text or text-file>" % os.path.basename(args[0]) print >>sys.stderr, u"input image: 入力画像ファイル" print >>sys.stderr, u"output image: 出力画像ファイル(PNG等、アルファ画像が扱えること)" print >>sys.stderr, u"text: 埋め込む文字列" print >>sys.stderr, u"text-file: 埋め込むテキストファイル" print >>sys.stderr print >>sys.stderr, u"画像ファイルからテキストを抜き出す:" print >>sys.stderr, u"Usage: %s <input image>" % os.path.basename(args[0]) print >>sys.stderr, u"Usage: %s <input image> <output text-file>" % os.path.basename(args[0]) print >>sys.stderr, u"input image: 入力画像ファイル" print >>sys.stderr, u"output text-file: テキストの出力ファイル" sys.exit(1) # 画像ファイルにテキストを埋め込む. if len(args) == 4: cover(args[1], args[2], args[3]) # テキストが埋め込まれた画像からテキストを取り出す. elif len(args) == 3: # テキストをファイルに書き出す. file(args[2], "w").write(uncover(args[1])) elif len(args) == 2: # テキストを標準出力に書き出す. print uncover(args[1]) if __name__ == "__main__": main(sys.argv)

続きを読む...

2008年4月14日月曜日

WILLCOM D4 (WS016SH)

このブログ記事をはてなブックマークに追加

以前、こちらでもWillcomが新しいモバイルコミュニケーションマシンを開発という記事を書いたが、本日、その全容が明らかになった。

ウィルコム、Vista搭載の小型端末「WILLCOM D4」(ケータイWatchより)

PCと同等に使えるのは良い。いわゆる、UMPC(Ultra-Mobile PC)だね。問題はどれだけ快適に使えるかだなぁ。キビキビ・サクサク使えるなら買う価値ありかな。キーボードは難しいだろうから、ちゃんと使おうと思うならUSBキーボードをセットかな。逆に言えば、キーボードさえクリアすれば、プライベートでも仕事でもそれなりに使えるかも(もちろんサクサク動くこと前提)。あと携帯電話型のヘッドセットはカッコ良い。5月下旬に予約開始、6月中旬に発売らしい。

以下、主な仕様。

名称: WILLCOM D4 (WS016SH)
OS: Windows Vista Home Edition
CPU: Atom Z520(1.33GHz)
ディスプレイ: タッチパネル対応1,024×600ドット、262,144色、5インチワイドTFT液晶
メモリ: 1GB
40GB HDD
198万画素カメラ
microSDカードスロット装備
ワンセグサポート
W-OAM対応W-SIM
ワイヤレスLAN
USB端子(miniAB)
Office Word/Excel/PowerPoint
別売クレードル(外部ディスプレイ出力, オーディオ出力, LAN端子, 4ポートのUSB端子)

続きを読む...

2008年4月13日日曜日

Google App Engine APIクイックリファレンス

このブログ記事をはてなブックマークに追加

Developer's Guideより。自分用メモ。

    Datastore API: google.appengine.ext.db

    Modelクラス: データモデルの定義を扱う基底クラス。
    • class Model(parent=None, key_name=None, **kw)
    • クラスメソッド
      • Model.get(keys)
      • Model.get_by_id()
      • Model.get_by_key_name(key_names, parent=None)
      • Model.get_or_insert(key_name, **kwds)
      • Model.all()
      • Model.gql(query_string, *args, **kwds)
      • Model.kind()
      • Model.properties()
    • インスタンスメソッド
      • key()
      • put()
      • delete()
      • is_saved()
      • parent()
      • parent_key()
      • to_xml()
    Expandoクラス: インスタンスが任意の属性を追加できるModelクラス。
    • class Expando(parent=None, key_name=None, **kw)
    Propertyクラス: データモデルのプロパティ定義を扱う基底クラス。
    • class Property(verbose_name=None, name=None, default=None, required=False, validator=None, choices=None)
    • 属性
      • data_type
    • インスタンスメソッド
      • default_value()
      • validate(value)
      • empty(value)
      • get_value_for_datastore(model_instance)
      • make_value_from_datastore(value)
    Queryクラス: データストアのクエリインターフェイス。
    • class Query(model_class)
    • インスタンスメソッド
      • filter(property_operator, value)
      • order(property)
      • ancestor(ancestor)
      • get()
      • fetch(limit, offset=0)
      • count(limit)
    GqlQueryクラス: GQLによるデータストアのクエリインターフェイス。
    • GqlQuery(query_string, *args, **kwds)
    • インスタンスメソッド
      • bind(*args, **kwds)
      • get()
      • fetch(limit, offset=0)
      • count(limit)
    Keyクラス: データストアのエンティティのためのユニークキーを表す。
    • class Key(encoded=None)
    • クラスメソッド
      • Key.from_path(*args, **kwds)
    • インスタンスメソッド
      • app()
      • kind()
      • id()
      • name()
      • id_or_name()
      • has_id_or_name()
      • parent()
    関数
    • get(keys)
    • put(models)
    • delete(models)
    • run_in_transaction(function, *args, **kwargs)
    (クラス : 値の型 : ソート順)
    • StringProperty : str, unicode : Unicode
    • BooleanProperty : bool : False < True
    • IntegerProperty : int, long : Numeric
    • FloatProperty : float : Numeric
    • DateTimeProperty, DateProperty, TimeProperty : datetime.datetime : 時間順
    • ListProperty : list : 昇順/降順
    • ReferenceProperty, SelfReferenceProperty : db.Key :
    • UserProperty : users.User : 電子メールアドレス順
    • BlobProperty : db.Blob : -
    • TextProperty : db.Text : -
    • CategoryProperty : db.Category : Unicode
    • LinkProperty : db.Link : Unicode
    • EmailProperty : db.Email : Unicode
    • GeoPtProperty : db.GeoPt : 緯度/経度順
    • IMProperty : db.IM : Unicode
    • PhoneNumberProperty : db.PhoneNumber : Unicode
    • PostalAddressProperty : db.PostalAddress : Unicode
    • RatingProperty : db.RAting : Numeric


    Images API: google.appengine.api.images

    Imageクラス: 画像に対し様々な変換を行うことができる。
    • class Image(image_data)
    • インスタンスメソッド
      • resize(width=0, height=0)
      • crop(left_x, top_y, right_x, bottom_y)
      • rotate(degrees)
      • horizontal_flip()
      • vertical_flip()
      • im_feeling_lucky()
      • execute_transforms(output_encoding=images.PNG)
    関数
    • resize(image_data, width=0, height=0, output_encoding=images.PNG)
    • crop(image_data, left_x, top_y, right_x, bottom_y, output_encoding=images.PNG)
    • rotate(image_data, degrees, output_encoding=images.PNG)
    • horizontal_flip(image_data, output_encoding=images.PNG)
    • vertical_flip(image_data, output_encoding=images.PNG)
    • im_feeling_lucky(image_data, output_encoding=images.PNG)
    • execute_transforms(output_encoding=images.PNG)
    例外
    • exception Error()
    • exception TransformationError()
    • exception BadRequestError()
    • exception NotImageError()
    • exception BadImageError()
    • exception LargeImageError()


    Mail API: google.appengine.api.mail

    EmailMessageクラス: 電子メールメッセージを表す。
    • class EmailMessage(**kw)
    • インスタンスメソッド
      • check_initialized()
      • initialize(**kw)
      • is_initialized()
      • send()
    メッセージフィールド
    • sender
    • to
    • cc
    • bcc
    • reply_to
    • subject
    • body
    • html
    • attachments
    関数
    • check_email_valid(email_address, field)
    • invalid_email_reason(email_address, field)
    • is_email_valid(email_address)
    • send_mail(sender, to, subject, body, **kw)
    • send_mail_to_admins(sender, subject, body, **kw)


    Memcache API: google.appengine.api.memcache

    Clientクラス: Memcacheサービスによるすべての相互作用はClientクラスの実体を使って実行される。
    • class Client()
    • インスタンスメソッド
      • set(key, value, time=0, min_compress_len=0)
      • set_multi(mapping, time=0, key_prefix='', min_compress_len=0)
      • get(key)
      • get_multi(keys, key_prefix='')
      • delete(key, seconds=0)
      • delete_multi(keys, seconds=0, key_prefix='')
      • add(key, value, time=0, min_compress_len=0)
      • replace(key, value, time=0, min_compress_len=0)
      • incr(key, delta=1)
      • decr(key, delta=1)
      • flush_all()
      • get_stats()
    関数
    • set(key, value, time=0, min_compress_len=0)
    • set_multi(mapping, time=0, key_prefix='', min_compress_len=0)
    • get(key)
    • get_multi(keys, key_prefix='')
    • delete(key, seconds=0)
    • delete_multi(keys, seconds=0, key_prefix='')
    • add(key, value, time=0, min_compress_len=0)
    • replace(key, value, time=0, min_compress_len=0)
    • incr(key, delta=1)
    • decr(key, delta=1)
    • flush_all()
    • get_stats()


    URL Fetch API: google.appengine.api.urlfetch

    関数
    • fetch(url, payload=None, method=GET, headers={}, allow_truncated=False)
    レスポンスオブジェクト
    • content
    • content_was_truncated
    • status_code
    • headers


    Users API: google.appengine.api.users

    Userクラス: Googleアカウントによるユーザを表す。
    • class User(email=None)
    • インスタンスメソッド
      • nickname()
      • email()
    関数
    • create_login_url(dest_url)
    • create_logout_url(dest_url)
    • get_current_user()
    • is_current_user_admin()

    続きを読む...

    2008年4月12日土曜日

    なぜGoogle App Engineは凄いのか?

    このブログ記事をはてなブックマークに追加

    Google App Engineがなぜ凄いのかを書いてみる。これを書くのは、自分用にGoogle App Engineでシンプルなユーティリティを作ったのだが、アカウントが取れなかったせいでまだ使えないから。気を紛らわしているだけなので、あまり深い意味はない。

    閑話休題。まあ、Google App Engineはそこら中で凄い凄いと云われているから、なんとなく凄いんだろうなということは誰でも察していると思う。で、本当のところ、どこがどれだけ凄いのか。それを今から自分の解釈で書いてみる。もっとも、ウェブアプリについては素人同然の自分を物差しにしているので、人によってはその限りではないかも。

    まず、準備がとてつもなく楽だということが凄い。今までは、何らかのウェブサービスを個人で始めようと思ったら、レンタルサーバから見つけなくてはならない。無料とは云わなくても適当なレンタルサーバでは環境すら整っていないことが多い。自分で整えようとしても制限が強く理想とするものを作り上げるのが難しかったりする。その点、Google App Engineでは何もしなくてよいのだ。

    次に、アプリケーションを作る、その容易さが凄い。従来の環境であれば、アプリケーションの中身以外のところで苦労させられる。自分はこれをしたいのに、これをするにはまずはあれもそれも考慮しなくてはならない。以前、Pythonで簡単なアップローダを書いたことがあるが、やっぱりシンプルじゃない。拡張子がcgiってのも嫌だ(これはちょっと趣旨が違う)。その点、Google App Engineではしたいことだけ書く。シンプル。

    最後に、Pythonが使えるということ(特に今はこれのみサポート)、そのパフォーマンスが凄い。個人的にはここが一番凄いところで、Google側が用意しているモジュールは非常に強力だ。もともとGoogleはPythonを自社システムで利用しているだけあって、その親和性は驚くほど高い。そしてPython自体による使い勝手の良さと、生産性の高さが、その効果をいやがうえにも高めてくれている。これでPython使いが増えるといいなぁ。←この記事でのホンネ。

    ほかにも、至れり尽くせりな管理ツールだとか、世界最高レベルを誇るスケーラビリティだとか、その辺は凄いと思うのだが、実際に自分が体感しているわけじゃないからここでは取り上げない。

    要は、スクリプトでツールを書く感覚ですんなりウェブアプリが作れるのが凄いのだ。

    続きを読む...

    2008年4月10日木曜日

    Google App Engineでネコ化計画

    このブログ記事をはてなブックマークに追加

    Google App Engineが楽しい。ちょっと忙しいということもあって、まだほとんど触っていないんだけど、それでも簡単なお遊びウェブアプリを作ってみた。作るというのがおこがましいような落書きだけど。で、起動するとPython: ワンライナーでマンデルブロ集合を描くのページの文章をネコっぽくして、画像もネコの画像に置き換える。


    以下に示すように、ソースコード(kitten.py)はいたってシンプル。アイデアさえあれば、大抵のことはできそうだ。まだアカウントがないので稼働できないけど。早く欲しいなぁ。

    #!/usr/bin/env python # -*- coding: utf-8 -*- import re import wsgiref.handlers from google.appengine.api import urlfetch from google.appengine.ext import webapp class MainPage(webapp.RequestHandler): def get(self): result = urlfetch.fetch("http://handasse.blogspot.com/2008/04/python.html") if result.status_code == 200: content = re.sub("。|\&#12290;", "にゃ。", result.content) content = re.sub("な", "にゃ", content) img_list = re.findall('<img (?:.*)src="([a-zA-Z0-9\-/.:_]+)"', content) for img in img_list: content = re.sub(img, "images/kitten.jpg", content) self.response.out.write(content) def main(): application = webapp.WSGIApplication([('/', MainPage)]) wsgiref.handlers.CGIHandler().run(application) if __name__ == "__main__": main()

    ここのブログはなぜか句点が文字参照なので置換処理に変なコードが含まれているけど気にせずに。app.yamlや使用した画像ファイルなどはあまり意味がないので割愛。

    追記:
    app.yamlと画像ファイルを割愛したけど、Google App Engineですぐ結果を見たい人のために、やっぱり示しておく。app.yamlの中身は以下の通り。

    application: kitten version: 1 runtime: python api_version: 1 handlers: - url: /images/(.*) static_files: static/images/\1 upload: static/images/(.*) - url: .* script: kitten.py

    dev_appserver.pyを実行するディレクトリに、kittenというディレクトリを作り、そこにkitten.pyとともに置く。画像ファイルはEyesPic子猫 - フリー画像素材の画像を、

    python -c "import Image; Image.open('eyes0422.jpg').resize((256, 184)).save('kitten.jpg')"

    で256x184にリサイズした(kitten.jpg)。画像ファイルはkitten/static/images/に置くこと。

    あとは、

    dev_appserver.py kitten

    と実行し、http://localhost:8080/にアクセスするだけ。

    URLをスラッシュドット・ジャパンGIGAGINEなどに変更して試したりもしたけど、それなりに面白かった。

    続きを読む...

    2008年4月9日水曜日

    Google App Engine

    このブログ記事をはてなブックマークに追加

    Google App Engineが面白そう。簡単に云うと、Googleをインフラとするウェブサービスで、すべてのリソースはGoogleに置かれる。利点としては、トラフィックやスケーラビリティの心配をしなくても良い、一度アプリケーションを書けばどこでも実行できる、Googleのサービスとの親和性が高いということだろう。現在のところベータ版であり、使用言語はPythonに限られる(これは自分にとっては嬉しいのだが)。

    だがしかし、乗り遅れてしまった…。先着順に1万組が募集されたのだが早々に締め切られてしまった。とはいってもアプリケーションの開発はSDKをダウンロードすればできるけど。ちょっと遊んでみようかなぁ。

    続きを読む...

    The Python Challenge全問制覇、そしてヒント集

    このブログ記事をはてなブックマークに追加

    ついにThe Python Challengeを全問(34問)解いた。これだけ面白いものには滅多に出逢えないだろう。3年近く?、長かったなぁ。まあ、期間は長かったけれど、度々中断してたので(数ヶ月から一年以上とか)、実際は3週間ぐらいかな? フォーラムPython Challenge Hintsには助けられたけど、逆に混乱することも。ちょっとしたひらめきが重要なので、本当は一緒に考えてくれる仲間が周りにいれば良かったと思うんだけど、残念ながらいなかったので一人で解いた。これから挑戦する方は、できるだけ複数人で解くことをお勧めする。Pythonの勉強にもなるし。三人寄れば文殊の知恵。

    と云うわけで、ネタバレにならない程度…というか戯言程度のヒントを書いておく。これを読んで却って混乱しても責任は持てないので悪しからず。もし、ヒントについての質問などがあれば本記事のコメントでお願いしたい。返答は問題をスポイルしない程度(多分、フォーラム程度)になると思う。

    以下、ヒント集。読むのは詰まったときに。

    level 0: 238じゃない。もっと大きい。

    level 1: 手で変換は面倒だ。

    level 2: 読めないものは読まなくていいよね。

    level 3: でかいのが周りにいると自分が小さく感じる。

    level 4: そりゃ、手で書いていたら疲れるね。

    level 5: 笛鳴らしてから蹴る…かな?

    level 6: 日本語だとチャックだよね。

    level 7: PILの出番です。

    level 8: 忙しい?

    level 9: 子供の頃にやったなぁ。

    level 10: 数え方。

    level 11: 1と2、3と4…

    level 12: 何人に配ろうか。

    level 13: 悪者をコールしろ。

    level 14: ぐるぐる。

    level 15: 何年何月何日?

    level 16: あれに見えるはピンクじゃないか?

    level 17: クッキーには2種類ある。美味しいクッキーと美味しくないクッキーだ。

    level 18: 違うのだよ。

    level 19: 何で謝るの? 聴けば分かる。

    level 20: 中に入りたいのだが…

    level 21: 包みを回せ!

    level 22: よく見ると見える?

    level 23: どのモジュール?

    level 24: 迷いそうだ。

    level 25: 絵をどうやって音にするか。

    level 26: どうやって謝る?

    level 27: パターンを見る。

    level 28: 手袋を10回言ってみて。

    level 29: 最初に見たところは見落としがち。

    level 30: ファクターを考えてみよう。

    level 31: 計算は正確に…超科学?

    level 32: アルゴリズムが重要。

    level 33: 喉が乾いたなぁ。

    続きを読む...

    2008年4月5日土曜日

    Python: ワンライナーでマンデルブロ集合を描く

    このブログ記事をはてなブックマークに追加

    Pythonでマンデルブロ集合(Mandelbrot Set)を描いてみた。ただ、描くだけではつまらないので、ワンライナー(1行プログラム)で書いてみた。Pythonでワンライナーってのは邪悪以外のナニモノでもないと思うので、まともな方はまねしないほうがいいかと。

    ワンライナーにしたと云ってもスクラッチからではなく、Python FAQで有名なワンライナーのマンデルブロ集合を流用させてもらった。ただ、オリジナルのものはアスキー文字で表現していたのでPython Imaging Library (PIL)を利用してグラフィカルなものに修正した。もし、PILが入っていないようなら、それはPythonistaの名折れだと思う。コマンドラインで以下の通りに実行すればOK。

    python -c "import Image,math;im=Image.new('L',(480,480));im.fromstring((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM,Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro,i=i,Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)or (x*x+y*y>=4.0)or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(256-int(math.sqrt(F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i))*256/math.sqrt(i))),range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy))))(0.383,0.384,0.2337,0.2347,800,480,480));im.save('m.gif');im.show()"

    これで、最初に示した画像を画面に出力し、m.gifという画像ファイルを同時に作成する。後ろの方の引数を(-2.1,0.7,-1.4,1.4,256,480,480)に変更すれば、左に示す全体像を出力する。

    子供のころ、コンピュータの能力をフルに使って数学の深遠な真理を描いているように見えたマンデルブロ集合は憧れだったんだよなぁ。きっとものすごい難しいことをしているんだと想像したものだけど、実際に計算させるのは簡単なんだよね。

    追記: 上記のコードだけだとちょっと不親切だと思ったので、もう少しわかりやすい(ワンライナーではない)コードも書いておく。出力結果は同じ。

    import Image, math Ru, Ro, Iu, Io, IM, Sx, Sy = 0.383, 0.384, 0.2337, 0.2347, 800, 480, 480 #Ru, Ro, Iu, Io, IM, Sx, Sy = -2.1, 0.7, -1.4, 1.4, 256, 480, 480 def mandelbrot(x, y, IM): c, z, i = complex(x, y), complex(0, 0), 0 while abs(z**2) < 4.0 and i < IM: z = z**2 + c i += 1 return 256 - int(math.sqrt(i) * 256 / math.sqrt(IM)) im = Image.new("L", (Sx, Sy)) im.putdata([mandelbrot(Ru + (x / float(Sx)) * (Ro - Ru), Iu + (y / float(Sy)) * (Io - Iu), IM) for y in range(Sy) for x in range(Sx)]) im.save("m.gif") im.show()

    続きを読む...