メタプログラミングRuby 金曜日

コードを記述するコード

evalとか。pythonだとexecか

ProductName メタプログラミングRuby
Paolo Perrotta
アスキー・メディアワークス / ¥ 2,940 ()
在庫あり。

第二部は「Railsにおけるメタプログラミング」っていう部であまり興味がなかったのでさらっと読み流した。

最後の

メタプログラミングというものなど存在しない。すべてはただのプログラミングじゃ

とこは気に入った。

メタプログラミングRuby 木曜日

クラス定義

特異クラスとか特異メソッドとか

ProductName メタプログラミングRuby
Paolo Perrotta
アスキー・メディアワークス / ¥ 2,940 ()
在庫あり。

Rubyの話はなんとなくわかったけど、Pythonで考えた場合にいまいち理解が浅い。

DWの連載あとで読もう

メタプログラミングRuby 水曜日

水曜日はブロックの章。

ProductName メタプログラミングRuby
Paolo Perrotta
アスキー・メディアワークス / ¥ 2,940 ()
在庫あり。

まつもと直伝 プログラミングのオキテ 第5回(2)

ブロックとは,関数1つを採る高階関数を文法的に特別扱いしただけ,とみなすこともできます。

ほうほう、ということはpythonの場合はラムダ式とればいいのか。

章の終りの方の例がDSLで面白そうなのでやってみる。

def event(name,func):
    if func():
        print  "ALERT: %s" % func.__name__()

event("イベントが発生すること", lambda: True)
event("イベントが発生しないこと", lambda: False)

とやってみたが、ラムダ式は複雑なのが書けないのでコードロック渡したい。

デコレータ使えばいいかということでこんな感じにしてみたが_eventで終わる関数を評価するときに、文字列とって関数適用するところで悩んだ。getattrはまぁわかるとしてimport __main__しないと

Traceback (most recent call last):
  File "metr3.py", line 24, in <module>
    func = getattr(__main__, func_name)
NameError: name '__main__' is not defined

って出てくる理由がわからん。

def event(eventname):
    def decfunc(func):
        def func_():
            if func():
                print  "ALERT: %s" % eventname
        return func_
    return decfunc

@event("イベント発生")
def test1_event(): return True

@event("イベントが発生しないこと")
def test2_event(): return False


if __name__ == '__main__':
    import __main__
    for func_name in [elem for elem in dir() if elem.endswith("_event")]:
        func = getattr(__main__, func_name)
        func()

メタプログラミングRuby 火曜日

火曜日はメソッドを動的に定義する。Pythonだと__setattr__と__getattribute__を使えばいいが、魔術っぽさを感じる。Rubyは見た目自然じゃないか。

ProductName メタプログラミングRuby
Paolo Perrotta
アスキー・メディアワークス / ¥ 2,940 ()
在庫あり。

import re
class Ds(object):
    def get_cpu_info(self,i):
        return 2.16
    def get_cpu_price(self,i):
        return 150
    def get_mouse_info(self,i):
        return "dual optical"
    def get_mouse_price(self,i):
        return 40

# METAPROGRAMMING RUBY
# class computer
#   def initialize(computer_id, data_source)
#     @id = computer_id
#     @data_source = data_source
#     data_source.methods.grep(/^get_(.*)_info$/) { Computer.define_compornent $1}
#   end
#
#   def self.define_component(name)
#     define_method(name){
#       info = data_source.send "get_#{name}_info", @id
#       price = data_source.send "get_#{name}_price", @id
#       result = "#{name.capitalize}: #{info} ($#{price})"
#       return "* #{result}" if price >= 100
#       result
#       }
#     end
#   end

class Computer(object):

    def __init__(self,computer_id, data_source):
        self.data_source = data_source
        self.id = id
        for name in [re.search("^get_(.*)_info$",m).group(1) for m in
                     dir(data_source) if re.search("^get_(.*)_info$",m)]:
            self.define_compornent(name)

    def define_compornent(self, name):
        def define_method():
            info  = self.data_source.__getattribute__("get_%s_info"%name)(self.id)
            price = self.data_source.__getattribute__("get_%s_price"%name)(self.id)
            result = "%s: %s ($%s)" % (name.capitalize(), info, price)
            if price >= 100:
                return "* %s" % result
        self.__setattr__(name,define_method)

if __name__ == '__main__':
    ds = Ds()
    com = Computer(12,ds)
    print com.mouse()
    print com.cpu()

後半はmethod_missingを使う例だった。perlだとAUTOLOADかとおもってググッてみたら。一覧になってた。

ところで、__getattribute__と__getattr__の違いがわからんという新たな発見をした。

メタプログラミングRuby 月曜日

メタプログラミングRubyを読み始めた。第一部は月曜から金曜にわかれていて、、、、、 まぁ毎日読めってことですな。

というわけで、機能はRubyのオブジェクトモデルに関して読んでいた。

ProductName メタプログラミングRuby
Paolo Perrotta
アスキー・メディアワークス / ¥ 2,940 ()
在庫あり。

rubyにはancestorsっていうメソッドがあって継承関係を調べることができる。

pythonだとこんな感じか?

def ancestors(ins): return [c.__name__ for c in ins.__class__.mro()]

クラスに関してはPython Types and Objectsが分かりやすかった。

あと、pythonでのメタプログラミングはエキスパートPythonで少し触れられている。

ProductName エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス / ¥ 3,780 ()
在庫あり。

Sphinxで日本語pdfをつくる

rst2pdf で reStructuredText から PDF を生成するで日本語pdfが生成できるようになってもSphinxでは

[ERROR] pdfbuilder.py:120 BuildEnvironment instance has no attribute 'modules'
....
if self.config.pdf_use_modindex and self.env.modules:
AttributeError: BuildEnvironment instance has no attribute 'modules'
FAILED

とかいうエラーが出る(Sphinx1.02,1.03 + rst2pdf-0.15)

この場合にはrst2pdfの新しい版をsvnでインストール(0.16.dev-r2311をいれた)

あとはSphinxで日本語PDFを生成するの通りにやればうまくいく

ProductName エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス / ¥ 3,780 ()
在庫あり。

中身は全然進んでないのに、出力だけは色々できるようになった ;-)

pyprocessing

pythonでprocessingっぽいことをやろうというプロジェクトが立ち上がってた。

pyprocessing

楽しみ。

bruceとかでかっこいいプレゼンが出来るようになると嬉しい。

igraphを使ってmaximum common substructure(MCS)を求める

MCSを求めるには比較したい二つの化合物のModular Productを求めて、それの最大クリーク探索をすればいい。

Modular Productとは

  • 二つのグラフの属性の対応が同じノードが両方共隣接していてエッジの属性が同じ場合
  • 二つのグラフの属性の対応が同じノードが両方共隣接していない場合

にエッジを張ったグラフ。

これを作ってあとはigraphの最大クリーク探索メソッドで

import openbabel as ob
from igraph import *

sm1 = 'OCC(N)=O'
sm2 = 'O=CC(N)=O'

obc = ob.OBConversion()
obc.SetInAndOutFormats('smi','smi')

source = ob.OBMol()      
obc.ReadString(source,sm1)
source_atoms = [a for a in ob.OBMolAtomIter(source)]

target = ob.OBMol()      
obc.ReadString(target,sm2)
target_atoms = [a for a in ob.OBMolAtomIter(target)]

pairs = []
for i in range(len(source_atoms)):
    for j in range(len(target_atoms)):
        if source_atoms[i].GetAtomicNum() == target_atoms[j].GetAtomicNum():
            pairs.append((source_atoms[i].GetIdx(),target_atoms[j].GetIdx()))

p = []
for x in range(len(pairs)-1):
    for y in range(x+1,len(pairs)):
        u = pairs[x]
        v = pairs[y]
        if u[0] != v[0] and u[1] != v[1]:
            sb = source.GetBond(u[0],v[0])
            tb = target.GetBond(u[1],v[1])
            if sb != None: sbo = sb.GetBondOrder()
            if tb != None: tbo = tb.GetBondOrder()

            if (sb == None and tb == None) or (sb != None and tb != None and sbo == tbo):
                p.append((pairs.index(u),pairs.index(v)))

g = Graph(p)
mc = g.largest_cliques()
for c in mc:
    print [pairs[i] for i in c]

実際にはline graph(エッジとノードを入れ替えたグラフ)のModular Productをもとめてクリーク探索したほうが計算量がかなり減るらしい。

が、今回はやっていない。

MCSSTanimotoで組んだネットワークのメリット

MCSSTanimotoで組んだネットワークのメリットはなんといってもエッジの属性にMCSの情報(smilesとかInChi)をのっけられることであろう。

mcs-net

import sys
from Gaston import MCSSTanimoto,Gaston
import openbabel as ob

obc = ob.OBConversion()
obc.SetInAndOutFormats("sdf", "smi")

input = "pc_sample.sdf"
mol = ob.OBMol()
next = obc.ReadFile(mol,input)
mols = [mol]

while next:
    mol.StripSalts(14)
    mols.append(mol)
    mol = ob.OBMol()
    next = obc.Read(mol)

mols = mols[1:11]

for i in range(len(mols)-1):
    for j in range(i+1,len(mols)):
        sys.stdout.flush()
        mcs = MCSSTanimoto(mols[i],mols[j])
        if mcs.score() > 0.2:
            print "%s\t%s\t%s\t%s\t%2.3f" % (mols[i].GetTitle(), "mcs", \
            mols[j].GetTitle(), obc.WriteString(mcs.mcs).split()[0], mcs.score())

でも、実際に組んでみたら、解釈が意外に難しそうなことに気がついた。MCSSTanimotoのアルゴリズム自体が問題なのかもしれない。

MCSよりはmolblaster的に細切れにしてみて最頻出のフラグメントからネットワークを組んでいくか、ありがち置換基の定義済みテーブルを使って類似性の高い置換基動どうしをつなぐという経験ベースのアプローチのほうが直感的かもしれないなぁ。

Pythonで外部コマンドがPATHに存在するか探す

SWIGでgastonのPythonバインディング作るのがいいのかもしれないけど、SWIG力が足りないので、外部コマンドから使っている。

os.environ['PATH']で探してみたけど、普通はどうやるんだろうか

if 'gaston' not in reduce(lambda a,b: a+b,[os.listdir(d) for d in \
    os.environ['PATH'].split(':') if os.path.isdir(d)]):
    print "gaston: command not found"
    exit()
os.system("gaston %d %s %s > /dev/null 2>&1" % (freq,gasfile,output))