2010年に読んだ本

今年読んで良かった本をビジネス系と技術系で5つずつあげてみる。

ビジネス本は読んだ量の割に心に残るものは少なかったけど内容がいい本は深くて色々考えさせられた。特に「イシューからはじめよ」はピカイチだった。

技術書は前半はPK-PDの本とか読んでいて、後半はプログラミング関係をそこそこ読んだ。Ruby関連の本が面白かったような気がする。エキスパートPythonプログラミングも大きい。

ビジネス本

とんがり

ProductName 小さなチーム、大きな仕事―37シグナルズ成功の法則 (ハヤカワ新書juice)
ジェイソン・フリード
早川書房 / ?円 ( 2010-02-25 )


ログをとるのは楽しい

IT業界に限らず、変化が激しい業界では短期的な成果を出すことも、長期的な視野での成果(つまり自分の成長)もきちんと考えることが大切だと思う。

自分で自分を経営しているって感覚が当たり前になるんだろうなと思っている

ProductName 経営の教科書―社長が押さえておくべき30の基礎科目
新 将命
ダイヤモンド社 / 1680円 ( 2009-12-11 )


昨日読み終わったので感想記事を書いてないが、すごく良かった。ピンと来る人にはグイグイ引き込まれる内容。

ProductName イシューからはじめよ―知的生産の「シンプルな本質」
安宅和人
英治出版 / 1890円 ( 2010-11-24 )


技術書

5章まで残り。何度か読みなおしてPythonへの理解を深めていくための本

ProductName エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス / 3780円 ( 2010-05-28 )


月曜日から金曜日に章が分かれていてメタプログラミングを学んでいく

ProductName メタプログラミングRuby
Paolo Perrotta
アスキー・メディアワークス / 2940円 ( 2010-08-28 )


バリバリEmacsを使っているEmacs使いが、作業効率をカイゼンしてさらにバリバリ使うようになるための本

継続やYコンビネータを知るために。独特の形式もまた読んでて楽しい

ProductName Scheme手習い
Daniel P. Friedman
オーム社 / 2940円 ( 2010-10-22 )


創薬研究の問題解決の方法論としても参考になることは多い

ProductName Redmineによるタスクマネジメント実践技法
小川 明彦
翔泳社 / 3444円 ( 2010-10-13 )


feedgeneratorを使ってFlaskでRSSを出力する

FlaskでRSSを出力するのにテンプレートを使っていたのだけど、zenbackの関連記事が一向に反映されなくてFeed Validation Serviceにかけたらvalidじゃないとか言われたのでおそらくこれだろうと。

で、Feed::XMLみたいなのないかなぁと探したらfeedgeneratorってのがあったのでこれを使ってみたら便利。★10と行きたいところであったが、RSSのタイムゾーンではまったので★9くらいで。

結局、dateutil を使ったタイムゾーン管理 / Twisted Mindを参考にしてreplaceで変更した。

tz    = gettz('Asia/Tokyo')
title = u"Drkcore"
link  = u"http://blog.kzfmix.com/rss"

feed = feedgenerator.Rss201rev2Feed(
    title       = title,
    link        = link,
    feed_url    = u"http://blog.kzfmix.com/rss",
    description = u"Programming, Music, Snowboarding",
    language    = u"ja"
    )

for entry in entries:
    categories = [tag.name for tag in entry.tags]
    feed.add_item(
        title       = entry.title,
        link        = u"http://blog.kzfmix.com/entry/" + entry.perma,
        description = entry.htmlized_content,
        pubdate     = entry.pubdate.replace(tzinfo=tz),
        categories  = categories
        )

response = make_response(feed.writeString('utf-8'))
response.headers['Content-Type'] = 'application/xml; charset=utf-8'
return response

タグのネットワーク

sofでグラフ(ネットワーク)を描くためのjavascriptライブラリを見つけたので調べていたら、twitterCytoscapewebを教えてもらったので、cytoscape使い的にはそっちで色々試してからCytoscapewebで描画できるようにすれば効率的であることに気づき、自分のBlogのタグネットワークを描かせてみることにした。

が、色々つまづいて結局そこまでは至らなかったという。まず、networkxはutf8の属性含んだネットワークをgmlで出力仕様とするとエラーはいた。

しょうがないのでigraphでやるかと思ったらigraphの場合はネットワークを組み立てる場合には結構めんどくさかった(隣接行列から構築する場合には楽だけど)基本的に行列用意しておいて読み込ませるのはいいんだけど、sqlalchemyからオブジェクトをひっぱりつつネットワークを組み立てていくってのはnetworkxのほうがやりやすいようだ。 致命的だったのが、mac osxだとsaveしようとするとBus errorが出てセーブできなかった。

心が折れたので、igraphのプロットのスクリーンショット。

描かせたネットワークは僕のブログのエントリに入ってたタグ名をその頻度でノードの半径が大きくなるようにして、エッジは同じエントリに出現した場合(いわゆる共起)に貼るようにしたもの。

本当はこれをベースにして共起頻度の閾値を決めたり、ノードに最後に現れた日の情報を持たせて古いタグは透明度あげたりしようと思っているので、週末にでもSIFで出力してcytoscapeで綺麗にして適当なフォーマットでエクスポートしてcytoscapewebで見れるようにしてみよう。

タグのネットワーク

ちなみにpythonでigraphをいじるのは下のチュートリアルが良かった。

日本語で書籍になっているのはこれ。

ProductName ネットワーク分析 (Rで学ぶデータサイエンス 8)
鈴木 努
共立出版 / ¥ 3,465 ( 2009-09-25 )


from database import db_session
from models import Entry, Tag
from math import log
import igraph

entries = db_session.query(Entry).filter(Entry.publish == 1).order_by(Entry.pubdate.desc()).all()

g=igraph.Graph()

all_tags = []

for entry in entries:
    tags = [tag for tag in entry.tags]
    i = 0
    for tag in tags:
        if tag.name not in all_tags:
            all_tags.append(tag.name)

taghash = dict([(tag,i) for i,tag in enumerate(all_tags)])
tag_count = [0] * len(all_tags)

g.add_vertices(len(all_tags)-1)
g.vs["name"] = all_tags

all_edges = []
for entry in entries:
    tags = [tag for tag in entry.tags]
    num_tags = len(tags)
    if num_tags > 1:
        for i in range(num_tags-1):
            itag = tags[i].name
            tag_count[taghash[itag]] += 1
            for j in range(i+1,num_tags):
                jtag = tags[j].name
                tag_count[taghash[jtag]] += 1
                edge = (taghash[itag],taghash[jtag])
                if edge not in all_edges:
                    all_edges.append(edge)
    else:
        tag_count[taghash[tags[0].name]] += 1

vsizes = [int(log(tc))*5 + 10 for tc in tag_count]

g.add_edges(all_edges)

layout = g.layout("kk")
g.vs["label"] = g.vs["name"]

igraph.plot(g,layout=layout, bbox = (800, 600), edge_color = "#777777",\
vertex_label_size=10, vertex_size=vsizes, vertex_color="#ccff33")
#g.write_gml("tag_network.gml")

Flaskでカスタムフィルターを使う

FlaskでJinja2のカスタムをフィルターを使いたい。具体的にはテンプレート中で{{ URL | urlencode }}がやりたい。

urlエンコードはwerkzeug.urlsのurl_quote_plusをつかえばいい。

>>> from werkzeug.urls import url_quote_plus
>>> url_quote_plus('http://www.kzfmix.com')
'http%3A%2F%2Fwww.kzfmix.com'

Jinja2だとEnvironmentオブジェクトに新しいフィルターを突っ込めばいい

>>> from jinja2 import Environment, PackageLorder
>>> env = Environment(loader=PackageLoader('testapp', 'templates'))
>>> env.filters['urlencode'] = url_quote_plus
>>> template = env.get_template('template.txt')
>>> template.render(url='http://www.kzfmix.com')
u'URL: http%3A%2F%2Fwww.kzfmix.com'

Flaskの場合にはjinja_envってのが用意されているので、ここからfiltersに突っ込めばいいだけだった。

from flask import Flask,render_template
from werkzeug.urls import url_quote_plus

app = Flask(__name__)

app.jinja_env.filters['urlencode'] = url_quote_plus

Registering Filtersに書いてあった。

blogをFlaskベースのものに移行した

(前回までのあらすじ)

今まではCatalystで書いたブログシステムを使っていたのだけど、新しいバージョンのCatalystで動かすためには、色々書きなおさないとこが多すぎて、めんどうだなぁ、どうしようかなぁと思っていたところFlaskの存在を知って、おー楽しそう、書きなおそうとカキカキしていた(夏ごろ)。

(あらすじおわり)

で、ある程度まで作ったものの、なんだかモチベーションが下がって放置していたflaskベースのblogシステムだけど、Mashup Award6で小飼弾さんの 404 API Not Found賞を授賞しましたで、僕のエントリがお役に立ててよかったですねとか。

てか、よく考えたら自分はなんにも作り上げて無いじゃん?と、やる気が戻ってきた(blogはこういうレスポンスがもらえたりするのでいいですね)ので、今週は頑張って動かすとこまで持っていった。(あとは今週は風邪とか謎の頭痛、腹痛で体調は最悪だったが、逆に時間がまとめてとれたので布団の中でゴニョゴニョできた)

Sqliteはつかいやすい

データベースはCatalystで使っていたSqliteのファイルをそのまま流用したので、データの変換とかしないで移行できた。あと、SQLAlchemyのDeclarative便利すぎ。

mod_wsgiのWSGIDaemonProcess素敵

Flaskのドキュメント読みながらmod_wsgi対応させたけど、apache再起動しなくても変更が反映されるようになるのが良い。

手元のmacbookで開発 -> サーバーのバックアップリポジトリにpush -> バックアップリポジトリから稼動させてるディレクトリにpull

という流れで変更を反映できる。

javascriptのライブラリ変えた

特に意味はないがMochikitからjQueryへ変えてみた。jQueryプラグインは便利なんだかどうなんだかわからない。自分でやりたいようにするには手を入れなければいけないことが多いような気がするので。

結局自分でプラグイン書けるようになる必要があるというか、getJSONをうまく使いこなすのがFlaskで楽しく開発するコツかもと思った。それにしてもFlaskのjsonifyは便利ですな。

amazon web service用のモジュールはPerlのNet::Amazonのほうが楽

python-amazon-product-api使ってみたけど、Net::Amazonのほうが使いやすかった。

TODO

  • タグクラウドどうするかなぁ。404のページにはタグ一覧でるようにしといたけど
  • タグでカテゴライズされた個別のRSS Auto Discoveryに対応する
  • 自動ページ送りにも対応する

石庭っていう画像埋め込みプログラムをつくろうとしたが途中で飽きた

プレゼンテーションZenに触発されてつくろうと思ったが途中で飽きた。あとは画像に埋め込むのと画像から抽出する部分を書けばいいんだけどね。まぁ気が向いたらまたやる。

ProductName プレゼンテーションzen
Garr Reynolds
ピアソン桐原 / 2415円 ( 2009-09-04 )


やりたかったのはうさぎの写真にfib(5)を実行するコードを埋め込むとか。

re.Scannerの使い方を覚えたのでよしとしよう。

import sys
import re
from re import Scanner
import Image

LOGLEVEL = 2

def log(level, *msg):
    if level <= LOGLEVEL:
        print "\t".join(map(str, msg))

class VM(object):
    """ Virtual Machine """
    def __init__(self,insns):
        self.insns = insns
        self.stack      = []
        self.heap       = {}
        self.labels     = self.find_labels(insns)

    def run(self):
        return_to = []
        pc = 0
        while pc < len(self.insns):
            if len(self.insns[pc]) == 1:
                insn = self.insns[pc][0]
            else:
                insn,arg = self.insns[pc]

            if insn == "push":
                self.push(arg)
            elif insn == "dup":
                self.push(self.stack[-1])
            elif insn == "copy":
                self.push(self.stack[(arg+1)])
            elif insn == "swap":
                x = self.pop()
                y = self.pop()
                self.push(x)
                self.push(y)
            elif insn == "discard":
                self.pop()
            elif insn == "slide":
                x = self.pop()
                for i in range(arg):
                    self.pop()
                self.push(x)
            elif insn == "add":
                self.push(self.pop() + self.pop())
            elif insn == "sub":
                y = self.pop()
                x = self.pop()
                self.push(x - y)                
            elif insn == "mul":
                self.push(self.pop() * self.pop())                
            elif insn == "div":
                y = self.pop()
                x = self.pop()
                self.push(x / y)
            elif insn == "mod":
                y = self.pop();
                x = self.pop()
                self.push(x % y)
            elif insn == "heap_write":
                value = self.pop()
                addr  = self.pop()
                self.heap[addr] = value
            elif insn == "heap_read":
                addr = self.pop()
                value = self.heap[addr]
                self.push(value)
            elif insn == "jump":
                pc = self.jump_to(arg)
            elif insn == "jump_zero":
                if self.pop() == 0:
                    pc = self.jump_to(arg)
            elif insn == "jump_negative":
                if self.pop() < 0:
                    pc = self.jump_to(arg)
            elif insn == "call":
                return_to.append(pc)
                pc = self.jump_to(arg)
            elif insn == "return":
                if len(return_to) < 1: IndexError('return_to has no item')
                pc = return_to.pop()
            elif insn == "exit":
                return
            elif insn == "char_out":
                sys.stdout.write(chr(self.pop()))
            elif insn == "num_out":
                print self.pop()
            elif insn == "char_in":
                addr = self.pop()
                self.heap[addr] = ord(sys.stdin.read(1))
            elif insn == "num_in":
                addr = self.pop()
                self.heap[addr] = int(sys.stdin.read())
            pc += 1
        print "プログラムの最後がexitではない"

    def push(self, item):
        if type(item).__name__ != 'int': raise TypeError('Not a Integer')
        self.stack.append(item)
        log(2, "stack op: ",self.stack)
    def pop(self):
        if len(self.stack) < 1: raise IndexError('stack has no item')
        return self.stack.pop()

    def jump_to(self, name):
        pc = self.labels[name]
        return pc

    def find_labels(self, insns):
        labels = {}
        for i,tp in enumerate(insns):
            if tp[0] == 'label':
                labels[tp[1]] = i
        return labels

### Scanner

rnum = "([01]+)2"
rlabel = rnum

scanner = Scanner([
    (r"00%s" % rnum,    lambda s,token: ("push", to_num(re.sub(r'^00','',token)))),
    (r"020",            lambda s,token: ("dup",)),
    (r"010%s" % rnum,   lambda s,token: ("copy", to_num(re.sub(r'^010','',token)))),
    (r"021",            lambda s,token: ("swap",)),
    (r"022",            lambda s,token: ("discard",)),
    (r"012%s" % rnum,   lambda s,token: ("slide", to_num(re.sub(r'^012','',token)))),
    (r"1000",           lambda s,token: ("add",)),
    (r"1001",           lambda s,token: ("sub",)),
    (r"1002",           lambda s,token: ("mul",)),
    (r"1010",           lambda s,token: ("div",)),
    (r"1011",           lambda s,token: ("mod",)),
    (r"110",            lambda s,token: ("heap_write",)),
    (r"111",            lambda s,token: ("heap_read",)),
    (r"200%s" % rlabel, lambda s,token: ("label", to_label(re.sub(r'^200','',token)))),
    (r"201%s" % rlabel, lambda s,token: ("call",  to_label(re.sub(r'^201','',token)))),
    (r"202%s" % rlabel, lambda s,token: ("jump",  to_label(re.sub(r'^202','',token)))),
    (r"210%s" % rlabel, lambda s,token: ("jump_zero", to_label(re.sub(r'^210','',token)))),
    (r"211%s" % rlabel, lambda s,token: ("jump_negative", to_label(re.sub(r'^211','',token)))),
    (r"212",            lambda s,token: ("return",)),
    (r"222",            lambda s,token: ("exit",)),
    (r"1200",           lambda s,token: ("char_out",)),
    (r"1201",           lambda s,token: ("num_out",)),
    (r"1210",           lambda s,token: ("char_in",)),
    (r"1211",           lambda s,token: ("num_in",))
    ])

def to_num(str):
    n = int(str[1:-1],2)
    if str[0] == '0': return n
    elif str[0] == '1': return -n
    else: raise TypeError('Not a Integer')

def to_label(str): return str[:-1]

### Parser

def parse_image(file):
    "pixelのrgpの合計のmod3をつなげた文字列を返す"
    modstr = ""
    im = Image.open(file)
    height,width = im.size
    for h in range(height):
        for w in range(width):
            r,g,b = im.getpixel((h,w))
            modstr += str((r+g+b)%3)
    return modstr

def extract_code(str):
    """10個以上の210の並びと10個以上の210の並びに挟まれた領域がコード領域"""
    return re.search(r'(210){10,}([012]+?)(210){10,}').group(2)

if __name__ == '__main__':

#     print "Hello World\n================="
#     t = [('push',10),('push',100),('push',108),('push',114),('push',111),('push',87),('push',32),
#          ('push',111),('push',108),('push',108),('push',101),('push',72),('char_out',),('char_out',),
#          ('char_out',),('char_out',),('char_out',),('char_out',),('char_out',),('char_out',),
#          ('char_out',),('char_out',),('char_out',),('char_out',),('exit',),]
#     vm = VM(t)
#     vm.run()

     print "token check\n================"
     tokens, remainder = scanner.scan("0001000012000110010020001101100200011100102000110111120001010111200010000020001101111200011011002000110110020001100101200010010002120012001200120012001200120012001200120012001200222")

     print tokens

     print "token -> vm\n================"
     vm2 = VM(tokens)

ProductName Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~
原 悠
毎日コミュニケーションズ / ?円 ( 2008-12-20 )


「Google API Expertが解説する Closure Libraryプログラミングガイド」を注文した

amazonのwishlistには入っていたのだけど、いまいちどういう本かわからなかったので、保留中であったが、著者のblogに本の内容のエントリがでてたので読んだら欲しくなった。

ProductName Google API Expertが解説する Closure Libraryプログラミングガイド
伊藤 千光
インプレスジャパン / 3570円 ( 2010-12-10 )


サーバー側がGAE for Pythonってのもなんだかいい。

「Learn Python The Hard Way」を読んだ

52のエクササイズに分かれていて一つが数ページなので読みやすく理解しやすい。

Learn Python The Hard Way

  • Exercise 20: Functions And Files
    • rewindしたければ、f.seek(0)
  • Exercise 23: Read Some Code
    • bitbucketに行ってランダムにPythonのコードを読め。そして学べ。
  • Exercise 38: Reading Code
    • わからない部分があったらプリントアウトしてメモリながら理解して、理解したらコンピューターのスクリーンで眺めて理解出来てるか確認しろ

Pythonの正規表現で最長マッチと最短マッチ

基本は最長マッチ、最短マッチさせたい時には?をつける

>>> import re
>>> a = "ABABABABAB"
>>> re.search(r'(AB)+',a).group()
'ABABABABAB'
>>> re.search(r'(AB)+?',a).group()
'AB'

すぐに忘れるのでメモ

画像ファイルのピクセルのRGB値の合計のmod3

画像ファイルのピクセルのRGB値の合計のmod3を取って文字列にする

def parse_image(file):
    modstr = ""
    im = Image.open(file)
    height,width = im.size
    for h in range(height):
        for w in range(width):
            r,g,b = im.getpixel((h,w))
            modstr += str((r+g+b)%3)
    return modstr

でもちょっと遅い。