<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6153301078335336992</id><updated>2012-01-03T20:10:53.197+09:00</updated><category term='matplotlib'/><category term='モバイル'/><category term='Python'/><category term='Unix'/><category term='Xbox360'/><category term='Fortress'/><category term='Twitter'/><category term='Blu-ray'/><category term='SciPy'/><category term='PS3'/><category term='インターネット'/><category term='DLNA'/><category term='C'/><category term='PSP'/><category term='動画'/><category term='ゲーム'/><category term='ActionScript'/><category term='キーボード'/><category term='静止画'/><category term='Windows'/><category term='Lisp'/><category term='Ajax'/><category term='C++'/><category term='WILLCOM'/><category term='Scala'/><category term='Flash'/><category term='教育'/><category term='アルゴリズム'/><category term='PowerShell'/><category term='書籍'/><category term='FeliCa'/><category term='spam'/><category term='Processing'/><category term='PC'/><category term='プログラミング'/><category term='Lua'/><category term='Cell'/><category term='Android'/><category term='OpenGL'/><category term='セキュリティ'/><category term='科学'/><category term='WWW'/><category term='ssh'/><category term='Scratch'/><category term='Perl'/><category term='SIMD'/><category term='Java'/><category term='Google'/><category term='Haskell'/><category term='Fortran'/><category term='良いもの。'/><category term='アセンブリ言語'/><category term='GPGPU'/><category term='Proxy'/><category term='カード'/><category term='TeX'/><category term='雑記'/><category term='悪いもの。'/><category term='JavaScript'/><category term='音楽'/><category term='アンチウイルス'/><category term='Blog'/><category term='Erlang'/><category term='メール'/><title type='text'>良いもの。悪いもの。</title><subtitle type='html'>【良い】物事が質的に他よりすぐれまさっている。&lt;br /&gt;
【悪い】劣っている。上等でない。いやしい。(広辞苑より)</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default?start-index=101&amp;max-results=100'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>294</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-7899908936865378421</id><published>2012-01-01T06:23:00.001+09:00</published><updated>2012-01-01T06:34:25.801+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='雑記'/><title type='text'>恭賀新年</title><content type='html'>今年は2012年です。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;bool leap_year(int y) { return !(y%4)^!(y%100)^!(y%400); }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

そして、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;leap_year(2012)&lt;/span&gt; が真となるので閏年です。平年と比べて一日増えるわけですが、その一日たりとも無駄にしない充実した一年にしたいと思います。&lt;br /&gt;&lt;br /&gt;

本年もどうぞ宜しくお願い申し上げます。&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-7899908936865378421?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/7899908936865378421/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=7899908936865378421' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/7899908936865378421'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/7899908936865378421'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2012/01/blog-post.html' title='恭賀新年'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-8713026585219650691</id><published>2011-12-23T17:02:00.004+09:00</published><updated>2011-12-23T17:26:15.032+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='アルゴリズム'/><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='Haskell'/><title type='text'>円周率を1万桁まで求める</title><content type='html'>&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%81%E3%83%B3%E3%81%AE%E5%85%AC%E5%BC%8F"&gt;マチンの公式&lt;/a&gt;で円周率を1万桁まで求めるプログラムを、Python, Erlang, Haskell, C++で書いてみた。出力結果の最後の数桁はずれているかも。また、実行時間を測ったりもしているが、1万桁程度ならどのコードでも瞬時に求まる。&lt;br /&gt;&lt;br /&gt;

まずは素直にPythonで。実行時オプションで桁数指定、実行時間測定付き。オプションなしで1万桁まで求める。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;pi.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#!/usr/bin/env python

import sys, time

N = 10**10000

def arctan(m):
    global N
    c = N
    a = b = c / m
    m2 = m * m
    s = k = 1
    while c:
        b /= m2
        k += 2
        c, s = b / k, -s
        a += c * s
    return a

def main(args):
    global N
    if len(args) &amp;gt; 1: N = 10**int(args[1])
    t1 = time.time()
    pi = str((arctan(5) * 4 - arctan(239)) * 4)
    t2 = time.time()
    print pi[0] + '.' + pi[1:]
    print "Time: %f" % (t2 - t1)

if __name__ == "__main__": main(sys.argv)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;実行:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;$ ./pi.py&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

次にErlangで求めてみる。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;pi.erl&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;-module(pi).
-export([pi/0]).

pi()-&amp;gt;N=e(10,10000),(a(5,N)*4-a(239,N))*4.
e(B,N)-&amp;gt;e(B,N,1).
e(_,0,R)-&amp;gt;R;
e(B,N,R)-&amp;gt;e(B,N-1,R*B).
a(X,N)-&amp;gt;a(X,N div X,N div X,N,1,1).
a(_,A,_,0,_,_)-&amp;gt;A;
a(M,A,B,_,S,K)-&amp;gt;B_=B div(M*M),C=B_ div(K+2),S_=-S,A_=A+C*S_,a(M,A_,B_,C,S_,K+2).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;実行:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;$ erl
1&amp;gt; c(pi).
2&amp;gt; pi:pi().&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

そしてHaskellを使う。ソースコードは一行だ。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;pi.hs&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;n=10^10^4;p=a 5 n*4-a 239 n;a m n=t m(n`div`m)(n`div`m)n 1 3;t _ a _ 0 _ _=a;t m a b c s k=t m(a-y*s)x y(-s)(k+2)where x=b`div`m^2;y=x`div`k&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;実行:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;$ ghci
Prelude&amp;gt; :l pi
Prelude&amp;gt; p*4&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

最後にC++のコード。多倍長演算でちょっと長い。実行時間測定付き。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;strong&gt;pi.cpp&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cmath&amp;gt;

#ifdef _MSC_VER
#include &amp;lt;ctime&amp;gt;
inline double get_time()
{
    return static_cast&amp;lt;double&amp;gt;(std::clock()) / CLOCKS_PER_SEC;
}
#else
#include &amp;lt;sys/time.h&amp;gt;
inline double get_time()
{
    timeval tv;
    gettimeofday(&amp;amp;tv, 0);
    return tv.tv_sec + 1e-6 * tv.tv_usec;
}
#endif

typedef unsigned long long number;

const int FIG      = 8;
const number BASE  = 0xffffffffULL;
const number NN    = 100000000ULL;

// C = 1.0 / (4 * log10(2)) / FIG;
#define C (0.830482023722/FIG)
#define M (X/FIG+2)
#define N (static_cast&amp;lt;int&amp;gt;(X*C+2))

template&amp;lt;int X&amp;gt; class PI {
private:
    number ans[N];

public:
    void add(number* a, number* b);
    void sub(number* a, number* b);
    void div(number* a, number d);
    number intdiv(number x, number y);
    void mul(number* a, number d);
    void atan(number* a, number m);
    number or_numbers(number* a);
    bool has_bit(number* a);
    void decimal(number* a, number* w);
    void print();
    void calc();
};

// a &amp;lt;- arctan(1 / m)
template&amp;lt;int X&amp;gt;
void PI&amp;lt;X&amp;gt;::atan(number* a, number m)
{
    number b[N] = { 1 };
    number c[N] = { 1 };
    div(b, m);
    std::copy(b, b + N, a);
    const number m2 = m * m;
    for (int i = 1; has_bit(c); i++) {
        div(b, m2);
        std::copy(b, b + N, c);
        div(c, i * 2 + 1);
        if (i &amp;amp; 1) sub(a, c);
        else add(a, c);
    }
}

// a &amp;lt;- a + b
template&amp;lt;int X&amp;gt;
void PI&amp;lt;X&amp;gt;::add(number* a, number* b)
{
    for (int i = 0; i &amp;lt; N; i++) {
        number x = a[i] + b[i];
        if (x &amp;amp; ~BASE) {
            a[i] = x &amp;amp; BASE;
            int j;
            for (j = i - 1; a[j] == BASE; j--) a[j] = 0;
            a[j]++;
        } else a[i] = x;
    }
}

// a &amp;lt;- a - b  (a &amp;gt; b)
template&amp;lt;int X&amp;gt;
void PI&amp;lt;X&amp;gt;::sub(number* a, number* b)
{
    for (int i = 0; i &amp;lt; N; i++) {
        if (a[i] &amp;lt; b[i]) {
            a[i] = (BASE + 1) + a[i] - b[i];
            int j;
            for (j = i - 1; a[j] == 0; j--) a[j] = BASE;
            a[j]--;
        } else a[i] -= b[i];
    }
}

// a &amp;lt;- a / d  (d &amp;lt;= BASE)
template&amp;lt;int X&amp;gt;
void PI&amp;lt;X&amp;gt;::div(number* a, number d)
{
    number res = 0;
    for (int i = 0; i &amp;lt; N; i++) {
        res &amp;lt;&amp;lt;= (FIG * 4);
        number x = a[i] + res;
        number q = x / d;
        a[i] = q;
        res = x - q * d;
    }
}

template&amp;lt;int X&amp;gt;
number PI&amp;lt;X&amp;gt;::intdiv(number x, number y)
{
    const number maxbit = ~((number)-1&amp;gt;&amp;gt;1);
    number a = 0;
    number b = 0;
    int cnt = sizeof(number) * 8;

    while (cnt--) {
        a &amp;lt;&amp;lt;= 1ULL;
        if (x &amp;amp; maxbit) a |= 1ULL;
        x &amp;lt;&amp;lt;= 1ULL;
        b &amp;lt;&amp;lt;= 1ULL;
        if (a &amp;gt;= y) { ++b; a -= y; }
    }
    return b;
}

// a &amp;lt;- a * d  (d &amp;lt;= BASE)
template&amp;lt;int X&amp;gt;
void PI&amp;lt;X&amp;gt;::mul(number* a, number d)
{
    number q = 0;
    for (int i = N - 1; i &amp;gt;= 0; i--) {
        number x = a[i] * d + q;
        a[i] = x &amp;amp; BASE;
        q = x &amp;gt;&amp;gt; (FIG * 4);
    }
}

// OR numbers
template&amp;lt;int X&amp;gt;
number PI&amp;lt;X&amp;gt;::or_numbers(number* a)
{
    number ret = 0;
    for (int i = 0; i &amp;lt; N; i++) ret |= a[i];
    return ret;
}

template&amp;lt;int X&amp;gt;
bool PI&amp;lt;X&amp;gt;::has_bit(number* a)
{
    for (int i = 0; i &amp;lt; N; i++) if (a[i]) return true;
    return false;
}

// hex to decimal number
template&amp;lt;int X&amp;gt;
void PI&amp;lt;X&amp;gt;::decimal(number* a, number* w)
{
    number b[N];
    std::copy(a, a + N, b);
    w[0] = b[0];
    b[0] = 0;
    for (int i = 1; i &amp;lt; M; i++) {
        mul(b, NN);
        w[i] = b[0];
        b[0] = 0;
    }
}

// print answer
template&amp;lt;int X&amp;gt;
void PI&amp;lt;X&amp;gt;::print()
{
    number w[M];
    decimal(ans, w);
    std::cout &amp;lt;&amp;lt; std::setw(4) &amp;lt;&amp;lt; std::right &amp;lt;&amp;lt; w[0] &amp;lt;&amp;lt; ".";
    for (int i = 1; i &amp;lt; M; i++) {
        std::cout &amp;lt;&amp;lt; std::setw(FIG) &amp;lt;&amp;lt; std::setfill('0') &amp;lt;&amp;lt; w[i] &amp;lt;&amp;lt; " ";
        if(i % 6 == 0) std::cout &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; "     ";
    }
    std::cout &amp;lt;&amp;lt; std::endl;
}

// calculate PI
// Machin's formula: 4 * arctan(1/5) - arctan(1/239) = PI / 4
template&amp;lt;int X&amp;gt;
void PI&amp;lt;X&amp;gt;::calc()
{
    number x[N];
    atan(ans, 5);
    mul(ans, 4);
    atan(x, 239);
    sub(ans, x);
    mul(ans, 4);
}

int main()
{
    std::ios_base::sync_with_stdio(false);

    PI&amp;lt;10000&amp;gt; pi;

    double start_time = get_time();
    pi.calc();
    double end_time = get_time();
    pi.print();

    std::cerr &amp;lt;&amp;lt; "Time: " &amp;lt;&amp;lt; end_time - start_time &amp;lt;&amp;lt; std::endl;

    return 0;
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;実行:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;$ c++ pi.cpp -O3 -o pi
$ ./pi&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-8713026585219650691?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/8713026585219650691/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=8713026585219650691' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8713026585219650691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8713026585219650691'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/12/1.html' title='円周率を1万桁まで求める'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-4795496528124370763</id><published>2011-11-30T00:09:00.008+09:00</published><updated>2011-12-01T00:28:40.239+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>好きな・嫌いなコンピュータ言語のアンケート</title><content type='html'>社内で好きなコンピュータ言語と嫌いなコンピュータ言語のアンケートを取ってみた。ことの発端は&lt;a href="http://twitter.com/#!/foota/status/134991192315277314"&gt;自分のツイート&lt;/a&gt;なのだが、思いがけず賛同を得ることができたので、実際にアンケートを社内で行うことにしたのだ。各自で社内Wikiを編集してもらってもよかったのだけど、プログラムを作る会社でもあるし、投票システムに興味もあったので、Google App Engine (GAE)で作ってみることにした。以前にGAEで掲示板などを作成していたので、それらを流用して時間をかけずにすぐに仕上げることができた。以下のリンクから実際に投票できる。&lt;br /&gt;&lt;br /&gt;

&lt;div style="text-align: center;"&gt;&lt;strong&gt;&lt;a href="http://opinion-poll.appspot.com/vote?id=lang"&gt;好きな・嫌いなコンピュータ言語のアンケート&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;

表示されているコンピュータ言語一覧から、最も好きな言語と最も嫌いな言語をそれぞれ一つずつ、それ以外にも好きな言語や嫌いな言語があれば、それらも選択(複数可)する。投票したい言語が一覧にない場合は「言語の追加」で自由に追加できるようになっている。言語を選択して投票ボタンを押すと、投票数とそのグラフが更新される。表示されるグラフには、「最も好きな・最も嫌いな言語」と「好きな・嫌いな言語」の2つがあり、青のグラフが「好きな言語」、赤のグラフが「嫌いな言語」を表している。「好きな・嫌いな言語」のグラフについては、「最も好きな・最も嫌いな言語」と「好きな・嫌いな言語」を足し合わせた票数となっている。&lt;br /&gt;&lt;br /&gt;

また、コメントも投稿できるようになっていて、自分の好きな言語を啓蒙するのも良し、嫌いな言語について文句を言うのもアリだ。自由に書き込める。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
現時点での社内での票は以下のグラフに示す通りで、幅広く票が入っているように思う。傾向としては、C, C++, C#, ASM, Python, Rubyあたりが人気で、Visual Basic, Java, Perlが不人気みたいだ。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-imuDetsQ-Yc/TtT47TfrssI/AAAAAAAAAbE/7N-cctTP7gc/s1600/lang01.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 92px;" src="http://3.bp.blogspot.com/-imuDetsQ-Yc/TtT47TfrssI/AAAAAAAAAbE/7N-cctTP7gc/s400/lang01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5680438727750169282" /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-v6BsA8nnSpQ/TtT7iLet0gI/AAAAAAAAAbc/e5GR5nY3XME/s1600/lang02.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 99px;" src="http://4.bp.blogspot.com/-v6BsA8nnSpQ/TtT7iLet0gI/AAAAAAAAAbc/e5GR5nY3XME/s400/lang02.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5680441594636784130" /&gt;&lt;/a&gt;&lt;br /&gt;

今回作成したコードの簡単な説明を書いておく。上述の「好きな・嫌いなコンピュータ言語」だけでなく、初期変数を設定することで、「好きな・嫌いな動物」「好きな・嫌いな食べ物」などのように、簡単に任意のアンケートを作成できるようになっている。グラフについては&lt;a href="http://code.google.com/apis/chart/"&gt;Google Chart API&lt;/a&gt;を利用しているが、1,000ピクセルを超えることができないので、表示数を1,000ピクセル以内に抑える処理を入れている。また、頻繁にデータベースにアクセスしないようにmemcacheも利用している。詳細についてはコードを読んで欲しい。&lt;br /&gt;&lt;br /&gt;

以下に今回作成したGAEのコードを示しておく。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;main.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""main.py  Copyright (C) 2011 nox"""

import os, cgi, re, math, datetime
import urllib, Cookie
import wsgiref.handlers
from google.appengine.api import memcache
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.ext.webapp import template

# 許可する投票タイトル.
TITLES = { "test": u"投票テスト",
           "lang": u"好きな・嫌いなコンピュータ言語のアンケート" }

# 表示メッセージ.
MESSAGES = { "test": u"投票をお願いします。",
             "lang": u"最も好きな言語と最も嫌いな言語をそれぞれ1つ、他に好きな言語や嫌いな言語があればそれも選択(複数可)して投票をお願いします。" }

# 投票する属性. (タイトル / love / like / hate / dislike)
PROPS = { "test": (u"テスト", u"最高", u"良い", u"最低", u"悪い"),
          "lang": (u"言語", u"最も好き", u"好き", u"最も嫌い", u"嫌い") }

# チャートのタイトル. (love and hate / like and dislike [including love and hate])
CHARTS = { "test": (u"最高・最低のテスト", u"良い・悪いテスト"),
           "lang": (u"最も好きな・最も嫌いな言語", u"好きな・嫌いな言語") }

# 最大表示件数
TARGET_LIMIT = 300
COMMENT_LIMIT = 100

class MainPage(webapp.RequestHandler):
    def get(self):
        self.response.out.write(u"""
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;Opinion Poll&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;&amp;lt;a href="vote"&amp;gt;投票システム&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;""")

# 投票データ.
class VoteData(db.Model):
    id = db.StringProperty(multiline=False)
    content = db.StringProperty(multiline=False)
    love = db.IntegerProperty()
    like = db.IntegerProperty()
    hate = db.IntegerProperty()
    dislike = db.IntegerProperty()

# コメントデータ.
class CommentData(db.Model):
    id = db.StringProperty(multiline=False)
    username = db.StringProperty(multiline=False)
    content = db.TextProperty()
    date = db.DateTimeProperty(auto_now_add=True)

# 投票システム・表示.
class Vote(webapp.RequestHandler):
    def get(self):
        global TITLES, TARGET_LIMIT
        id = self.request.get("id")
        if id not in TITLES.keys(): return

        # データベースまたはMemcacheからデータの読み込み.
        cache_votes_key = "votes_%s" % id
        try:
            votes = memcache.get(cache_votes_key)
        except:
            try: memcache.delete(cache_votes_key)
            except: pass
            votes = None
        if votes is None:
            votes = VoteData.all().order("content").filter("id =", id).fetch(TARGET_LIMIT)
            try: memcache.add(cache_votes_key, votes)
            except: pass

        cache_comments_key = "comments_%s" % id
        try:
            comments = memcache.get(cache_comments_key)
        except:
            try: memcache.delete(cache_comments_key)
            except: pass
            comments = None
        if comments is None:
            comments = CommentData.all().order("-date").filter("id =", id).fetch(COMMENT_LIMIT)
            try: memcache.add(cache_comments_key, comments)
            except: pass

        # チャート表示 (1,000ピクセル以下にデータ数を収める).
        target2, target4 = [], []
        target2_max, target4_max = 0, 0
        for v in votes:
            v.love = int(v.love) if v.love else 0
            v.hate = int(v.hate) if v.hate else 0
            v.like = int(v.like) if v.like else 0
            v.dislike = int(v.dislike) if v.dislike else 0
            if v.love or v.hate or v.like or v.dislike:
                target4.append((v.content, v.love + v.like, v.hate + v.dislike))
                target4_max = max(target4_max, v.love + v.like, v.hate + v.dislike)
                if v.love or v.hate:
                    target2.append((v.content, v.love, v.hate))
                    target2_max = max(target2_max, v.love, v.hate)
        target2_max = (target2_max - 1) / 10 if target2_max &amp;gt; 0 else 0
        target2_max = (target2_max + 1) * 10
        target4_max = (target4_max - 1) / 10 if target4_max &amp;gt; 0 else 0
        target4_max = (target4_max + 1) * 10
        n = 0
        while True:
            target2 = filter(lambda t: max(t[1:]) &amp;gt; n, target2)
            if len(target2) &amp;lt;= 16: break
            n += 1
        if target2:
            text = ""
            if n &amp;gt; 0: text = u" (%d票以上)" % (n + 1)
            fig = 10**(int(math.log10(target2_max)))
            if target2_max / fig &amp;lt; 5: fig /= 2
            chart_scale = "|".join([str(i) for i in range(0, target2_max + 1, fig)])
            chart_target2_url = u"http://chart.apis.google.com/chart?chs=%dx200&amp;amp;chd=t:%s|%s&amp;amp;chds=0,%d&amp;amp;cht=bvg&amp;amp;chco=4d89f9,f9894d&amp;amp;chxt=y,x&amp;amp;chxl=0:|%s|1:|%s&amp;amp;chtt=%s%s" % (len(target2) * 60 + 30, ",".join([str(t[1]) for t in target2]), ",".join([str(t[2]) for t in target2]), target2_max, chart_scale, "|".join([urllib.quote(t[0].encode('utf-8')) for t in target2]), CHARTS[id][0], text)
        else: chart_target2_url = ""
        n = 0
        while True:
            target4 = filter(lambda t: max(t[1:]) &amp;gt; n, target4)
            if len(target4) &amp;lt;= 16: break
            n += 1
        if target4:
            text = ""
            if n &amp;gt; 0: text = u" (%d票以上)" % (n + 1)
            fig = 10**(int(math.log10(target4_max)))
            if target4_max / fig &amp;lt; 5: fig /= 2
            chart_scale = "|".join([str(i) for i in range(0, target4_max + 1, fig)])
            chart_target4_url = u"http://chart.apis.google.com/chart?chs=%dx200&amp;amp;chd=t:%s|%s&amp;amp;chds=0,%d&amp;amp;cht=bvg&amp;amp;chco=4d89f9,f9894d&amp;amp;chxt=y,x&amp;amp;chxl=0:|%s|1:|%s&amp;amp;chtt=%s%s" % (len(target4) * 60 + 30, ",".join([str(t[1]) for t in target4]), ",".join([str(t[2]) for t in target4]), target4_max, chart_scale, "|".join([urllib.quote(t[0].encode('utf-8')) for t in target4]), CHARTS[id][1], text)
        else: chart_target4_url = ""

        # 挨拶.
        H = (datetime.datetime.utcnow() + datetime.timedelta(hours=9)).hour
        if H &amp;gt;= 5 and H &amp;lt; 11: greeting = u"おはようございます。"
        elif H &amp;gt;= 11 and H &amp;lt; 17: greeting = u"こんにちは。"
        elif H &amp;gt;= 17 or H &amp;lt; 5: greeting = u"こんばんは。"
        else: greeting = u"こんにちは。"
        greeting += MESSAGES[id]

        # ウェブ上で表示させるデータ.
        template_values = {
            "id": id,
            "title": TITLES[id],
            "target": PROPS[id][0],
            "love": PROPS[id][1],
            "like": PROPS[id][2],
            "hate": PROPS[id][3],
            "dislike": PROPS[id][4],
            "votes": votes,
            "comments": comments,
            "chart_target2_url": chart_target2_url,
            "chart_target4_url": chart_target4_url,
            "greeting": greeting,
            }
        path = os.path.join(os.path.dirname(__file__), "vote.html")
        self.response.out.write(template.render(path, template_values))

# 投票ターゲットの追加.
class AddVote(webapp.RequestHandler):
    def post(self):
        vote = VoteData()
        vote.id = self.request.get("id")

        try:
            try: memcache.delete("votes_%s" % vote.id)
            except: pass
        except:
            pass

        if not self.request.get("content").strip():
            self.redirect("/vote?id=%s" % vote.id)
            return

        content = cgi.escape(self.request.get("content").strip()[:100])
        v = VoteData.all().filter("id =", vote.id).filter("content =", content).get()
        if not v:
            vote.content = content
            vote.put()

        self.redirect("/vote?id=%s" % vote.id)

# 投票.
class PostVote(webapp.RequestHandler):
    def post(self):
        vote = VoteData()
        vote.id = self.request.get("id")
        love = self.request.get_all("love")
        like = self.request.get_all("like")
        hate = self.request.get_all("hate")
        dislike = self.request.get_all("dislike")

        try:
            try: memcache.delete("votes_%s" % vote.id)
            except: pass
        except:
            pass

        if not (love or like or hate or dislike):
            self.redirect("/vote?id=%s" % vote.id)
            return

        for d in love:
            v = VoteData.all().filter("id =", vote.id).filter("content =", d).get()
            if not v.love: v.love = 1
            else: v.love = int(v.love) + 1
            v.put()
        for d in like:
            v = VoteData.all().filter("id =", vote.id).filter("content =", d).get()
            if not v.like: v.like = 1
            else: v.like = int(v.like) + 1
            v.put()
        for d in hate:
            v = VoteData.all().filter("id =", vote.id).filter("content =", d).get()
            if not v.hate: v.hate = 1
            else: v.hate = int(v.hate) + 1
            v.put()
        for d in dislike:
            v = VoteData.all().filter("id =", vote.id).filter("content =", d).get()
            if not v.dislike: v.dislike = 1
            else: v.dislike = int(v.dislike) + 1
            v.put()
        
        self.redirect("/vote?id=%s" % vote.id)

# コメントの投稿.
class PostComment(webapp.RequestHandler):
    def insert_whitespace(self, matchobj):
        return re.sub("(?&amp;lt;!&amp;lt;br) ", "&amp;amp;nbsp;", matchobj.group(0).replace("\t", "    "))

    def post(self):
        global TARGET_LIMIT
        comment = CommentData()
        comment.id = self.request.get("id")

        try:
            try: memcache.delete("comments_%s" % comment.id)
            except: pass
        except:
            pass

        if not self.request.get("content").strip():
            self.redirect("/vote?id=%s" % comment.id)
            return

        if self.request.get("username").strip():
            comment.username = cgi.escape(self.request.get("username").strip()[:255])
        else:
            comment.username = "Anonymous"
        comment.content = cgi.escape(self.request.get("content").strip()[:2000])
        comment.content = re.sub("\r\n|\r|\n", "&amp;lt;br /&amp;gt;", comment.content)
        comment.content = re.sub("\[link\](.*?)\[/link\]", "&amp;lt;a href=\"\g&amp;lt;1&amp;gt;\"&amp;gt;\g&amp;lt;1&amp;gt;&amp;lt;/a&amp;gt;", comment.content)
        comment.content = re.sub("\[code\](.*?)\[/code\]", self.insert_whitespace, comment.content)
        comment.content = re.sub("\[code\](.*?)\[/code\]", "&amp;lt;span style=\"font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;\"&amp;gt;\g&amp;lt;1&amp;gt;&amp;lt;/span&amp;gt;", comment.content)
        comment.date = comment.date + datetime.timedelta(hours=9)
        comment.put()

        self.redirect("/vote?id=%s" % comment.id)

def main():
    application = webapp.WSGIApplication([("/", MainPage),
                                          ("/vote", Vote),
                                          ("/vote/add", AddVote),
                                          ("/vote/post", PostVote),
                                          ("/vote/comment", PostComment),
                                          ],
                                          debug=False)
    wsgiref.handlers.CGIHandler().run(application)

if __name__ == "__main__": main()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;vote.html&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"&amp;gt;
&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja"&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&amp;gt;
    &amp;lt;link type="text/css" rel="stylesheet" href="/stylesheets/main.css" /&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;script type="text/javascript"&amp;gt;
    function resize_textarea(ev){
        var textarea = ev.target || ev.srcElement;
        var value = textarea.value;
        var lines = 1;
        for (var i = 0, l = value.length; i &amp;lt; l; i++)
            if (value.charAt(i) == '\n') lines++;
        if (lines &amp;gt; 5) {
            if (lines &amp;gt; 20) textarea.setAttribute("rows", 20);
            else textarea.setAttribute("rows", lines);
        } else textarea.setAttribute("rows", 5);
    }
    &amp;lt;/script&amp;gt;
    &amp;lt;div style="font-weight: bold; font-size: large; padding-bottom: 20px;"&amp;gt;{{ title }}&amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
    &amp;lt;p&amp;gt;{{ greeting }}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;form action="/vote/add" method="post"&amp;gt;
      &amp;lt;input type="text" name="content" size="20" maxlength="100"&amp;gt;&amp;lt;/input&amp;gt;
      &amp;lt;input type="submit" value="{{ target }}の追加" /&amp;gt;
      &amp;lt;input type="hidden" name="id" value="{{ id }}"&amp;gt;
    &amp;lt;/form&amp;gt;

    &amp;lt;form action="/vote/post" method="post"&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;table border="0" width="400"&amp;gt;
          &amp;lt;th&amp;gt;{{ target }}&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;{{ love }}&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;{{ like }}&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;{{ hate }}&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;{{ dislike }}&amp;lt;/th&amp;gt;
        {% for vote in votes %}
        &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt;&amp;lt;a href="http://ja.wikipedia.org/wiki/{{ vote.content }}"&amp;gt;{{ vote.content }}&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
          &amp;lt;td align="center"&amp;gt;&amp;lt;input type="radio" name="love" value="{{ vote.content }}" /&amp;gt; {% if vote.love %} {{ vote.love }} {% else %} 0 {% endif %}&amp;lt;/td&amp;gt;
          &amp;lt;td align="center"&amp;gt;&amp;lt;input type="checkbox" name="like" value="{{ vote.content }}" /&amp;gt;  {% if vote.like %} {{ vote.like }} {% else %} 0 {% endif %}&amp;lt;/td&amp;gt;
          &amp;lt;td align="center"&amp;gt;&amp;lt;input type="radio" name="hate" value="{{ vote.content }}" /&amp;gt; {% if vote.hate %} {{ vote.hate }} {% else %} 0 {% endif %}&amp;lt;/td&amp;gt;
          &amp;lt;td align="center"&amp;gt;&amp;lt;input type="checkbox" name="dislike" value="{{ vote.content }}" /&amp;gt; {% if vote.dislike %} {{ vote.dislike }} {% else %} 0 {% endif %}&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        {% endfor %}
        &amp;lt;tr align=center&amp;gt;
          &amp;lt;td colspan=5&amp;gt;&amp;lt;input type="submit" value="投票" style="width: 100px; height: 25px" /&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;/table&amp;gt;
        &amp;lt;input type="hidden" name="id" value="{{ id }}"&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/form&amp;gt;&amp;lt;br /&amp;gt;

    &amp;lt;div&amp;gt;
      {% if chart_target2_url %}
      &amp;lt;p&amp;gt;&amp;lt;img src="{{ chart_target2_url }}" /&amp;gt;&amp;lt;/p&amp;gt;
      {% endif %}
      {% if chart_target4_url %}
      &amp;lt;p&amp;gt;&amp;lt;img src="{{ chart_target4_url }}" /&amp;gt;&amp;lt;/p&amp;gt;
      {% endif %}
    &amp;lt;/div&amp;gt;

    コメント記入: &amp;lt;span style="font-size:70%;color:#666666;"&amp;gt;リンク: [link]～[/link], ソースコード: [code]～[/code], 1,000文字まで&amp;lt;/span&amp;gt;&amp;lt;br /&amp;gt;
    &amp;lt;form action="/vote/comment" method="post"&amp;gt;
      &amp;lt;div&amp;gt;&amp;lt;textarea name="content" rows="5" cols="60" onkeyup="resize_textarea(event)"&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;/div&amp;gt;
      名前: &amp;lt;input type="text" name="username" size="31" maxlength="255" value="{{ username }}"&amp;gt;&amp;lt;/input&amp;gt; &amp;lt;input type="submit" value="コメント" /&amp;gt; &amp;lt;span style="font-size:70%;color:#666666;"&amp;gt;無記名で匿名投稿&amp;lt;/span&amp;gt;
      &amp;lt;input type="hidden" name="id" value="{{ id }}"&amp;gt;
    &amp;lt;/form&amp;gt;

    &amp;lt;div&amp;gt;
    {% for comment in comments %}
      {{ comment.username }} [{{ comment.date.year }}年{{ comment.date.month }}月{{ comment.date.day }}日{{ comment.date.hour }}時{{ comment.date.minute }}分{{ comment.date.second }}秒]:
      &amp;lt;blockquote&amp;gt;{{ comment.content }}&amp;lt;/blockquote&amp;gt;
    {% endfor %}
    &amp;lt;/div&amp;gt;
    &amp;lt;!-- ここより下は変更しないでください。不具合やご意見などがありましたら、下記のnoxのリンク先で報告していただけると嬉しいです。 --&amp;gt;
    &amp;lt;div&amp;gt;&amp;lt;img src="http://code.google.com/appengine/images/appengine-silver-120x30.gif" alt="Powered by Google App Engine" /&amp;gt; &amp;lt;span style="font-size:70%;color:#666666;"&amp;gt;&amp;lt;a href="http://handasse.blogspot.com/"&amp;gt;ソースコードおよびその解説&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;span style="font-size:70%;color:#666666;"&amp;gt;Copyright &amp;amp;copy; 2011 &amp;lt;a href="http://handasse.blogspot.com/"&amp;gt;nox&amp;lt;/a&amp;gt;. All rights reserved.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;

  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-4795496528124370763?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/4795496528124370763/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=4795496528124370763' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4795496528124370763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4795496528124370763'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/11/blog-post.html' title='好きな・嫌いなコンピュータ言語のアンケート'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-imuDetsQ-Yc/TtT47TfrssI/AAAAAAAAAbE/7N-cctTP7gc/s72-c/lang01.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-5264564019318384484</id><published>2011-10-19T00:07:00.006+09:00</published><updated>2011-10-19T21:22:40.982+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ゲーム'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='アルゴリズム'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>コンピュータにラストワンを解かせてみた</title><content type='html'>先日、地元のまつりがあった。そこの会場となっている区民館では子供用ボーリングやプラ板遊びなどの催しが開かれていたのだが、その中の一つに「ひとりぼっち」というゲームを行なっているのを見つけたので、一緒にいた小4の自分の娘に「このゲーム面白そうだね」と言ったところ、「ラストワンでしょ」と返され、よく知っているゲームとのことだった。遊び方は次の通り。全部で33のマスがあり、真ん中以外の32個のマスにコマを置く。コマは縦・横方向に隣のコマを一つ飛び越えて進むことができる。飛び越えられたコマは取り除かれる。これを繰り返して、最後のコマを真ん中のマスに置けばクリアとなる。32コマを使うステージ以外にもT字型やピラミッド型にコマを配置するなど、さまざまなバリエーションがある。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-P9HlayptBvw/Tp5ppXtgIvI/AAAAAAAAAac/mRYmEe_qkbQ/s1600/lastone_ex.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 176px;" src="http://2.bp.blogspot.com/-P9HlayptBvw/Tp5ppXtgIvI/AAAAAAAAAac/mRYmEe_qkbQ/s400/lastone_ex.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5665081540739080946" /&gt;&lt;/a&gt;&lt;br /&gt;

ネットで調べたところ、&lt;a href="http://blogs.yahoo.co.jp/showagamer/64820991.html"&gt;1985年にバンダイから同名の電子ゲームが出ていた&lt;/a&gt;ようだ。リンク先では動画もあるのでそれを見てもらえば遊び方は一目瞭然だろう。普通に解いても面白そうだが、コンピュータを使って解かせたくなったので、早速プログラムを作ってみた。&lt;br /&gt;&lt;br /&gt;

余談だが、子供のころ、コンピュータは何でも解いてくれる魔法の箱だと思っていた。円周率を何桁まで解いたと聞いて、ワクワクしたものだ。ラストワンのようなゲームをコンピュータに解かせたいという気持ちは、子供のころのコンピュータに対する憧れから来るのかもしれない。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
閑話休題。ラストワンは33マスあり、コマが「ある」「ない」の2状態のみを持つので、33ビットを使って盤面の状態を表すことができる。そして、ある状態から取り得る次の状態をすべて求めて、それを優先順位付きキューに入れて探索した。キューが空になるか、ゴールの状態が見つかるまで探索を行う。優先順位付きキューにしたのは探索を短縮するためにヒューリスティックな関数でスコアを決めようと思ったからなんだけど、32コマあるステージも含め、どれも一瞬で解けてしまうので、どうやら高度なヒューリスティック関数を用意しなくても問題なかったらしい。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-M6XPsULAs00/Tp2WsDx3sfI/AAAAAAAAAaQ/fQIMyCH8U60/s1600/ex_t.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 180px; height: 180px;" src="http://3.bp.blogspot.com/-M6XPsULAs00/Tp2WsDx3sfI/AAAAAAAAAaQ/fQIMyCH8U60/s400/ex_t.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5664849589974839794" /&gt;&lt;/a&gt;&lt;br /&gt;

上図のように初期配置としてT字型にコマを置く場合、以下に示すような入力データを用意して、これをプログラムに渡す。&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;o&lt;/span&gt; (オー)でコマのあるマスを表し、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;.&lt;/span&gt; (ドット)で空のマスを表している。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;lastone.dat&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;  ...
  ...
.......
..ooo..
...o...
  .o.
  ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

入力データのファイル名が lastone.dat であれば、以下のように実行すればよい。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;% ./lastone lastone.dat&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

実行結果は以下のようになる。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;  ...
  ...
.......
..ooo..
...o...
  .o.
  ...

  ...
  ...
.......
.o..o..
...o...
  .o.
  ...

  ...
  ...
.......
.o.oo..
.......
  ...
  ...

  ...
  ...
.......
.oo....
.......
  ...
  ...

  ...
  ...
.......
...o...
.......
  ...
  ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

最後に作成したコードを示す。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;lastone.cpp&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;// Last One solver  by nox, 14 Oct. 2011

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;fstream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;utility&amp;gt;

using namespace std;

typedef unsigned long long state_t;
typedef int score_t;

const int N = 33;  // number of cells

class LastOne {
private:
    vector&amp;lt;vector&amp;lt;pair&amp;lt;int, int&amp;gt; &amp;gt; &amp;gt; D;  // table of directions
    state_t start, goal;
    vector&amp;lt;state_t&amp;gt; ans;  // solution

public:
    LastOne(const string datafile="");
    ~LastOne() { }

    void read(const string datafile);
    int movable(state_t state);
    void next(state_t state, vector&amp;lt;state_t&amp;gt;&amp;amp; neighbors);
    bool search();
    void show(state_t state);
    void answer();
};

// initialize and make table
LastOne::LastOne(const string datafile)
{
    if (!datafile.empty()) read(datafile);

    vector&amp;lt;pair&amp;lt;int, int&amp;gt; &amp;gt; v;
    v.push_back(make_pair(1, 2));
    v.push_back(make_pair(3, 8));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(4, 9));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(1, 0));
    v.push_back(make_pair(5, 10));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(4, 5));
    v.push_back(make_pair(8, 15));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(9, 16));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(4, 3));
    v.push_back(make_pair(10, 17));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(7, 8));
    v.push_back(make_pair(13, 20));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(8, 9));
    v.push_back(make_pair(14, 21));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(3, 0));
    v.push_back(make_pair(7, 6));
    v.push_back(make_pair(9, 10));
    v.push_back(make_pair(15, 22));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(4, 1));
    v.push_back(make_pair(8, 7));
    v.push_back(make_pair(10, 11));
    v.push_back(make_pair(16, 23));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(5, 2));
    v.push_back(make_pair(9, 8));
    v.push_back(make_pair(11, 12));
    v.push_back(make_pair(17, 24));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(10, 9));
    v.push_back(make_pair(18, 25));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(11, 10));
    v.push_back(make_pair(19, 26));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(14, 15));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(15, 16));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(8, 3));
    v.push_back(make_pair(14, 13));
    v.push_back(make_pair(16, 17));
    v.push_back(make_pair(22, 27));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(9, 4));
    v.push_back(make_pair(15, 14));
    v.push_back(make_pair(17, 18));
    v.push_back(make_pair(23, 28));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(10, 5));
    v.push_back(make_pair(16, 15));
    v.push_back(make_pair(18, 19));
    v.push_back(make_pair(24, 29));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(17, 16));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(18, 17));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(13, 6));
    v.push_back(make_pair(21, 22));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(14, 7));
    v.push_back(make_pair(22, 23));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(15, 8));
    v.push_back(make_pair(21, 20));
    v.push_back(make_pair(23, 24));
    v.push_back(make_pair(27, 30));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(16, 9));
    v.push_back(make_pair(22, 21));
    v.push_back(make_pair(24, 25));
    v.push_back(make_pair(28, 31));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(17, 10));
    v.push_back(make_pair(23, 22));
    v.push_back(make_pair(25, 26));
    v.push_back(make_pair(29, 32));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(18, 11));
    v.push_back(make_pair(24, 23));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(19, 12));
    v.push_back(make_pair(25, 24));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(22, 15));
    v.push_back(make_pair(28, 29));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(23, 16));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(24, 17));
    v.push_back(make_pair(28, 27));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(27, 22));
    v.push_back(make_pair(31, 32));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(28, 23));
    D.push_back(v);

    v.clear();
    v.push_back(make_pair(29, 24));
    v.push_back(make_pair(31, 20));
    D.push_back(v);
}

// read LastOne data
void LastOne::read(const string datafile)
{
    fstream fs(datafile.c_str(), ios_base::in);
    string line;

    goal = 0x10000;
    start = 0ULL;
    while (getline(fs, line)) {
        for (string::iterator p = line.begin(); p != line.end(); ++p) {
            if (*p == 'o') { start &amp;lt;&amp;lt;= 1; start |= 1ULL; }
            else if (*p == '.') start &amp;lt;&amp;lt;= 1;
        }
    }

    fs.close();
}

// number of movable pieces
int LastOne::movable(state_t state)
{
    int s = 0;
    for (int i = 0; i &amp;lt; N; i++)
        if ((state &amp;gt;&amp;gt; i) &amp;amp; 1)
            for (vector&amp;lt;pair&amp;lt;int, int&amp;gt; &amp;gt;::iterator p = D[i].begin(); p != D[i].end(); ++p)
                s += ((state &amp;gt;&amp;gt; p-&amp;gt;first) &amp;amp; 1) &amp;amp; !((state &amp;gt;&amp;gt; p-&amp;gt;second) &amp;amp; 1);
    return s;
}

// next states
void LastOne::next(state_t state, vector&amp;lt;state_t&amp;gt;&amp;amp; neighbors)
{
    neighbors.clear();
    for (int i = 0; i &amp;lt; N; i++)
        if ((state &amp;gt;&amp;gt; i) &amp;amp; 1)
            for (vector&amp;lt;pair&amp;lt;int, int&amp;gt; &amp;gt;::iterator p = D[i].begin(); p != D[i].end(); ++p)
                if (((state &amp;gt;&amp;gt; p-&amp;gt;first) &amp;amp; 1) &amp;amp; !((state &amp;gt;&amp;gt; p-&amp;gt;second) &amp;amp; 1))
                    neighbors.push_back(state ^ (1ULL &amp;lt;&amp;lt; p-&amp;gt;first) ^ (1ULL &amp;lt;&amp;lt; p-&amp;gt;second) ^ (1ULL &amp;lt;&amp;lt; i));
}

// search solution
bool LastOne::search()
{
    vector&amp;lt;state_t&amp;gt; checked(1, start);
    priority_queue&amp;lt;pair&amp;lt;score_t, vector&amp;lt;state_t&amp;gt; &amp;gt; &amp;gt; queue;
    queue.push(make_pair(1, checked));

    vector&amp;lt;state_t&amp;gt; neighbors;
    while (!queue.empty()) {
        pair&amp;lt;score_t, vector&amp;lt;state_t&amp;gt; &amp;gt; q(queue.top());
        queue.pop();
        state_t last = q.second.back();
        if (last == goal) {
            ans = q.second;
            return true;
        }
        next(last, neighbors);
        for (vector&amp;lt;state_t&amp;gt;::iterator p = neighbors.begin(); p != neighbors.end(); ++p) {
            if (binary_search(checked.begin(), checked.end(), *p)) continue;
            if (movable(*p) == 0 &amp;amp;&amp;amp; *p != goal) continue;
            checked.insert(upper_bound(checked.begin(), checked.end(), *p), *p);
            vector&amp;lt;state_t&amp;gt; path(q.second);
            path.push_back(*p);
            queue.push(make_pair(static_cast&amp;lt;score_t&amp;gt;(path.size()), path));
        }
    }

    return false;
}

// show state
void LastOne::show(state_t state)
{
    string line;

    while (state) {
        if (state &amp;amp; 1ULL) line.push_back('o');
        else line.push_back('.');
        state &amp;gt;&amp;gt;= 1;
    }

    int n = N - line.size();
    for (int i = 0; i &amp;lt; n; i++) line.push_back('.');
    reverse(line.begin(), line.end());

    cout &amp;lt;&amp;lt; "  " &amp;lt;&amp;lt; line.substr(0, 3) &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; "  " &amp;lt;&amp;lt; line.substr(3, 3) &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; line.substr(6, 7) &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; line.substr(13, 7) &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; line.substr(20, 7) &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; "  " &amp;lt;&amp;lt; line.substr(27, 3) &amp;lt;&amp;lt; endl;
    cout &amp;lt;&amp;lt; "  " &amp;lt;&amp;lt; line.substr(30, 3) &amp;lt;&amp;lt; endl;
}

// show solution
void LastOne::answer()
{
    for (vector&amp;lt;state_t&amp;gt;::iterator p = ans.begin(); p != ans.end(); ++p) {
        show(*p);
        cout &amp;lt;&amp;lt; endl;
    }
}

int main(int argc, char* argv[])
{
    if (argc &amp;lt; 2) {
        cerr &amp;lt;&amp;lt; "Usage: " &amp;lt;&amp;lt; argv[0] &amp;lt;&amp;lt; " datafile" &amp;lt;&amp;lt; endl;
        exit(1);
    }

    LastOne lastone(argv[1]);

    if (lastone.search()) lastone.answer();
    else cout &amp;lt;&amp;lt; "No solution." &amp;lt;&amp;lt; endl;

    return 0;
}&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-5264564019318384484?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/5264564019318384484/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=5264564019318384484' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/5264564019318384484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/5264564019318384484'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/10/blog-post_19.html' title='コンピュータにラストワンを解かせてみた'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-P9HlayptBvw/Tp5ppXtgIvI/AAAAAAAAAac/mRYmEe_qkbQ/s72-c/lastone_ex.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-1543616563058595024</id><published>2011-10-14T01:03:00.006+09:00</published><updated>2011-10-17T13:15:20.133+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='アルゴリズム'/><category scheme='http://www.blogger.com/atom/ns#' term='雑記'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>オフィスビル内の最短経路</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-WrnVpLsFWTk/TpcMYO8zZgI/AAAAAAAAAZU/foUTEU736mA/s1600/office_3f_which_t.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 227px; height: 294px;" src="http://1.bp.blogspot.com/-WrnVpLsFWTk/TpcMYO8zZgI/AAAAAAAAAZU/foUTEU736mA/s400/office_3f_which_t.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5663008666911204866" /&gt;&lt;/a&gt;現在のオフィスでは、エントランスからどの通路を通るのが最も近いのか、未だに話題になることがある。エントランスからオフィスにつながるエレベータまでの道が二通りあり、どちらも似たような距離なので同じオフィスに行く人達なのにそこで別々になってしまうこともしばしば。左の図が問題のフロアなんだけど、エントランスから青丸で示したエレベータまで行く必要がある。だったらコンピュータに解かせてしまえばいいじゃない、ということでプログラムを作って最短経路と正確な距離を調べてみた。定規で測ったほうが早いという意見は聞こえません。&lt;br /&gt;&lt;br /&gt;

以前、&lt;a href="http://handasse.blogspot.com/2009/10/python-2.html"&gt;Python: 画像で与えられた迷路に対し2点間の最短経路を求める&lt;/a&gt;というブログ記事で、画像から直接最短距離を求めるプログラムを書いたことがあるので、まずはそれを使って調べてみた。図面の邪魔な文字だけ消して、あとはスタート地点とゴール地点の座標を引数で指定するだけだから簡単なんだけど、これだと問題があることに気がついた。というのはCCL (connected component labeling; 連結成分ラベリング)で移動できる領域を調べて、それをA*で解いているのだけど、隣接するノード(ピクセル)を上下左右斜めの8方向のみで調べているから正しい距離を出すことができないのだ。実際に作成した図は以下のようにギザギザになってしまい最短ルートを通らない。なので通り道の分岐や方向が変化する箇所をノードにしてそれをA*で求めることにした。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-i03VQvv0dqo/TpcMz0DU8uI/AAAAAAAAAZg/LVF0fO_CsTc/s1600/result_01a_t.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 228px; height: 295px;" src="http://1.bp.blogspot.com/-i03VQvv0dqo/TpcMz0DU8uI/AAAAAAAAAZg/LVF0fO_CsTc/s400/result_01a_t.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5663009140727149282" /&gt;&lt;/a&gt;&lt;br /&gt;
全部で10個のノード(画像のピクセル座標で指定)を作って調べたところ、以下のような結果になった。左側を通る通路の距離が324.53、右側を通る通路の距離が322.51となり僅かに右側の通路のほうが近かった。ただし、右側の方は途中でフロア案内と少し重なるし、6つあるエレベータのどれを使うかによっても結果が変わってしまうぐらいなので、実質的には同じ距離といってしまっても差し支えないと思う。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-PaIVSHjMgGw/TpcNm32yk0I/AAAAAAAAAZs/8V2Jb9TMa-8/s1600/route_01a_dist_t.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 228px; height: 295px;" src="http://3.bp.blogspot.com/-PaIVSHjMgGw/TpcNm32yk0I/AAAAAAAAAZs/8V2Jb9TMa-8/s400/route_01a_dist_t.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5663010017921635138" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-_FCvaRvRe90/TpcNnAlQcTI/AAAAAAAAAZ0/-5xWxYnPnS8/s1600/route_01_dist_t.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 228px; height: 295px;" src="http://2.bp.blogspot.com/-_FCvaRvRe90/TpcNnAlQcTI/AAAAAAAAAZ0/-5xWxYnPnS8/s400/route_01_dist_t.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5663010020264014130" /&gt;&lt;/a&gt;&lt;br /&gt;

結局、それぞれの距離にはほとんど差がないので、結論としては「好きな方を使え」ということになるだろう。自分は広い通路の方が好みなので右側の通路を利用している。最後にソースコードとその使い方を示しておく。&lt;br /&gt;&lt;br /&gt;

nodes.datに、一行を"ピクセルX座標 ピクセルY座標 隣接ノード番号リスト"としてノード数の分だけ記述する。ノード番号は0から始まり、先頭行と最終行のノードはそれぞれスタート地点、ゴール地点を表している。また、フロアの図面画像をinput.pngとして渡すと、最短距離のルートが書きこまれた画像をoutput.pngとして出力する。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;% ./route_solver.py nodes.dat input.png output.png&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;nodes.dat&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt; 27 273  1 3 6
 21 128  0 2
125  23  1 9
117 221  0 4 5 6
141 178  3 7
167 178  3 7
195 178  0 3 7
192 110  4 5 6 8
193  91  7 9
151  41  2 8&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;route_solver.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os, math, heapq, Image, ImageDraw

class Node:
    def __init__(self):
        self.pos = (0, 0)
        self.neighbors = []
        self.dist = float("inf")
        self.path = []

# A* Search Algorithm
class AStar:
    def __init__(self, start, goal):
        self.start, self.goal = start, goal
        self.start.dist = 0.0
        self.start.path = [self.start]

    def distance(self, node1, node2):
        return math.sqrt((node1.pos[0] - node2.pos[0])**2 + (node1.pos[1] - node2.pos[1])**2)

    def heuristic(self, node):
        return self.distance(node, self.goal)

    def search(self):
        queue = []
        heapq.heappush(queue, (self.heuristic(self.start), self.start))
        while queue:
            score, last = heapq.heappop(queue)
            if last == self.goal: return last.path, last.dist
            for nb in last.neighbors:
                newdist = last.dist + self.distance(last, nb)
                if nb.dist &amp;lt; newdist: continue
                nb.dist = newdist
                nb.path = last.path + [nb]
                heapq.heappush(queue, (nb.dist + self.heuristic(nb), nb))
        return [], 0.0

def draw_path(in_image, out_image, path):
    im = Image.open(in_image)
    im = im.convert("RGB")
    draw = ImageDraw.Draw(im)
    draw.line([(int(p.pos[0]), int(p.pos[1])) for p in path], fill=255, width=2)
    im.save(out_image)

def main(args):
    if len(args) &amp;lt; 4:
        print &amp;gt;&amp;gt;sys.stderr, "Usage: %s datafile in_image out_image" % os.path.basename(args[0])
        sys.exit(1)

    datafile, in_image, out_image = args[1:4];

    print "Reading nodes data..."
    data = [map(int, l.strip().split()) for l in file(datafile).readlines()]
    nodes = [Node() for i in range(len(data))]
    for i, node in enumerate(nodes):
        node.pos = tuple(data[i][:2])
        for j in data[i][2:]:
            node.neighbors.append(nodes[j])
    print " Number of nodes: %d" % len(nodes)

    print "A* searching phase..."
    start, goal = nodes[0], nodes[-1]
    print " Start: (%d, %d), Goal: (%d, %d)" % (start.pos + goal.pos)
    astar = AStar(start, goal)
    path, dist = astar.search()
    if path:
        print " Found route:"
        for p in path: print "  (%d, %d)" % p.pos
        print " Distance: %f" % dist
    else:
        print " No route."

    print "Drawing path on the imagefile..."
    draw_path(in_image, out_image, path)

if __name__ == "__main__": main(sys.argv)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

追記 (2011/10/17): ソースコードが間違っていたので修正。今回の結果には影響なし。&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-1543616563058595024?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/1543616563058595024/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=1543616563058595024' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1543616563058595024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1543616563058595024'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/10/blog-post.html' title='オフィスビル内の最短経路'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-WrnVpLsFWTk/TpcMYO8zZgI/AAAAAAAAAZU/foUTEU736mA/s72-c/office_3f_which_t.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-4918669912178267541</id><published>2011-09-27T04:06:00.002+09:00</published><updated>2011-09-27T04:13:07.563+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WWW'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>GoogleのURL短縮サービスgoo.glのAPIをPythonで利用する</title><content type='html'>しばらく前から&lt;a href="http://goo.gl/"&gt;goo.gl&lt;/a&gt;のAPI (Google URL Shortener API)が使えるようになっていたことは知っていたけど、実際に使ったことなかったので試しに使ってみた。import文を含めなければ3行で短縮URLの作成・復元の自動判別を行うことができた。条件演算子などを使えば1行にまとめられる。簡単。&lt;br /&gt;&lt;br /&gt;

ただし、正規表現は適当だし、エラー処理はしてないので、ちゃんと使う場合はその辺の処理を行う必要あり。あと、本格的に使うのであれば&lt;a href="http://code.google.com/intl/ja/apis/urlshortener/v1/getting_started.html#APIKey"&gt;APIキーを取得&lt;/a&gt;すべき。また、&lt;a href="http://code.google.com/p/google-api-python-client/"&gt;Google APIs Client Library for Python&lt;/a&gt;を&lt;a href="http://code.google.com/p/google-api-python-client/source/browse/samples/urlshortener/urlshortener.py"&gt;使う方法&lt;/a&gt;もある。&lt;br /&gt;&lt;br /&gt;

http://handasse.blogspot.com/ を短縮URLに変換:&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;% googl.py http://handasse.blogspot.com/
http://goo.gl/RiIiD&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

http://goo.gl/RiIiD を元のURLに戻す:&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;% googl.py http://goo.gl/RiIiD
http://handasse.blogspot.com/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;googl.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;import sys,re
from urllib2 import urlopen as U, Request as R
from json import loads as J

API,URL="https://www.googleapis.com/urlshortener/v1/url",sys.argv[1]
if re.match('http://goo\.gl/.+',URL):print J(U(API+'?shortUrl=%s'%URL).read())['longUrl']
else:print J(U(R(API,'{"longUrl":"%s"}'%URL,{'Content-Type':'application/json'})).read())['id']&lt;/span&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-4918669912178267541?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/4918669912178267541/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=4918669912178267541' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4918669912178267541'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4918669912178267541'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/09/googleurlgooglapipython.html' title='GoogleのURL短縮サービスgoo.glのAPIをPythonで利用する'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-7426056449432874947</id><published>2011-08-08T00:48:00.003+09:00</published><updated>2011-08-08T01:04:39.890+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>暇潰しで書いたプログラム</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-exAA9-B7QaA/Tj60VmUauXI/AAAAAAAAAYw/qCwAJxKt4vI/s1600/code.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 200px;" src="http://1.bp.blogspot.com/-exAA9-B7QaA/Tj60VmUauXI/AAAAAAAAAYw/qCwAJxKt4vI/s320/code.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5638142066670877042" /&gt;&lt;/a&gt;
ちょっと出先で手持ち無沙汰になったとき、なにかコードが書きたくなることってあると思う。その時に書くプログラムは何でもよくて、言語も選ばない。でも、駅で電車を待っている間とか、注文したメニューが来るまでとか、ノートPCを引っ張り出してまでやりたくはない。そんなとき、Android端末を片手に&lt;a href="http://code.google.com/p/android-scripting/"&gt;SL4A&lt;/a&gt;でコードを書くっていうのは丁度よい。&lt;br /&gt;&lt;br /&gt;

というわけで、ちょっと外出したときの暇な時間にSL4AのPythonで書いたコードが以下のコード。本当にくだらないプログラムなわけだけど、暇潰しにはなった。何をするプログラムか興味のある方は読み解いて欲しい。暇潰しぐらいにしかならないけど。まあ、勘のよい人ならファイル名だけでわかってしまうかも。 &lt;br /&gt;&lt;br /&gt;

Android端末を持っている方なら、SL4Aのメニューを開いてAddを選択し、Scan Barcodeで冒頭のQRコードを読み込むことでソースコードを取得できる。SL4Aで書いたプログラムだけど、Android端末以外でもPythonの動作する環境なら大抵は実行できると思う。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;Zm9yIGkgaW4gcmFuZ2UoMSwxMDEpOnByaW50J0ZpenonKigxLWklMykrJ0J1enonKigxLWklNSlvciBp.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;exec(r"""%s'''gzga*%%%%,hmkl*ocr*nco`fc"a8ajp*mpf*a+\\0+.p%%%s'kormpv"mq.`cqg469gzga*`cqg46,fgamfgqvpkle*mq,rcvj,`cqglcog*]]dkng]]+Y8/1_++')))%%+++''')))"""%(lambda _:(_,_))("exec(''.join(map(lambda _:chr(ord(_)^2),"))&lt;/span&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-7426056449432874947?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/7426056449432874947/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=7426056449432874947' title='2 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/7426056449432874947'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/7426056449432874947'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/08/blog-post.html' title='暇潰しで書いたプログラム'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-exAA9-B7QaA/Tj60VmUauXI/AAAAAAAAAYw/qCwAJxKt4vI/s72-c/code.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-8181701634916849271</id><published>2011-07-11T03:24:00.012+09:00</published><updated>2011-07-11T16:32:39.143+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><title type='text'>Scalaのアクターモデルでマンデルブロ集合を並列計算</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-S0iwqNlWX-M/Thn3eszHkAI/AAAAAAAAAYM/EDhlTP0lgEw/s1600/mandelbrot.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 200px;" src="http://1.bp.blogspot.com/-S0iwqNlWX-M/Thn3eszHkAI/AAAAAAAAAYM/EDhlTP0lgEw/s200/mandelbrot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5627801316169846786" /&gt;&lt;/a&gt;
最近、本格的にScalaを使用するつもりで環境を整えた。ビルドツールとして&lt;a href="https://github.com/harrah/xsbt/wiki"&gt;Simple Build Tool (sbt)&lt;/a&gt;を利用し、エディタは&lt;a href="https://github.com/aemoncannon/ensime"&gt;ensime&lt;/a&gt;を入れたEmacsにしている。利用するに当たって&lt;a href="http://aemon.com/file_dump/ensime_manual.html#tth_sEc2.2"&gt;.emacsに必要な記述を加えておく&lt;/a&gt;こと。初めてのプロジェクトの場合、適当に作成したディレクトリ内でsbtを起動してプロジェクトの雛形を作った後、Emacsを立ち上げて M-x ensime-config-gen で下準備をする。ここまでは初回のみの作業となる。その後は M-x ensime を実行して、src/main/scala/内でコードを書くだけ。これで、コーディング中にタブで補完してくれるし、文法などが間違っている場合に赤でハイライトもしてくれる。コンパイルや実行などは、C-c C-v s とすればsbtがEmacs上で立ち上がるので compile や run するだけ。簡単。&lt;br /&gt;&lt;br /&gt;

Scalaと言えばアクターでしょ、やっぱり。ということで今回はアクターモデルを使用したコードを書いてみた。自分はScalaをいじるよりも前にErlangをいじっていたので余計にそう考えてしまうのかもしれない。それでもマルチコアやらメニーコアが溢れているこの時代、並行処理の重要性は誰もが認識しているはず。&lt;br /&gt;&lt;br /&gt;

書いたコードはマンデルブロ集合の計算だ。毎回飽きもせずにまたそれかと思うかもしれないが、並列処理させるのが簡単だし、いつも同じ内容にしておけば比較も容易。なによりもマンデルブロ集合の画像はともて魅力的だと自分では思っているので、今回もマンデルブロ集合だ。冒頭に作成した画像を載せたけど、やっぱり美しいと思う。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
今回のアクターモデルでは、scala.actors.Actorから派生させずに、scala.actors.Actor.actorを使用した。クラスを作るほど大行ではないし、その方がスッキリするからだ。そして、計算のループ部分は末尾再帰とパターンマッチを使っている。関数型言語として使うならやっぱりその方がしっくりくる。ただ、ちゃんと末尾再帰させるように、final修飾子をつけるなど、気をつけること。それと@tailrecアノテーション(scala.annotation.tailrec)は常に付けたほうが良い。@tailrecアノテーションを末尾再帰させたい関数につけておけば、末尾再帰としてコンパイルされなかった時にエラーを返してくれる。アクターを使用しないコードもついでに作成した。画像の色付け部分は&lt;a href="http://d.hatena.ne.jp/a-san/20100213#p1"&gt;ScalaでMandelbrot&lt;/a&gt;、時間測定は&lt;a href="http://diaspar-journal.blogspot.com/2009/04/blog-post.html#lang-scala"&gt;関数の実行時間を計測するには？&lt;/a&gt;を参考にさせてもらった。&lt;br /&gt;&lt;br /&gt;

実行環境としてXeon X5650@2.67GHz * 2 (12コア/24スレッド; メモリ48GB)を使用した。マンデルブロ集合の条件としては、画像の大きさが4,800 x 4,800ピクセルで、(-0.005, -0.005)から(0.005, 0.005)を計算の範囲とし、繰り返し数10,000で10.0を閾値としている。因みにこの範囲は集合に含まれるので計算量としては最大となる(画像としては真っ黒なので面白くはないが)。実行すると最後に画像ファイル(mandelbrot.png)を出力して終了する。&lt;br /&gt;&lt;br /&gt;

実行した結果、アクターを使用しなかったコードの実行時間が&lt;strong&gt;1007.18秒&lt;/strong&gt;だったのに対し、アクターを使用したコードでは&lt;strong&gt;51.636秒&lt;/strong&gt;であった。&lt;strong&gt;約20倍の高速化&lt;/strong&gt;である。コア数12・スレッド数24であることを考えても、この高速化は十分なのではないだろうか。ところで、アクターを使用しないシングルスレッドでのコードでも以前作成したC++のコード(1067.39秒)より速かった。それに、TBBを使用した並列化したコードでも56.8956秒の計算時間がかかっており、アクターを使用したScalaのコードはそれよりも速い。色の作成の部分など少し異なるところもあるのでそのまま比較することはできないかもしれないが、少なくとも今回の場合ではScalaで十分な速度を得られることは確認できた。余談だが、Telsaのボードを2枚挿ししたマシン上で実行した&lt;a href="http://handasse.blogspot.com/2011/06/cuda-40gpu.html"&gt;CUDAのコードでは3.73秒&lt;/a&gt;なので次元が違ったりする。&lt;br /&gt;&lt;br /&gt;

しかし、実際にコードを書くときは注意深くなる必要があるかもしれない。例えば以下のコードがある。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;    i match {
      case MAX_LOOP =&amp;gt; -1
      case _ if zr2 + zi2 &amp;gt;= TH =&amp;gt; i
      case _ =&amp;gt; {
        val zrzi = zr * zi
        calcLoop(i + 1, cr, ci, zr2 - zi2 + cr, zrzi + zrzi + ci)
      }
    }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

これを以下のように変更する。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;    (i, zr2 + zi2) match {
      case (MAX_LOOP, _) =&amp;gt; -1
      case (_, z) if z &amp;gt;= TH =&amp;gt; i
      case _ =&amp;gt; {
        val zrzi = zr * zi
        calcLoop(i + 1, cr, ci, zr2 - zi2 + cr, zrzi + zrzi + ci)
      }
    }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

これだけのことで実行速度が6倍程遅くなる。これに気付かずにいると本来のパフォーマンスを出すことができない。予想よりもプログラムが遅いと感じた場合は再度コードを確認したほうがいいだろう。&lt;br /&gt;&lt;br /&gt;

&lt;a href="http://www.amazon.co.jp/gp/product/4822284239/ref=as_li_qf_sp_asin_il?ie=UTF8&amp;tag=footawwwservi-22&amp;linkCode=as2&amp;camp=247&amp;creative=1211&amp;creativeASIN=4822284239"&gt;&lt;img style="float:left;margin:0 10px 10px 0;" border="0" src="http://ws.assoc-amazon.jp/widgets/q?_encoding=UTF8&amp;Format=_SL110_&amp;ASIN=4822284239&amp;MarketPlace=JP&amp;ID=AsinImage&amp;WS=1&amp;tag=footawwwservi-22&amp;ServiceVersion=20070822" &gt;&lt;/a&gt;&lt;img src="http://www.assoc-amazon.jp/e/ir?t=footawwwservi-22&amp;l=as2&amp;o=9&amp;a=4822284239" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;&lt;a href="http://www.amazon.co.jp/gp/product/4844327453/ref=as_li_tf_il?ie=UTF8&amp;tag=footawwwservi-22&amp;linkCode=as2&amp;camp=247&amp;creative=1211&amp;creativeASIN=4844327453"&gt;&lt;img style="float:right;margin:0 10px 10px 0;" border="0" src="http://ws.assoc-amazon.jp/widgets/q?_encoding=UTF8&amp;Format=_SL110_&amp;ASIN=4844327453&amp;MarketPlace=JP&amp;ID=AsinImage&amp;WS=1&amp;tag=footawwwservi-22&amp;ServiceVersion=20070822" &gt;&lt;/a&gt;&lt;img src="http://www.assoc-amazon.jp/e/ir?t=footawwwservi-22&amp;l=as2&amp;o=9&amp;a=4844327453" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;float:left;margin:0 10px 10px 0;" /&gt;自分は普段は主にC++とPythonを使っていて、目的によって使い分けている。ある程度しっかりした作りで高速に動作させたい場合はC++だし、プロトタイプとして作ったり、結果の解析用コードをサクっと作りたい場合はPythonを使う。Scalaはどのような用途に使えるのか。まず、Javaの資産を使えるのは大きい。ネットワーク処理や画像処理などをある程度本格的につくろうとしたときに便利そうだ。あとは言語の仕様として処理を簡潔に書き表せるという点が良い。ライブラリが強力で簡潔に書ける。この点だけで十分に利用する価値があるだろう。&lt;br /&gt;&lt;br /&gt;

最後に自分の持っているScalaの書籍を2つ挙げておく。&lt;a href="http://www.amazon.co.jp/gp/product/4822284239/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;tag=footawwwservi-22&amp;linkCode=as2&amp;camp=247&amp;creative=1211&amp;creativeASIN=4822284239"&gt;Scalaプログラミング入門&lt;/a&gt;&lt;img src="http://www.assoc-amazon.jp/e/ir?t=footawwwservi-22&amp;l=as2&amp;o=9&amp;a=4822284239" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;は実践的だし、&lt;a href="http://www.amazon.co.jp/gp/product/4844327453/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;tag=footawwwservi-22&amp;linkCode=as2&amp;camp=247&amp;creative=1211&amp;creativeASIN=4844327453"&gt;Scalaスケーラブルプログラミング&lt;/a&gt;&lt;img src="http://www.assoc-amazon.jp/e/ir?t=footawwwservi-22&amp;l=as2&amp;o=9&amp;a=4844327453" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;は網羅的なので、どちらも持っていて損はないと思う。&lt;br /&gt;&lt;br /&gt;

作成したコードを以下に示しておく。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;mandelbrot.scala (アクターあり; 並行処理)&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;// mandelbrot.scala with actor  by nox, 2011.07.09

import scala.actors._, Actor._
import scala.annotation.tailrec
import java.io.FileOutputStream
import java.awt.image.BufferedImage
import java.awt.Color
import javax.imageio.ImageIO
import scala.testing.Benchmark

/**
 * マンデルブロ集合.
 * SW       : 画像の幅.
 * SH       : 画像の高さ.
 * T        : 集合の上の位置.
 * L        : 集合の左の位置.
 * W        : 集合の幅.
 * H        : 集合の高さ.
 * MAX_LOOP : イテレーションの最大回数.
 * TH       : 閾値.
 */
final class Mandelbrot(SW: Int, SH: Int, T: Double, L: Double, W: Double, H: Double, MAX_LOOP: Int, TH: Double) {

  /**
   * イテレーションの回数から色を決定して任意のピクセルにセットする.
   */
  def setPixel(x: Int, y: Int, a: Any, image: BufferedImage) =
    a match {
      case -1 =&amp;gt; image setRGB(x, y, 0)
      case i: Int =&amp;gt; image setRGB(x, y, Color HSBtoRGB(i / 100.0f, 1.0f, 1.0f))
    }

  /**
   * マンデルブロ集合の計算部分.
   */
  @tailrec
  def calcLoop(i: Int, cr: Double, ci: Double, zr: Double, zi: Double): Int = {
    val zr2 = zr * zr
    val zi2 = zi * zi
    i match {
      case MAX_LOOP =&amp;gt; -1
      case _ if zr2 + zi2 &amp;gt;= TH =&amp;gt; i
      case _ =&amp;gt; {
        val zrzi = zr * zi
        calcLoop(i + 1, cr, ci, zr2 - zi2 + cr, zrzi + zrzi + ci)
      }
    }
  }

  /**
   * アクター.
   * ある幅に存在するピクセルの計算を受け持つ.
   */
  def calc = actor {
    react {
      case (ix: Int, image: BufferedImage) =&amp;gt; {
        for (iy &amp;lt;- 0 until SH)
          setPixel(ix, iy, calcLoop(0, L + (ix.toDouble / SW) * W, T + (iy.toDouble / SH) * H, 0.0, 0.0), image)
        reply(ix)
      }
    }
  }

  /**
   * 幅のピクセル数分だけアクターにメッセージを送る.
   */
  @tailrec
  def runLoop(ix: Int, results: Array[Future[Any]], image: BufferedImage): Unit =
    ix match {
      case SW =&amp;gt; results foreach(_ apply)
      case _ =&amp;gt; {
        results(ix) = calc !! ((ix, image))
        runLoop(ix + 1, results, image)
      }
    }

  /**
   * 実行.
   * 計算部分の処理時間計測および画像ファイルの書き出し.
   */
  def run = {
    val results = new Array[Future[Any]](SW)
    val image = new BufferedImage(SW, SH, BufferedImage TYPE_3BYTE_BGR)

    val t = (new Benchmark { def run = runLoop(0, results, image) } runBenchmark 1)(0) / 1000.0
    println("Time: " + t + " s")

    val out = new FileOutputStream("mandelbrot.png")
    ImageIO write(image, "png", out)
    out close
  }
}

object MandelbrotMain {
  def main(args: Array[String]) =
    new Mandelbrot(4800, 4800, -0.005, -0.005, 0.01, 0.01, 10000, 10.0) run
    //new Mandelbrot(640, 480, -1.0, -2.0, 2.6666667, 2.0, 1000, 10.0) run
    //new Mandelbrot(800, 800, 0.025185, -1.401565, 0.0005, 0.0005, 10000, 10.0) run
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;mandelbrot.scala (アクターなし; 逐次処理)&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;// mandelbrot.scala without actor  by nox, 2011.07.09

import scala.annotation.tailrec
import java.io.FileOutputStream
import java.awt.image.BufferedImage
import java.awt.Color
import javax.imageio.ImageIO
import scala.testing.Benchmark

/**
 * マンデルブロ集合.
 * SW       : 画像の幅.
 * SH       : 画像の高さ.
 * T        : 集合の上の位置.
 * L        : 集合の左の位置.
 * W        : 集合の幅.
 * H        : 集合の高さ.
 * MAX_LOOP : イテレーションの最大回数.
 * TH       : 閾値.
 */
final class Mandelbrot(SW: Int, SH: Int, T: Double, L: Double, W: Double, H: Double, MAX_LOOP: Int, TH: Double) {

  /**
   * イテレーションの回数から色を決定して任意のピクセルにセットする.
   */
  def setPixel(x: Int, y: Int, a: Any, image: BufferedImage) =
    a match {
      case -1 =&amp;gt; image setRGB(x, y, 0)
      case i: Int =&amp;gt; image setRGB(x, y, Color HSBtoRGB(i / 100.0f, 1.0f, 1.0f))
    }

  /**
   * マンデルブロ集合の計算部分.
   */
  @tailrec
  def calcLoop(i: Int, cr: Double, ci: Double, zr: Double, zi: Double): Int = {
    val zr2 = zr * zr
    val zi2 = zi * zi
    i match {
      case MAX_LOOP =&amp;gt; i
      case _ if zr2 + zi2 &amp;gt;= TH =&amp;gt; i
      case _ =&amp;gt; {
        val zrzi = zr * zi
        calcLoop(i + 1, cr, ci, zr2 - zi2 + cr, zrzi + zrzi + ci)
      }
    }
  }

  def calc(cr: Double, ci: Double): Int = 
    calcLoop(0, cr, ci, 0.0, 0.0)

  /**
   * 画像のピクセル数分だけループする.
   */
  @tailrec
  def runLoop(ix: Int, iy: Int, image: BufferedImage): Unit =
    (ix, iy) match {
      case (SW, _) =&amp;gt; None
      case (_, SH) =&amp;gt; runLoop(ix + 1, 0, image)
      case (_, _) =&amp;gt; {
        setPixel(ix, iy, calc(L + (ix.toDouble / SW) * W, T + (iy.toDouble / SH) * H), image)
        runLoop(ix, iy + 1, image)
      }
    }

  /**
   * 実行.
   * 計算部分の処理時間計測および画像ファイルの書き出し.
   */
  def run = {
    val image = new BufferedImage(SW, SH, BufferedImage TYPE_3BYTE_BGR)

    val t = (new Benchmark { def run = runLoop(0, 0, image) } runBenchmark 1)(0) / 1000.0
    println("Time: " + t + " s")

    val out = new FileOutputStream("mandelbrot.png")
    ImageIO write(image, "png", out)
    out close
  }
}

object MandelbrotMain {
  def main(args: Array[String]) =
    new Mandelbrot(4800, 4800, -0.005, -0.005, 0.01, 0.01, 10000, 10.0) run
    //new Mandelbrot(640, 480, -1.0, -2.0, 2.6666667, 2.0, 1000, 10.0) run
    //new Mandelbrot(800, 800, 0.025185, -1.401565, 0.0005, 0.0005, 10000, 10.0) run
}&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-8181701634916849271?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/8181701634916849271/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=8181701634916849271' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8181701634916849271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8181701634916849271'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/07/scala.html' title='Scalaのアクターモデルでマンデルブロ集合を並列計算'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-S0iwqNlWX-M/Thn3eszHkAI/AAAAAAAAAYM/EDhlTP0lgEw/s72-c/mandelbrot.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-8996017614539605761</id><published>2011-06-06T03:22:00.004+09:00</published><updated>2011-06-06T03:54:35.424+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='GPGPU'/><title type='text'>CUDA 4.0でマルチGPU化が限りなく簡単になっているという話</title><content type='html'>以前、&lt;a href="http://handasse.blogspot.com/2011/02/gpu.html"&gt;複数のGPUでマンデルブロ集合を並列計算&lt;/a&gt;というブログ記事を書いた。このときはTeslaが1枚で7.45秒、Teslaが2枚で4.26秒で計算できたと報告したが、マルチGPU化は多少面倒だったし、ある程度のオーバーヘッドが掛かっていた。&lt;br /&gt;&lt;br /&gt;

しかし、&lt;a href="http://developer.nvidia.com/cuda-toolkit-40"&gt;CUDA 4.0&lt;/a&gt;がリリースされてから状況が一変した。自由にcudaSetDeviceが呼び出せるようになり、またUVA (Unified Virtual Addressing)を利用することにより、それはそれは簡単にマルチGPU化や、ホスト・複数GPU間での一元的なメモリの参照を実現できることになった。&lt;br /&gt;&lt;br /&gt;

というわけで、CUDA 4.0を使ったマンデルブロ集合のプログラムを以下に示す。シングルGPUからマルチGPUへ変更した部分については赤字で示してある。ただし、簡便のために、GPUデバイスは2枚で決め打ちとし、マンデルブロ集合の幅は2の倍数のピクセルとなるようにしてある。また、UVAは使わなくても良かったのだが、使い方を示すために利用している。&lt;br /&gt;&lt;br /&gt;

CUDA 4.0によるマルチGPU化によりマンデルブロ集合計算の実行時間(計算条件は&lt;a href="http://handasse.blogspot.com/2011/02/gpu.html"&gt;前回&lt;/a&gt;と同じ)は&lt;strong&gt;7.45秒から3.73秒&lt;/strong&gt;となった。ほぼ2倍の速度である。シングルGPUからの修正箇所はほんの僅か。カーネル関数に至っては一文字たりとも変更していない。良い時代になったものだ。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;

&lt;strong&gt;mandelbrot_multigpu.cu&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;// madelbrot using multi GPU devices  by nox, 2011.06.06
// nvcc -lcutil_x86_64 -arch sm_13 -use_fast_math -prec-sqrt=false -keep -L ~/NVIDIA_GPU_Computing_SDK/C/lib -I ~/NVIDIA_GPU_Computing_SDK/C/common/inc -g mandelbrot_multigpu.cu -o mandelbrot_multigpu

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;fstream&amp;gt;

#include "cutil_inline.h"

using namespace std;

const int BLOCK_SIZE_X = 16;
const int BLOCK_SIZE_Y = 16;

__device__
uchar4 coloring(int n, int b)
{
    int d = n % b;
    d *= 256 / b;
    int m = static_cast&amp;lt;int&amp;gt;(d / 42.667f);

    switch (m) {
    case 0: return make_uchar4(0, 6 * d, 255, 0);
    case 1: return make_uchar4(0, 255, 255 - 6 * (d - 43), 0);
    case 2: return make_uchar4(6 * (d - 86), 255, 0, 0);
    case 3: return make_uchar4(255, 255 - 6 * (d - 129), 0, 0);
    case 4: return make_uchar4(255, 0, 6 * (d - 171), 0);
    case 5: return make_uchar4(255 - 6 * (d - 214), 0, 255, 0);
    default: return make_uchar4(0, 0, 0, 0);
    }
}

__global__
void mandelbrot(float t, float l, float w, float h, int sw, int sh, int max_iter, float th, uchar4* d_color)
{
    int ix = blockIdx.x * blockDim.x + threadIdx.x;
    int iy = blockIdx.y * blockDim.y + threadIdx.y;
    if (ix &amp;gt;= sw || iy &amp;gt;= sh) return;

    float ci = t + (static_cast&amp;lt;float&amp;gt;(iy) / sh) * h;
    float cr = l + (static_cast&amp;lt;float&amp;gt;(ix) / sw) * w;
    float zi = 0.0f;
    float zr = 0.0f;
    float zrzi, zr2, zi2;

    for (int i = 0; i &amp;lt; max_iter; i++) {
        zrzi = zr * zi;
        zr2 = zr * zr;
        zi2 = zi * zi;
        zr = zr2 - zi2 + cr;
        zi = zrzi + zrzi + ci;
        if (zi2 + zr2 &amp;gt;= th) {
            d_color[ix*sh+iy] = coloring(i, 256);
            return;
        }
    }
    d_color[ix*sh+iy] = make_uchar4(0, 0, 0, 0);
}

void write_mandelbrot(const string outfile, float t, float l, float w, float h, int sw, int sh, int max_iter=256, float th=2.0f)
{
    dim3 num_blocks((sw - 1) / BLOCK_SIZE_X + 1, (sh - 1) / BLOCK_SIZE_Y + 1, 1);
    dim3 num_threads(BLOCK_SIZE_X, BLOCK_SIZE_Y, 1);

    uchar4* h_color;
    uchar4* d_color&lt;span style="color:red;"&gt;[2]&lt;/span&gt;;
    &lt;span style="color:red;"&gt;cudaSetDevice(0);&lt;/span&gt;
    cudaMallocHost((void**)&amp;amp;h_color, sizeof(uchar4) * sw * sh);
    &lt;span style="color:red;"&gt;for (int i = 0; i &amp;lt; 2; i++) {
        cudaSetDevice(i);&lt;/span&gt;
        cudaMalloc((void**)&amp;amp;d_color&lt;span style="color:red;"&gt;[i]&lt;/span&gt;, sizeof(uchar4) * sw * sh &lt;span style="color:red;"&gt;/ 2&lt;/span&gt;);
    &lt;span style="color:red;"&gt;}&lt;/span&gt;

    unsigned int timer;
    cutCreateTimer(&amp;amp;timer);
    cutStartTimer(timer);
    &lt;span style="color:red;"&gt;for (int i = 0; i &amp;lt; 2; i++) {
        cudaSetDevice(i);&lt;/span&gt;
        mandelbrot&amp;lt;&amp;lt;&amp;lt;num_blocks, num_threads&amp;gt;&amp;gt;&amp;gt;(t, l &lt;span style="color:red;"&gt;+ (w / 2) * i&lt;/span&gt;, w &lt;span style="color:red;"&gt;/ 2&lt;/span&gt;, h, sw &lt;span style="color:red;"&gt;/ 2&lt;/span&gt;, sh, max_iter, th, d_color&lt;span style="color:red;"&gt;[i]&lt;/span&gt;);
    &lt;span style="color:red;"&gt;}&lt;/span&gt;
    cudaThreadSynchronize();
    cutStopTimer(timer);
    cout &amp;lt;&amp;lt; "Time: " &amp;lt;&amp;lt; cutGetTimerValue(timer) / 1000 &amp;lt;&amp;lt; endl;

    &lt;span style="color:red;"&gt;for (int i = 0; i &amp;lt; 2; i++)&lt;/span&gt;
        cudaMemcpy(h_color &lt;span style="color:red;"&gt;+ sw * sh / 2 * i&lt;/span&gt;, d_color&lt;span style="color:red;"&gt;[i]&lt;/span&gt;, sizeof(uchar4) * sw * sh &lt;span style="color:red;"&gt;/ 2&lt;/span&gt;, &lt;span style="color:red;"&gt;cudaMemcpyDefault&lt;/span&gt;);

    fstream fs(outfile.c_str(), ios_base::out);
    fs &amp;lt;&amp;lt; sw &amp;lt;&amp;lt; endl;
    fs &amp;lt;&amp;lt; sh &amp;lt;&amp;lt; endl;
    for (int i = 0; i &amp;lt; sw * sh; i++)
        fs &amp;lt;&amp;lt; h_color[i].x &amp;lt;&amp;lt; h_color[i].y &amp;lt;&amp;lt; h_color[i].z;
    fs.close();

    &lt;span style="color:red;"&gt;for (int i = 0; i &amp;lt; 2; i++) {
        cudaSetDevice(i);&lt;/span&gt;
        cudaFree(d_color&lt;span style="color:red;"&gt;[i]&lt;/span&gt;);
    &lt;span style="color:red;"&gt;}
    cudaSetDevice(0);&lt;/span&gt;
    cudaFreeHost(h_color);
}

int main(int argc, char* argv[])
{
    string outfile("mandelbrot.dat");
    if (argc &amp;gt;= 2) outfile = argv[1];
    
    // for UVA
    &lt;span style="color:red;"&gt;cudaSetDevice(0);
    cudaDeviceEnablePeerAccess(1, 0);
    cudaSetDevice(1);
    cudaDeviceEnablePeerAccess(0, 0);&lt;/span&gt;

    // write_mandelbrot(output filename,
    //                  top position,
    //                  left position,
    //                  width,
    //                  height,
    //                  screen width,
    //                  screen height,
    //                  max number of iterations [256]
    //                  threshold [2.0]);

    //write_mandelbrot(outfile, -1.0f, -1.5f, 2.0f, 2.0f, 480, 480, 100);
    //write_mandelbrot(outfile, 0.57f, 0.34f, 0.036f, 0.027f, 6400, 4800, 1000);
    write_mandelbrot(outfile, -0.005f, -0.005f, 0.01f, 0.01f, 4800, 4800, 10000);

    return 0;
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

出力ファイルの画像への変換は&lt;a href="http://handasse.blogspot.com/2011/02/gpu.html"&gt;複数のGPUでマンデルブロ集合を並列計算&lt;/a&gt;の記事に記載されているread_mandelbrot.pyが利用できる。&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-8996017614539605761?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/8996017614539605761/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=8996017614539605761' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8996017614539605761'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8996017614539605761'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/06/cuda-40gpu.html' title='CUDA 4.0でマルチGPU化が限りなく簡単になっているという話'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-51966318054904482</id><published>2011-05-13T01:26:00.002+09:00</published><updated>2011-07-09T19:00:48.303+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>C++: Base64エンコード・デコードとURLエンコード</title><content type='html'>覚書。Base64エンコード・デコードを行う関数とURLエンコードを行う関数をどんな環境でもすぐに参照できるように書いておく。ただし、短いコードのままにしておきたかったので、まったくもって安全ではない。入力前にチェックを入れるとか、関数内で制限を掛けるとか、引数に最大文字数を渡すとか、適当な処理を入れること。よく分からない場合は使わないのが吉。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cstring&amp;gt;

using namespace std;

int base64encode(const char* p, char* buf)
{
    const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    const int in_size = strlen(p);
    if (in_size == 0 || p == buf) return 0;

    int cnt = 0;
    for (int i = 0; i &amp;lt; (in_size - 1) / 3 + 1; i++) {
        const unsigned char* c = reinterpret_cast&amp;lt;const unsigned char*&amp;gt;(&amp;amp;p[i*3]);
        for (int j = 0; j &amp;lt; 4; j++)
            if (in_size &amp;gt;= i * 3 + j) buf[cnt++] = b64[(c[j-1]&amp;lt;&amp;lt;(6-j*2) | c[j]&amp;gt;&amp;gt;(j*2+2)) &amp;amp; 0x3f];
            else buf[cnt++] = '='; 
    }
    buf[cnt] = '\0';

    return cnt;
}


int base64decode(const char* p, char* buf)
{
    char b64[128] = {};
    for (char n = 0, *w = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; *w; b64[*w++] = n++ &amp;amp; 0x3f);

    char c[4];
    int i = 0, j, k;
    while (*p &amp;amp;&amp;amp; *p != '=') {
        for (j = 0; j &amp;lt; 4 &amp;amp;&amp;amp; *p != '='; j++) c[j] = b64[*p++];
        for (k = 0; k &amp;lt; j - 1; k++) buf[i++] = c[k]&amp;lt;&amp;lt;(k&amp;lt;&amp;lt;1)+2 | c[k+1]&amp;gt;&amp;gt;(2-k&amp;lt;&amp;lt;1);
    }
    buf[i] = '\0';

    return i;
}

string urlencode(const string&amp;amp; text)
{
    const string url_letters("-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~");
    stringstream ss;

    for (string::const_iterator p = text.begin(); p != text.end(); ++p) {
        if (binary_search(url_letters.begin(), url_letters.end(), *p)) ss &amp;lt;&amp;lt; *p;
        else ss &amp;lt;&amp;lt; "%" &amp;lt;&amp;lt; setw(2) &amp;lt;&amp;lt; setfill('0') &amp;lt;&amp;lt; hex &amp;lt;&amp;lt; static_cast&amp;lt;int&amp;gt;(*p);
    }

    return ss.str();
}

int main()
{
    // Base64エンコードでは入力文字数の4/3倍ほどの領域が必要.
    // 文字数が少なかったり改行を入れるともっと必要.
    char buf[256]; // 256は安全ではない.

    base64encode("test", buf); // (buf, buf)はダメ.
    cout &amp;lt;&amp;lt; buf &amp;lt;&amp;lt; endl;       // dGVzdA==

    base64decode(buf, buf);    // 邪悪.
    cout &amp;lt;&amp;lt; buf &amp;lt;&amp;lt; endl;       // test

    cout &amp;lt;&amp;lt; urlencode("user@test.com") &amp;lt;&amp;lt; endl; // user%40test.com

    return 0;
}&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-51966318054904482?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/51966318054904482/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=51966318054904482' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/51966318054904482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/51966318054904482'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/05/c-base64url.html' title='C++: Base64エンコード・デコードとURLエンコード'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-8169053137125773135</id><published>2011-04-24T02:00:00.004+09:00</published><updated>2011-04-24T02:54:44.417+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='科学'/><category scheme='http://www.blogger.com/atom/ns#' term='アルゴリズム'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>タンパク質分子において水素原子の位置が異なるアミノ酸構造の同型判定</title><content type='html'>今回は興味を持ってもらえる人が限定される記事だと思うけど、以前作ったコードを腐らせてしまうのも勿体無いので敢えて晒してみる。&lt;br /&gt;&lt;br /&gt;

生体内のタンパク質構造をX線回折やNMRで解析したデータの多くは&lt;a href="http://www.pdb.org/"&gt;PDB (Protein Data Bank)データ&lt;/a&gt;として登録される。以下に示すように原子タグ、原子の通し番号、原子名、アミノ酸名、アミノ酸の通し番号、原子の座標などがテキストで書かれている。そして、機能の解明などのために&lt;a href="http://ja.wikipedia.org/wiki/%E5%88%86%E5%AD%90%E5%8B%95%E5%8A%9B%E5%AD%A6%E6%B3%95"&gt;分子動力学(MD; molecular dynamics)&lt;/a&gt;計算プログラムなどでそれらのPDBデータは利用される。MD計算プログラムは世に沢山あるが、自分がよく使っていたのは&lt;a href="http://ambermd.org/"&gt;Amber&lt;/a&gt;というソフトウェアだ。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;PDBデータの一部&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;ATOM   1439  N   ILE   212      52.408  -3.243  59.013  1.00 40.03           N  
ATOM   1440  CA  ILE   212      52.511  -3.151  57.556  1.00 33.72           C  
ATOM   1441  C   ILE   212      51.464  -2.243  56.911  1.00 34.65           C  
ATOM   1442  O   ILE   212      51.733  -1.619  55.886  1.00 38.03           O  
ATOM   1443  CB  ILE   212      52.482  -4.572  56.921  1.00 38.88           C  
ATOM   1444  CG1 ILE   212      53.615  -4.703  55.919  1.00 40.75           C  
ATOM   1445  CG2 ILE   212      51.129  -4.901  56.281  1.00 39.55           C  
ATOM   1446  CD1 ILE   212      54.931  -4.446  56.550  1.00 46.31           C  
ATOM   1447  N   HIS   213      50.308  -2.142  57.570  1.00 36.88           N  
ATOM   1448  CA  HIS   213      49.160  -1.334  57.169  1.00 37.60           C  
ATOM   1449  C   HIS   213      49.523   0.121  56.879  1.00 39.63           C  
ATOM   1450  O   HIS   213      48.887   0.773  56.028  1.00 39.63           O  
ATOM   1451  CB  HIS   213      48.115  -1.389  58.289  1.00 44.39           C  
ATOM   1452  CG  HIS   213      46.795  -0.746  57.947  1.00 55.55           C  
ATOM   1453  ND1 HIS   213      46.364  -0.536  56.651  1.00 54.19           N  
ATOM   1454  CD2 HIS   213      45.800  -0.306  58.748  1.00 53.97           C  
ATOM   1455  CE1 HIS   213      45.166   0.002  56.670  1.00 51.63           C  
ATOM   1456  NE2 HIS   213      44.787   0.157  57.930  1.00 44.85           N  
ATOM   1457  N   CYS   214      50.530   0.634  57.584  1.00 37.10           N  
ATOM   1458  CA  CYS   214      50.986   2.011  57.385  1.00 37.27           C  
ATOM   1459  C   CYS   214      51.504   2.255  55.949  1.00 38.90           C  
ATOM   1460  O   CYS   214      51.699   3.400  55.518  1.00 39.15           O  
ATOM   1461  CB  CYS   214      52.037   2.389  58.443  1.00 36.90           C  
ATOM   1462  SG  CYS   214      53.736   1.793  58.215  1.00 34.05           S&lt;/span&gt;&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
タンパク質はアミノ酸の集合体である。アミノ酸の分子構造はよく知られているが、一部のアミノ酸については水素が結合する位置が異なることがある。例えば&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%92%E3%82%B9%E3%83%81%E3%82%B8%E3%83%B3"&gt;ヒスチジン(HIS)&lt;/a&gt;というアミノ酸では、以下の3種が存在することが知られている(図中の3文字記号はAmberで使われる名称)。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-RmyyiotHrZo/TbMKLXpRksI/AAAAAAAAAWU/Zt0ObNvpYzs/s1600/his01.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 212px;" src="http://4.bp.blogspot.com/-RmyyiotHrZo/TbMKLXpRksI/AAAAAAAAAWU/Zt0ObNvpYzs/s400/his01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5598829952193630914" /&gt;&lt;/a&gt;&lt;br /&gt;

ここでタンパク質に対するMD計算について考えてみる。MD計算ではアミノ酸ごとに計算のためのデータベースを持っており、それを利用して計算される。なので、同じヒスチジンでも水素の付加の位置によって異なるデータが使われる。しかし、PDBデータの多くは水素が付加されておらず(X線結晶構造解析では水素の位置は特定できない)、計算前にアミノ酸の配置や想定されるpHなどから水素を付加させる必要がある。そして、ヒスチジン毎に異なる位置に水素が付加されるわけだ。&lt;br /&gt;&lt;br /&gt;

異なる位置に水素が付加されたヒスチジンに対して、正しい計算データをMD計算プログラムに伝えるためにそのアミノ酸の名前を変更する必要があるのだが、これが結構な手間となる。例えば、1000個以上のタンパク質構造からヒスチジン分子をビューアで確認してその水素の位置からアミノ酸の名前を付け替えるという作業なんて誰もやりたくないだろう。&lt;br /&gt;&lt;br /&gt;

そこで、水素の位置の異なるアミノ酸を自動的に判定してアミノ酸の名前を付け直すプログラムを作った。このコードでは&lt;a href="http://en.wikipedia.org/wiki/Subgraph_isomorphism_problem"&gt;部分グラフ同型判定アルゴリズム&lt;/a&gt;を利用している。使用したアルゴリズムをここで説明するのは面倒なので、興味のある方は&lt;a href="http://www.springerlink.com/content/4751121q3575v041/"&gt;V. Lipets, N. Vanetik, and E. Gudes. Subsea: an efficient heuristic algorithm for subgraph isomorphism. &lt;i&gt;Data Min. Knowl. Disc.&lt;/i&gt; &lt;b&gt;19&lt;/b&gt;. 320-350 (2009)&lt;/a&gt;あたりを参考にしてほしい。&lt;br /&gt;&lt;br /&gt;

使い方は次の通り。以下に示す例では、input.pdbが対象とするPDBデータで、名前がHISとなっているアミノ酸について判定する。判定するためのリファレンス構造は3種類で、HID.pdb、HIE.pdb, HIP.pdbの3つのPDB構造に対して行う。構造と一致したら、それぞれアミノ酸名をHID、HIE、HIPに置き換える。最終的に修正した構造をoutput.pdbとして出力する。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;% matching_residue input.pdb output.pdb HIS HID HID.pdb HIE HIE.pdb HIP HIP.pdb&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

標準出力には以下のような表示が出る。この表示の場合、入力PDBファイルには6個のHISがあり、それぞれ17, 66, 132, 146, 167, 261番目のアミノ酸で、HIEとHIPに変換されたことを示している。ここでは判定するための対象としてヒスチジンを例に取っているが、&lt;a href="http://ja.wikipedia.org/wiki/%E3%82%A2%E3%82%B9%E3%83%91%E3%83%A9%E3%82%AE%E3%83%B3%E9%85%B8"&gt;アスパラギン酸&lt;/a&gt;や&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%AA%E3%82%B7%E3%83%B3"&gt;リシン&lt;/a&gt;でも構わないし、アミノ酸でなくてもいい。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;Number of HIS: 6
HIS   17 -&gt; HIE
HIS   66 -&gt; HIE
HIS  132 -&gt; HIE
HIS  146 -&gt; HIP
HIS  167 -&gt; HIE
HIS  261 -&gt; HIP&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

今回に処理については、必ずしも部分グラフとして判定する必要はないのだが、今後の拡張のため、例えばアミノ酸の一部から正しい構造を判別する場合などについても考慮して部分グラフとして扱えるようにしている。&lt;br /&gt;&lt;br /&gt;

最後に今回のプログラムのソースコードを示す。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;matching_residue.cpp&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;// matching_residue.cpp  by nox, 2011.04.18

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;fstream&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;utility&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;limits&amp;gt;
#include &amp;lt;cmath&amp;gt;

using namespace std;

struct Node {
    vector&amp;lt;Node*&amp;gt; nb;    // Node list of neighbor atoms ([Node])
    int idx;             // index of the atom in the residue (int)
    int d;               // index of traverse history (int)
    vector&amp;lt;int&amp;gt; t;       // neighbors of traverse history ([int])
    string atom;         // atom type (string)
    int res_id;          // residue id of the PDB (int)
    vector&amp;lt;double&amp;gt; crd;  // coordinate of the atom ([float,float,float])

    Node(int idx_, string atom_, int res_id_, vector&amp;lt;double&amp;gt;&amp;amp; crd_)
        : idx(idx_), d(-1), atom(atom_), res_id(res_id_), crd(crd_) { }
};

struct PdbData {
    string atom;
    int res_id;
    vector&amp;lt;double&amp;gt; crd;

    PdbData(string line) {
        stringstream ss;
        double d;

        atom = line.substr(76, 2);
        atom.erase(0, atom.find_first_not_of(' '));
        atom.erase(atom.find_last_not_of(' ') + 1);
        ss &amp;lt;&amp;lt; line.substr(20, 6);  ss &amp;gt;&amp;gt; res_id;
        ss.str("");  ss.clear();
        ss &amp;lt;&amp;lt; line.substr(30, 8);  ss &amp;gt;&amp;gt; d;  crd.push_back(d);
        ss.str("");  ss.clear();
        ss &amp;lt;&amp;lt; line.substr(38, 8);  ss &amp;gt;&amp;gt; d;  crd.push_back(d);
        ss.str("");  ss.clear();
        ss &amp;lt;&amp;lt; line.substr(46, 8);  ss &amp;gt;&amp;gt; d;  crd.push_back(d);
        ss.str("");  ss.clear();
    }
};

// comparison function for sorting of traverse history
class LessNeighborD {
public:
    bool operator()(const Node* x, const Node* y) {
        int x_min_d, y_min_d;
        x_min_d = y_min_d = numeric_limits&amp;lt;int&amp;gt;::max();
        for (vector&amp;lt;Node*&amp;gt;::const_iterator p = x-&amp;gt;nb.begin(); p != x-&amp;gt;nb.end(); ++p) {
            if ((*p)-&amp;gt;d == -1) continue;
            x_min_d = min(x_min_d, (*p)-&amp;gt;d);
        }
        for (vector&amp;lt;Node*&amp;gt;::const_iterator p = y-&amp;gt;nb.begin(); p != y-&amp;gt;nb.end(); ++p) {
            if ((*p)-&amp;gt;d == -1) continue;
            y_min_d = min(y_min_d, (*p)-&amp;gt;d);
        }
        return x_min_d &amp;lt; y_min_d;
    }
};

class GreaterNbSize {
public:
    bool operator()(const Node* x, const Node* y) {
        return x-&amp;gt;nb.size() &amp;gt; y-&amp;gt;nb.size();
    }
};

class LessD {
public:
    bool operator()(const Node* x, const Node* y) {
        return x-&amp;gt;d &amp;lt; y-&amp;gt;d;
    }
};

class Matching {
private:
    // visit time of traverse history
    int vtime;
    // mapping between target and the replacing residue ((Node,Node))
    vector&amp;lt;pair&amp;lt;Node*, Node*&amp;gt; &amp;gt; mapping;
    map&amp;lt;pair&amp;lt;string, string&amp;gt;, double&amp;gt; bond_dist;

public:
    Matching();
    double get_bond_dist(const Node* v1, const Node* v2);
    double distance(const Node* v1, const Node* v2);
    void connect(vector&amp;lt;Node*&amp;gt;&amp;amp; nodes);
    void traverse_history(Node* node);
    void clear_traverse_history(vector&amp;lt;Node*&amp;gt;&amp;amp; nodes);
    vector&amp;lt;pair&amp;lt;string, vector&amp;lt;string&amp;gt; &amp;gt; &amp;gt; parse_pdb(string inpdb);
    vector&amp;lt;vector&amp;lt;Node*&amp;gt; &amp;gt; create_nodes_list(vector&amp;lt;pair&amp;lt;string, vector&amp;lt;string&amp;gt; &amp;gt; &amp;gt;&amp;amp; all_lines, vector&amp;lt;string&amp;gt;&amp;amp; rep_res);
    bool match(Node* g, vector&amp;lt;Node*&amp;gt;&amp;amp; h, int depth);
};

Matching::Matching() : vtime(0)
{
    // table of max bond distances
    bond_dist[make_pair("C", "C")] = 2.0;
    bond_dist[make_pair("C", "N")] = 1.8;
    bond_dist[make_pair("C", "O")] = 1.8;
    bond_dist[make_pair("C", "H")] = 1.2;
    bond_dist[make_pair("N", "N")] = 1.8;
    bond_dist[make_pair("N", "O")] = 1.8;
    bond_dist[make_pair("H", "N")] = 1.2;
    bond_dist[make_pair("O", "O")] = 1.8;
    bond_dist[make_pair("H", "O")] = 1.2;
}

double Matching::get_bond_dist(const Node* v1, const Node* v2)
{
    pair&amp;lt;string, string&amp;gt; atom_pair(make_pair(min(v1-&amp;gt;atom, v2-&amp;gt;atom), max(v1-&amp;gt;atom, v2-&amp;gt;atom)));
    if (bond_dist.find(atom_pair) != bond_dist.end()) return bond_dist[atom_pair];
    return 0.0;
}

double Matching::distance(const Node* v1, const Node* v2)
{
    double d = 0.0;
    for (int i = 0; i &amp;lt; 3; i++) {
        double dv = v1-&amp;gt;crd[i] - v2-&amp;gt;crd[i];
        d += dv * dv;
    }
    return sqrt(d);
}

// create connect list for neighbor atoms
void Matching::connect(vector&amp;lt;Node*&amp;gt;&amp;amp; nodes)
{
    for (vector&amp;lt;Node*&amp;gt;::iterator p = nodes.begin(); p != nodes.end(); ++p) {
        for (vector&amp;lt;Node*&amp;gt;::iterator q = nodes.begin(); q != nodes.end(); ++q) {
            if (*p == *q || (*p)-&amp;gt;idx &amp;gt; (*q)-&amp;gt;idx) continue;
            if (distance(*p, *q) &amp;lt; get_bond_dist(*p, *q)) {
                (*p)-&amp;gt;nb.push_back(*q);
                (*q)-&amp;gt;nb.push_back(*p);
            }
        }
    }
}

// traverse history, similar to canonical labeling
void Matching::traverse_history(Node* node)
{
    node-&amp;gt;d = vtime;
    vtime++;
    vector&amp;lt;Node*&amp;gt; nb;
    for (vector&amp;lt;Node*&amp;gt;::iterator p = node-&amp;gt;nb.begin(); p != node-&amp;gt;nb.end(); ++p) {
        if ((*p)-&amp;gt;d == -1) nb.push_back(*p);
        else node-&amp;gt;t.push_back((*p)-&amp;gt;d);
    }
    sort(node-&amp;gt;t.begin(), node-&amp;gt;t.end());
    sort(nb.begin(), nb.end(), LessNeighborD());
    for (vector&amp;lt;Node*&amp;gt;::iterator p = nb.begin(); p != nb.end(); ++p)
        if ((*p)-&amp;gt;d == -1) traverse_history(*p);
}

void Matching::clear_traverse_history(vector&amp;lt;Node*&amp;gt;&amp;amp; nodes)
{
    for (vector&amp;lt;Node*&amp;gt;::iterator p = nodes.begin(); p != nodes.end(); ++p) {
        (*p)-&amp;gt;d = -1;
        (*p)-&amp;gt;t.clear();
    }
    mapping.clear();
}

vector&amp;lt;pair&amp;lt;string, vector&amp;lt;string&amp;gt; &amp;gt; &amp;gt; Matching::parse_pdb(string inpdb)
{
    int res_id, prev_res_id = -1;
    vector&amp;lt;string&amp;gt; lines;
    vector&amp;lt;pair&amp;lt;string, vector&amp;lt;string&amp;gt; &amp;gt; &amp;gt; all_lines;
    string res, line;
    stringstream ss;

    fstream fs(inpdb.c_str(), ios_base::in);
    while (getline(fs, line)) {
        ss.str("");  ss.clear();
        if (line.length() &amp;gt; 20 &amp;amp;&amp;amp; line.substr(0, 4) == "ATOM") { ss &amp;lt;&amp;lt; line.substr(20, 6); ss &amp;gt;&amp;gt; res_id; }
        else res_id = -1;
        if (prev_res_id == res_id) lines.push_back(line);
        else {
            if (!lines.empty()) all_lines.push_back(make_pair(res, lines));
            lines.clear();  lines.push_back(line);
        }
        if (line.length() &amp;gt; 20 &amp;amp;&amp;amp; line.substr(0, 4) == "ATOM") {
            res = line.substr(17, 3);
            res.erase(0, res.find_first_not_of(' '));
            res.erase(res.find_last_not_of(' ') + 1);
        } else res = "";
        prev_res_id = res_id;
    }
    all_lines.push_back(make_pair(res, lines));

    return all_lines;
}

vector&amp;lt;vector&amp;lt;Node*&amp;gt; &amp;gt; Matching::create_nodes_list(vector&amp;lt;pair&amp;lt;string, vector&amp;lt;string&amp;gt; &amp;gt; &amp;gt;&amp;amp; all_lines, vector&amp;lt;string&amp;gt;&amp;amp; rep_res)
{
    vector&amp;lt;vector&amp;lt;Node*&amp;gt; &amp;gt; nodes_list;
    vector&amp;lt;vector&amp;lt;PdbData&amp;gt; &amp;gt; pdb_data;

    for (vector&amp;lt;pair&amp;lt;string, vector&amp;lt;string&amp;gt; &amp;gt; &amp;gt;::iterator p = all_lines.begin(); p != all_lines.end(); ++p) {
        string res(p-&amp;gt;first);
        vector&amp;lt;PdbData&amp;gt; pdbs;
        for (vector&amp;lt;string&amp;gt;::iterator q = p-&amp;gt;second.begin(); q != p-&amp;gt;second.end(); ++q) {  // for l in lines
            if (q-&amp;gt;length() &amp;gt; 78 &amp;amp;&amp;amp; q-&amp;gt;substr(0, 4) == "ATOM" &amp;amp;&amp;amp; find(rep_res.begin(), rep_res.end(), res) != rep_res.end())
                pdbs.push_back(PdbData(*q));
        }
        pdb_data.push_back(pdbs);
    }
    for (vector&amp;lt;vector&amp;lt;PdbData&amp;gt; &amp;gt;::iterator p = pdb_data.begin(); p != pdb_data.end(); ++p) {
        if (p-&amp;gt;empty()) continue;
        vector&amp;lt;Node*&amp;gt; nodes;
        int idx = 0;
        for (vector&amp;lt;PdbData&amp;gt;::iterator q = p-&amp;gt;begin(); q != p-&amp;gt;end(); ++q)
            nodes.push_back(new Node(idx++, q-&amp;gt;atom, q-&amp;gt;res_id, q-&amp;gt;crd));
        connect(nodes);
        vtime = 0;
        traverse_history(nodes[0]);
        sort(nodes.begin(), nodes.end(), LessD());
        nodes_list.push_back(nodes);
    }

    return nodes_list;
}

bool Matching::match(Node* g, vector&amp;lt;Node*&amp;gt;&amp;amp; h, int depth)
{
    for (vector&amp;lt;Node*&amp;gt;::iterator p = g-&amp;gt;nb.begin(); p != g-&amp;gt;nb.end(); ++p)
        if ((*p)-&amp;gt;d != -1) g-&amp;gt;t.push_back((*p)-&amp;gt;d);
    if (h[depth]-&amp;gt;t.size() != g-&amp;gt;t.size() || h[depth]-&amp;gt;atom != g-&amp;gt;atom) {
        g-&amp;gt;t.clear();
        return false;
    }

    g-&amp;gt;d = depth;
    sort(g-&amp;gt;t.begin(), g-&amp;gt;t.end());
    if (g-&amp;gt;t == h[depth]-&amp;gt;t) {
        mapping.push_back(make_pair(h[depth], g));
        if (mapping.size() == h.size()) return true;
        vector&amp;lt;Node*&amp;gt; nb(mapping[h[depth+1]-&amp;gt;t[0]].second-&amp;gt;nb);
        sort(nb.begin(), nb.end());
        vector&amp;lt;Node*&amp;gt;::iterator end = nb.end();
        for (int i = 1; i &amp;lt; h[depth+1]-&amp;gt;t.size(); i++) {
            vector&amp;lt;Node*&amp;gt; nb2(mapping[h[depth+1]-&amp;gt;t[i]].second-&amp;gt;nb);
            sort(nb2.begin(), nb2.end());
            end = set_intersection(nb.begin(), end, nb2.begin(), nb2.end(), nb.begin());
        }
        nb.erase(end, nb.end());
        sort(nb.begin(), nb.end(), GreaterNbSize());
        for (vector&amp;lt;Node*&amp;gt;::iterator p = nb.begin(); p != nb.end(); ++p)
            if ((*p)-&amp;gt;d == -1 &amp;amp;&amp;amp; match(*p, h, depth + 1))
                return true;
        mapping.pop_back();
    }
    g-&amp;gt;d = -1;
    g-&amp;gt;t.clear();

    return false;
}

int main(int argc, char* argv[])
{
    string inpdb, outpdb, target;
    vector&amp;lt;pair&amp;lt;string, string&amp;gt; &amp;gt; res_pdb_list;

    if (argc &amp;lt; 6 || (argc &amp;amp; 1)) {
        cerr &amp;lt;&amp;lt; "Usage: " &amp;lt;&amp;lt; argv[0] &amp;lt;&amp;lt; " input_pdb output_pdb target_res replace_res1 replace_res1_pdb [replace_res2 replace_res2_pdb...]" &amp;lt;&amp;lt; endl;
        cerr &amp;lt;&amp;lt; "  ex.) " &amp;lt;&amp;lt; argv[0] &amp;lt;&amp;lt; " input.pdb output.pdb HIS HID HID.pdb HIE HIE.pdb HIP HIP.pdb" &amp;lt;&amp;lt; endl;
        exit(1);
    }

    inpdb = argv[1];
    outpdb = argv[2];
    target = argv[3];
    for (int i = 4; i &amp;lt; argc; i += 2)
        res_pdb_list.push_back(make_pair(argv[i], argv[i+1]));

    Matching m;

    // calculate nodes of replacing residues
    map&amp;lt;string, vector&amp;lt;Node*&amp;gt; &amp;gt; rep_res;
    for (vector&amp;lt;pair&amp;lt;string, string&amp;gt; &amp;gt;::iterator p = res_pdb_list.begin(); p != res_pdb_list.end(); ++p) {
        vector&amp;lt;pair&amp;lt;string, vector&amp;lt;string&amp;gt; &amp;gt; &amp;gt; all_lines(m.parse_pdb(p-&amp;gt;second));
        vector&amp;lt;string&amp;gt; res;
        res.push_back(p-&amp;gt;first);
        vector&amp;lt;vector&amp;lt;Node*&amp;gt; &amp;gt; nodes_list(m.create_nodes_list(all_lines, res));
        rep_res[p-&amp;gt;first] = nodes_list[0];
    }

    // calculate nodes of target residues in the PDB
    vector&amp;lt;pair&amp;lt;string, vector&amp;lt;string&amp;gt; &amp;gt; &amp;gt; all_lines(m.parse_pdb(inpdb));
    vector&amp;lt;string&amp;gt; res;
    res.push_back(target);
    vector&amp;lt;vector&amp;lt;Node*&amp;gt; &amp;gt; nodes_list(m.create_nodes_list(all_lines, res));
    cout &amp;lt;&amp;lt; "Number of " &amp;lt;&amp;lt; target &amp;lt;&amp;lt; ": " &amp;lt;&amp;lt; nodes_list.size() &amp;lt;&amp;lt; endl;

    // matching residues
    vector&amp;lt;string&amp;gt; result;
    bool is_found = false;
    for (vector&amp;lt;vector&amp;lt;Node*&amp;gt; &amp;gt;::iterator p = nodes_list.begin(); p != nodes_list.end(); ++p) {
        is_found = false;
        for (map&amp;lt;string, vector&amp;lt;Node*&amp;gt; &amp;gt;::iterator q = rep_res.begin(); q != rep_res.end(); ++q) {
            if (p-&amp;gt;size() != q-&amp;gt;second.size()) continue;
            m.clear_traverse_history(*p);
            for (vector&amp;lt;Node*&amp;gt;::iterator r = p-&amp;gt;begin(); r != p-&amp;gt;end(); ++r) {
                if (m.match(*r, q-&amp;gt;second, 0)) {
                    cout &amp;lt;&amp;lt; target &amp;lt;&amp;lt; " " &amp;lt;&amp;lt; setw(4) &amp;lt;&amp;lt; (*r)-&amp;gt;res_id &amp;lt;&amp;lt; " -&amp;gt; " &amp;lt;&amp;lt; q-&amp;gt;first &amp;lt;&amp;lt; endl;
                    result.push_back(q-&amp;gt;first);
                    is_found = true;
                    break;
                }
            }
            if (is_found) break;
        }
        if (!is_found) {
            cout &amp;lt;&amp;lt; target &amp;lt;&amp;lt; " " &amp;lt;&amp;lt; setw(4) &amp;lt;&amp;lt; (*p)[0]-&amp;gt;res_id &amp;lt;&amp;lt; " -&amp;gt; Not found." &amp;lt;&amp;lt; endl;
            result.push_back(target);
        }
    }

    // write PDB
    fstream fs(outpdb.c_str(), ios_base::out);
    int idx = 0;
    for (vector&amp;lt;pair&amp;lt;string, vector&amp;lt;string&amp;gt; &amp;gt; &amp;gt;::iterator p = all_lines.begin(); p != all_lines.end(); ++p) {
        if (p-&amp;gt;first == target) {
            for (vector&amp;lt;string&amp;gt;::iterator q = p-&amp;gt;second.begin(); q != p-&amp;gt;second.end(); ++q) {
                size_t pos = q-&amp;gt;find(target);
                if (pos != string::npos) q-&amp;gt;replace(pos, target.size(), result[idx]);
                fs &amp;lt;&amp;lt; *q &amp;lt;&amp;lt; endl;
            }
            idx++;
        } else {
            for (vector&amp;lt;string&amp;gt;::iterator q = p-&amp;gt;second.begin(); q != p-&amp;gt;second.end(); ++q)
                fs &amp;lt;&amp;lt; *q &amp;lt;&amp;lt; endl;
        }
    }

    return 0;
}&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-8169053137125773135?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/8169053137125773135/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=8169053137125773135' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8169053137125773135'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8169053137125773135'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/04/blog-post.html' title='タンパク質分子において水素原子の位置が異なるアミノ酸構造の同型判定'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-RmyyiotHrZo/TbMKLXpRksI/AAAAAAAAAWU/Zt0ObNvpYzs/s72-c/his01.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-623232218943807889</id><published>2011-03-22T01:22:00.005+09:00</published><updated>2011-03-22T16:29:54.760+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='科学'/><title type='text'>東北地方太平洋沖地震 可視化地図</title><content type='html'>東日本大震災(東北関東大震災)により被害を受けられた方々には心よりお見舞い申し上げます。&lt;br /&gt;&lt;br /&gt;

以前、&lt;a href="http://handasse.blogspot.com/2009/12/blog-post.html"&gt;日本全国コンビニ店舗分布地図&lt;/a&gt;を&lt;a href="http://processing.org/"&gt;Processing&lt;/a&gt;で作ったが、今回の地震の震源とその大きさについて視覚的に表した地図を作成した。少しでも何かの役に立てば良いのだが。&lt;br /&gt;&lt;br /&gt;

震源とその大きさを&lt;a href="http://www.kabipan.com/geography/whitemap/index.html"&gt;日本地図&lt;/a&gt;上に円として表現している。円の大きさはマグニチュードと震源の深さに依存している(これは大まかな目安であり、揺れた地域や震度・エネルギーに正確に対応しているわけではない)。また、震源の深さによって円の色が変わり、浅ければ赤に、深ければ緑に近づく。海の色は時刻を現しており、正午が最も明るい青で表現され、深夜であれば真っ黒となる。データについては、&lt;a href="http://tenki.jp/"&gt;日本気象協会&lt;/a&gt;が提供している3月9日から20日までの&lt;a href="http://tenki.jp/earthquake/"&gt;地震情報&lt;/a&gt;を利用させて頂いた。&lt;br /&gt;&lt;br /&gt;

操作方法は、スペースキーでポーズ、'N'キーで3時間進め、'B'キーで3時間戻る。'+'キーで時間の進みが速くなり、'-'キーで遅くなる。マウスの左クリックでドラッグすれば地図を動かすことができ、右クリックで上下にドラッグすれば拡大・縮小となる。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;script type="text/javascript" src="http://www.java.com/js/deployJava.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
  /* &lt;![CDATA[ */
  var attributes = { code: 'earthquake.class', archive: 'http://www.futatsugi.net/develop/processing/earthquake/earthquake.jar', width: 539, height: 563, image: 'http://www.futatsugi.net/develop/processing/earthquake/loading.gif' };
  var parameters = { };
  var version = '1.5';
  deployJava.runApplet(attributes, parameters, version);
  /* ]]&gt; */
&lt;/script&gt;

&lt;noscript&gt;
&lt;!--[if !IE]&gt; --&gt;
&lt;object classid="java:earthquake.class" type="application/x-java-applet" archive="http://www.futatsugi.net/develop/processing/earthquake/earthquake.jar" width="539" height="563" standby="Loading Processing software..."&gt;
  &lt;param name="archive" value="http://www.futatsugi.net/develop/processing/earthquake/earthquake.jar" /&gt;
  &lt;param name="mayscript" value="true" /&gt;
  &lt;param name="scriptable" value="true" /&gt;
  &lt;param name="image" value="http://www.futatsugi.net/develop/processing/earthquake/loading.gif" /&gt;
  &lt;param name="boxmessage" value="Loading Processing software..." /&gt;
  &lt;param name="boxbgcolor" value="#FFFFFF" /&gt;
  &lt;param name="test_string" value="outer" /&gt;
&lt;!--&lt;![endif]--&gt;

&lt;object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" codebase="http://java.sun.com/update/1.6.0/jinstall-6u20-windows-i586.cab" width="539" height="563" standby="Loading Processing software..."&gt;
  &lt;param name="code" value="earthquake" /&gt;
  &lt;param name="archive" value="http://www.futatsugi.net/develop/processing/earthquake/earthquake.jar" /&gt;
  &lt;param name="mayscript" value="true" /&gt;
  &lt;param name="scriptable" value="true" /&gt;
  &lt;param name="image" value="http://www.futatsugi.net/develop/processing/earthquake/loading.gif" /&gt;
  &lt;param name="boxmessage" value="Loading Processing software..." /&gt;
  &lt;param name="boxbgcolor" value="#FFFFFF" /&gt;
  &lt;param name="test_string" value="inner" /&gt;

  &lt;p&gt;&lt;strong&gt;This browser does not have a Java Plug-in.&lt;br /&gt;&lt;a href="http://www.java.com/getjava" title="Download Java Plug-in"&gt;Get the latest Java Plug-in here.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/object&gt;
&lt;!--[if !IE]&gt; --&gt;
&lt;/object&gt;
&lt;!--&lt;![endif]--&gt;
&lt;/noscript&gt;
&lt;br /&gt;&lt;br /&gt;

可視化地図から、3月11日14時46分のマグニチュード9.0の地震の起こる二日ほど前から、三陸沖で中規模の地震が頻発していることが分かる。ところが、M9.0の地震の数時間前からは揺れが全て途絶え、嵐の前の静けさの様相となる。そして、M9.0の地震の直後からはひっきりなしに揺れが続き、数日してやや沈静化してきている。ただ、M6程度の地震は未だに起こっているので、安心するにはまだ早いかもしれない。最近の地震では福島県沖や茨城県沖に多く、ややと南下している様子が分かる。&lt;br /&gt;&lt;br /&gt;

以下、ソースコードを示す。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;earthquake.pde&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;/*
 earthquake.pde  by nox, 2011.3.22
*/

String convData = "quake.tsv.gz";
String japanMap = "japan.svg";
PShape mapShape;

int totalCount;
Place[] places;
int placeCount = 0;

final float minX = -0.19834;
final float maxX =  0.2425;
final float minY = -0.1875;
final float maxY =  0.1845;

PImage mapImage;
PFont font;

float offsetX = 0.0;
float offsetY = 0.0;
int centerX = 0;
int centerY = 0;
float zoom = 1.0;
int mxStart, myStart, myPos;
int startTime, currentTime, stopTime;
int month_, day_, hour_, minute_;
boolean is_paused = false;
int speed = 10;

public void setup() {
  mapShape = loadShape(japanMap);
  smooth();
  loop();
  size(int(mapShape.width), int(mapShape.height), JAVA2D); // 539, 563
  shapeMode(CORNER);
  mapShape.disableStyle();

  font = loadFont("CourierNewPSMT-14.vlw");
  textMode(SCREEN);
  textFont(font);

  readData();
  
  startTime = millis();
  loop();
}

public void draw() {
  if (!mousePressed) currentTime = millis() - startTime;
  int currentDate = currentTime + (hour_ * 60 + minute_) * speed;
  int mon_ = month_;
  int d_ = day_ + currentDate / (24 * 60 * speed);
  int h_ = currentDate / (60 * speed) % 24;
  int m_ = currentDate / speed % 60;
  if (d_ &amp;gt;= 21 || d_ &amp;lt;= 8 || h_ &amp;lt; 0 || m_ &amp;lt; 0) startTime = millis();
  
  background(color(0, 0, (720 - abs(h_ * 60 + m_ - 720)) * 0.1));
  noStroke();
  fill(32);
  shape(mapShape, int(offsetX * zoom) + centerX, int(offsetY * zoom) + centerY);
  fill(127);
  String timeFormat = nf(mon_, 2) + "/" + nf(d_, 2) + " " + nf(h_, 2) + ":" + nf(m_, 2) + "  speed: " + nf(10.0 / speed, 1, 2);
  text(timeFormat, 15, 20);
  for (int i = 0; i &amp;lt; placeCount; i++) places[i].draw();
}

float TX(float x) {
  float offset = width * (1.0 - zoom) * 0.5;
  return map(x, minX, maxX, offset, width - offset);
}

float TY(float y) {
  float offset = height * (1.0 - zoom) * 0.5;
  return map(y, minY, maxY, height - offset, offset);
}

void mousePressed() {
  if (!is_paused) {
    mxStart = int(mouseX - offsetX * zoom);
    myStart = int(mouseY - offsetY * zoom);
    myPos = mouseY;
    stopTime();
    loop();
  }
}

void mouseReleased() {
  if (!is_paused) startTime();
}

void mouseDragged() {
  if (is_paused) return;
  if (mouseButton == LEFT) {
    offsetX = (mouseX - mxStart) / zoom;
    offsetY = (mouseY - myStart) / zoom;
  } else if (mouseButton == RIGHT) {
    if (mouseY - myPos &amp;gt; 4) {
      float zoomOut = 9.8 / 10.0;
      mapShape.scale(zoomOut);
      zoom *= zoomOut;
      centerX = int(width * (1.0 - zoom) * 0.5);
      centerY = int(height * (1.0 - zoom) * 0.5);
      myPos = mouseY;
    } else if (mouseY - myPos &amp;lt; -4) {
      float zoom_in = 10.0 / 9.8;
      mapShape.scale(zoom_in);
      zoom *= zoom_in;
      centerX = int(width * (1.0 - zoom) * 0.5);
      centerY = int(height * (1.0 - zoom) * 0.5);
      myPos = mouseY;
    }
  }
}

void startTime() {
  startTime += millis() - stopTime;
  loop();
}

void stopTime() {
  noLoop();
  stopTime = millis();
}

void keyPressed() {
  if (key == ' ') {
    if (is_paused) {
      startTime();
      is_paused = false;
    } else {
      stopTime();
      is_paused = true;
    }
  } else if (key == 'n' || key == 'N') startTime -= 180 * speed;
  else if (key == 'b' || key == 'B') startTime += 180 * speed;
  else if (key == '+' &amp;amp;&amp;amp; speed &amp;gt; 3) speed -= 1;
  else if (key == '-' &amp;amp;&amp;amp; speed &amp;lt; 30) speed += 1;
}

void readData() {
  new Slurper();
}

Place parsePlace(String line) {
  String pieces[] = split(line, '\t');

  int mins = int(pieces[0]);
  int mon = int(pieces[1]);
  int d = int(pieces[2]);
  int h = int(pieces[3]);
  int m = int(pieces[4]);
  float x = float(pieces[5]);
  float y = float(pieces[6]);
  float magnitude = float(pieces[7]);
  float depth = float(pieces[8]);
  String name = pieces[9];

  return new Place(mins, mon, d, h, m, x, y, magnitude, depth, name);
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;Place.pde&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;class Place {
  int mins;
  int mon, d, h, m;
  float x, y;
  float magnitude;
  float depth;
  String name;

  public Place(int mins, int mon, int d, int h, int m, float x, float y, float magnitude, float depth, String name) {
    this.mins = mins;
    this.mon = mon;
    this.d = d;
    this.h = h;
    this.m = m;
    this.x = x;
    this.y = y;
    this.magnitude = magnitude;
    this.depth = depth;
    this.name = name;
  }

  public void draw() {
    int xx = (int)TX(this.x) + int(offsetX * zoom);
    int yy = (int)TY(this.y) + int(offsetY * zoom);
    int a = mins * speed + 255 - currentTime;
    if (magnitude == 0.0 || a &amp;lt; 0 || a &amp;gt; 255) return;
    float r = pow(5.0, (magnitude / 2.7)) / sqrt(depth / 10 + 1);
    float m = r * ((255 - a) / 50.0) * zoom;
    fill(color(255 - this.depth * 2, this.depth * 2, 0), a);
    ellipse(xx, yy, m, m);
  }
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;Slurper.pde&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;class Slurper implements Runnable {
  Slurper() {
    Thread thread = new Thread(this);
    thread.start();
  }

  public void run() {
    try {
      BufferedReader reader = createReader(convData);

      String line = reader.readLine();
      totalCount = int(line);

      places = new Place[totalCount];

      line = reader.readLine();
      places[placeCount++] = parsePlace(line);
      month_ = places[0].mon;
      day_ = places[0].d;
      hour_ = places[0].h;
      minute_ = places[0].m;
      while ((line = reader.readLine()) != null)
        places[placeCount++] = parsePlace(line);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-623232218943807889?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/623232218943807889/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=623232218943807889' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/623232218943807889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/623232218943807889'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/03/blog-post_22.html' title='東北地方太平洋沖地震 可視化地図'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-1807635584529158996</id><published>2011-03-07T01:18:00.002+09:00</published><updated>2011-03-07T01:38:17.205+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='雑記'/><title type='text'>開発エンジニアの世界へ</title><content type='html'>昨年は開発エンジニアが転職するという話題が多かったように思う。それに触発されたというわけではないが、自分も昨年11月に開発エンジニアとして転職した。そしてつい先日、試用期間の3ヶ月が過ぎた。これで何かが変わったというわけではないが、正式に採用されたということで、転職して思うことなどをブログに書くことにした。転職先については敢えて名前を出さないが、主にマルチコアやGPGPUなどの並列処理を扱うベンチャー企業といえばピンとくる人もいるかも知れない。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
自分はこれまでずっとアカデミックな研究所に所属していたのだけど、少々居座りすぎてしまったことは否めない。基本的に研究者は流動的であることが推奨される。そして、所属していた研究所は所長でさえも任期制で、研究員は普段から職探しをするのが当たり前になっている。しかし、アカデミックなポストは空きも少なく、枠が一人のところに100人200人と応募してくることはザラで、終身雇用の職を得るのはかなり難しい。&lt;br /&gt;&lt;br /&gt;

そんな中、自分はほとんど職探しをしていなかった。第一の理由として、自分が専攻した薬学の分野でバリバリとコードを書くことと研究が両立できるか疑問だったことがある。ただでさえ狭き枠にそのような特殊な環境を求めるのが非常に厳しいものに思えたからだ。第二の理由として、所属していた研究所ではメインでHPCを扱っており薬学に関連した研究を遂行するためのプログラムを自由に書くことができ、それが求められていたことがある。相手の要求と自分の希望が合致していた。&lt;br /&gt;&lt;br /&gt;

そうは言っても、ここ最近の政治的判断による不透明さや年齢に伴うマネージメントの必要性など、当たり前だが要求は厳しくなってくる。また、地理的な面でも大きく変わることになったし、こちらの事情で現在の住処を離れたくないということもあった。研究所で一緒に仕事をしていた人たちには大変お世話になっていたので少々迷うところもあったのだが、最終的に転職することにした。&lt;br /&gt;&lt;br /&gt;

自分の仕事選びの基準は単純で「楽しめるかどうか」だけだ。楽しめればどんなにきつい仕事でも苦にならないし、逆にきつければきついほどチャレンジングであり、やりがいを感じられる。特に開発エンジニアの方なら、自分の技術で困難な問題を克服する達成感、創造する喜び、知識を得る楽しみなどについて分かってもらえるのではないかと思う。それに、企業としても生産性が上がるわけだから、まさにWin-Winの関係だ。もし、エンジニアでこれらの喜びや楽しみを見いだせない人がいたとしたら、何故、極端な専門職であるエンジニアを選んだのか知りたいところだ。&lt;br /&gt;&lt;br /&gt;

今の職場は自分自身の意志による最初のエントリーだったが、有難い事にそこで働けることになった。何年も前から知っていて、とても技術力のある企業だと思ったからここを選んだ。そして、そう思ったことは間違っておらず、想像していたより遥かに楽しんでいる自分がいる。大企業とは違って小回りはきくし、比較的やりたい事をやらせてもらえるし(もちろん責任は伴う)、別プロジェクトの開発者とは同じフロアにいるので交流し易い。そしてなにより、この職場ではプログラミングコンテスト優勝者やICPC関係者、ネットで時々名前を見かけるような技術力の高い人たちがいて話を聞くだけでも楽しい。示し合わせたわけでもないのに自分を含めて数人、先日の&lt;a href="http://atnd.org/events/11551"&gt;Boost.勉強会&lt;/a&gt;に参加している事実を以てしても、技術に対して真摯であることが伺える(参加はしなかったがライブ配信を見ていたという人たちもいたようだ)。それに、個人だと到底いじることができないような特殊な環境に触れることができるのも良い(まあ、特殊な環境という意味では研究所にいたころも複数のスーパーコンピュータをいじっていたわけで、それはそれで楽しかったけど)。ついでに、報酬や待遇も悪くないと思う。&lt;br /&gt;&lt;br /&gt;

最近では若い人たちを積極的に採用しているようで会社説明会も開いている。プログラミング(特に並列処理)に興味を持っている方に参加してもらえると嬉しい。&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-1807635584529158996?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/1807635584529158996/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=1807635584529158996' title='3 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1807635584529158996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1807635584529158996'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/03/blog-post.html' title='開発エンジニアの世界へ'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-1457383911080505427</id><published>2011-02-15T02:39:00.002+09:00</published><updated>2011-02-15T02:50:37.376+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='科学'/><category scheme='http://www.blogger.com/atom/ns#' term='GPGPU'/><title type='text'>複数のGPUでマンデルブロ集合を並列計算</title><content type='html'>範囲(-0.005, -0.005, 0.005, 0.005)のマンデルブロ集合を描画サイズ4,800x4,800、繰り返しの上限10,000として計算させてみた。C++でコードを書いてXeon X5650 1コアで走らせたところ942.97秒かかった(SIMD最適化はしていない)。TBBを利用した12論理コアの並列計算では88.68秒だった。次いでCUDAを使ってTeslaで走らせてみたが7.45秒まで短縮された。Teslaパない。そして贅沢にも2枚のTeslaをスレッドで並列化して使ってみたら4.26秒で計算できた。実にXeon 1コアの220倍の速度が出たわけだ。カリカリチューンをしなくてもこの程度の高速化ができるということが重要だ。&lt;br /&gt;&lt;br /&gt;

因みにマンデルブロ集合の上記の範囲は数値が発散しない集合部分であり、描画させてみても真っ黒なので面白くはない。これは並列化した際に計算が偏らないようにしたかったのでこのような範囲を指定している。プログラムで出力させたファイルは色の生データなのでそのままでは表示できない。一応、画像表示するためのPythonコードも付け加えておく。&lt;br /&gt;&lt;br /&gt;

以下に、GPU2枚を利用してマンデルブロ集合を計算するCUDAコードを示しておく。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;strong&gt;mandelbrot_thread.cu&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;// Mandelbrot set using GPGPU  by nox, 2011.02.12
// nvcc -lcutil_x86_64 -arch sm_13 -use_fast_math -prec-sqrt=false -keep -L ~/NVIDIA_GPU_Computing_SDK/C/lib -I ~/NVIDIA_GPU_Computing_SDK/C/common/inc -g mandelbrot_thread.cu -o mandelbrot_thread

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;fstream&amp;gt;

#include &amp;lt;cutil_inline.h&amp;gt;
#include &amp;lt;multithreading.h&amp;gt;

using namespace std;

const int BLOCK_SIZE_X = 16;
const int BLOCK_SIZE_Y = 16;

struct MandelbrotParam {
    int dev;          // GPU device id
    uchar4* h_color;  // image buffer
    float t, l, w, h; // top, left, width, height
    int sw, sh;       // screen width, screen height
    int max_iter;     // max number of iterators
    float th;         // threshold
};

__device__
uchar4 coloring(int n, int b)
{
    int d = n % b;
    d *= 256 / b;
    int m = static_cast&amp;lt;int&amp;gt;(d / 42.667f);

    switch (m) {
    case 0: return make_uchar4(0, 6 * d, 255, 0);
    case 1: return make_uchar4(0, 255, 255 - 6 * (d - 43), 0);
    case 2: return make_uchar4(6 * (d - 86), 255, 0, 0);
    case 3: return make_uchar4(255, 255 - 6 * (d - 129), 0, 0);
    case 4: return make_uchar4(255, 0, 6 * (d - 171), 0);
    case 5: return make_uchar4(255 - 6 * (d - 214), 0, 255, 0);
    default: return make_uchar4(0, 0, 0, 0);
    }
}

__global__
void mandelbrot(float t, float l, float w, float h, int sw, int sh, int max_iter, float th, uchar4* d_color)
{
    int ix = blockIdx.x * blockDim.x + threadIdx.x;
    int iy = blockIdx.y * blockDim.y + threadIdx.y;
    if (ix &amp;gt;= sw || iy &amp;gt;= sh) return;

    float ci = t + (static_cast&amp;lt;float&amp;gt;(iy) / sh) * h;
    float cr = l + (static_cast&amp;lt;float&amp;gt;(ix) / sw) * w;
    float zi = 0.0f;
    float zr = 0.0f;
    float zrzi, zr2, zi2;

    for (int i = 0; i &amp;lt; max_iter; i++) {
        zrzi = zr * zi;
        zr2 = zr * zr;
        zi2 = zi * zi;
        zr = zr2 - zi2 + cr;
        zi = zrzi + zrzi + ci;
        if (zi2 + zr2 &amp;gt;= th) {
            d_color[ix*sh+iy] = coloring(i, 256);
            return;
        }
    }
    d_color[ix*sh+iy] = make_uchar4(0, 0, 0, 0);
}

CUT_THREADPROC mandelbrot_thread(void* args)
{
    MandelbrotParam* param = (MandelbrotParam*)args;

    cudaSetDevice(param-&amp;gt;dev);

    uchar4* h_color = param-&amp;gt;h_color;
    float t = param-&amp;gt;t;
    float l = param-&amp;gt;l;
    float w = param-&amp;gt;w;
    float h = param-&amp;gt;h;
    int sw = param-&amp;gt;sw;
    int sh = param-&amp;gt;sh;
    int max_iter = param-&amp;gt;max_iter;
    float th = param-&amp;gt;th;

    dim3 num_blocks((sw - 1) / BLOCK_SIZE_X + 1, (sh - 1) / BLOCK_SIZE_Y + 1, 1);
    dim3 num_threads(BLOCK_SIZE_X, BLOCK_SIZE_Y, 1);

    uchar4* d_color;
    cudaMalloc((void**)&amp;amp;d_color, sizeof(uchar4) * sw * sh);

    mandelbrot&amp;lt;&amp;lt;&amp;lt;num_blocks, num_threads&amp;gt;&amp;gt;&amp;gt;(t, l, w, h, sw, sh, max_iter, th, d_color);
    cudaThreadSynchronize();

    cudaMemcpy(h_color, d_color, sizeof(uchar4) * sw * sh, cudaMemcpyDeviceToHost);
    cudaFree(d_color);

    CUT_THREADEND;
}

// outfile  : output filename
// t        : top position
// l        : left position
// w        : width
// h        : height
// sw       : screen width
// sh       : screen height
// max_iter : max number of iterations [256]
// th       : threshold [2.0]
void write_mandelbrot(const string outfile, float t, float l, float w, float h, int sw, int sh, int max_iter=256, float th=2.0f)
{
    MandelbrotParam param[2];

    uchar4* h_color;
    cudaMallocHost((void**)&amp;amp;h_color, sizeof(uchar4) * sw * sh);

    // using 2 GPUs
    for (int i = 0; i &amp;lt; 2; i++) {
        param[i].dev = i;
        param[i].h_color = h_color;
        param[i].t = t;
        param[i].l = l;
        param[i].w = w / 2.0f;
        param[i].h = h;
        param[i].sw = sw / 2;
        param[i].sh = sh;
        param[i].max_iter = max_iter;
        param[i].th = th;
    }
    param[1].h_color = h_color + sw * sh / 2;
    param[1].l = l + w / 2.0f;

    unsigned int timer;
    cutCreateTimer(&amp;amp;timer);
    cutStartTimer(timer);

    CUTThread threads[2];
    for (int i = 0; i &amp;lt; 2; i++)
        threads[i] = cutStartThread((CUT_THREADROUTINE)mandelbrot_thread, (void*)&amp;amp;param[i]);
    cutWaitForThreads(threads, 2);

    cutStopTimer(timer);
    cout &amp;lt;&amp;lt; "Time: " &amp;lt;&amp;lt; cutGetTimerValue(timer) / 1000 &amp;lt;&amp;lt; endl;

    fstream fs(outfile.c_str(), ios_base::out);
    fs &amp;lt;&amp;lt; sw &amp;lt;&amp;lt; endl;
    fs &amp;lt;&amp;lt; sh &amp;lt;&amp;lt; endl;
    for (int i = 0; i &amp;lt; sw * sh; i++)
        fs &amp;lt;&amp;lt; h_color[i].x &amp;lt;&amp;lt; h_color[i].y &amp;lt;&amp;lt; h_color[i].z;
    fs.close();

    cudaFreeHost(h_color);
}

int main(int argc, char* argv[])
{
    string outfile("mandelbrot.dat");
    if (argc &amp;gt;= 2) outfile = argv[1];

    // write_mandelbrot(output filename,
    //                  top position,
    //                  left position,
    //                  width,
    //                  height,
    //                  screen width,
    //                  screen height,
    //                  max number of iterations [256]
    //                  threshold [2.0]);

    //write_mandelbrot(outfile, -1.0f, -1.5f, 2.0f, 2.0f, 480, 480, 100);
    //write_mandelbrot(outfile, 0.57f, 0.34f, 0.036f, 0.027f, 6400, 4800, 1000);
    write_mandelbrot(outfile, -0.005f, -0.005f, 0.01f, 0.01f, 4800, 4800, 10000);

    return 0;
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

表示用のPythonコード。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;read_mandelbrot.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#!/usr/bin/env python

import sys, os, Image

def main(args):
    if len(args) &amp;lt; 2:
        print &amp;gt;&amp;gt;sys.stderr, "%s datafile [imagefile]" % os.path.basename(args[0])
        sys.exit()
    datafile = args[1]
    imagefile = args[2] if len(args) &amp;gt; 2 else ""

    f = file(datafile)
    sw = int(f.readline())
    sh = int(f.readline())
    data = map(ord, f.read())
    im = Image.new("RGB", (sw, sh))
    for x in range(sw):
        for y in range(sh):
            pos = (x * sh + y) * 3;
            im.putpixel((x, sh - y - 1), tuple(data[pos:pos+3]))
    if imagefile: im.save(imagefile)
    im.show()

if __name__ == "__main__": main(sys.argv)&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-1457383911080505427?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/1457383911080505427/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=1457383911080505427' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1457383911080505427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1457383911080505427'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/02/gpu.html' title='複数のGPUでマンデルブロ集合を並列計算'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-4277508127926576462</id><published>2011-01-01T01:36:00.002+09:00</published><updated>2011-01-01T01:49:42.729+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='雑記'/><title type='text'>2010年から2011年へ</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://chart.apis.google.com/chart?cht=qr&amp;chs=230x230&amp;chl=new_year.py%0A%23+-*-+coding%3A+utf-8+-*-%0A%0Aimport+android%0A%0Adroid%3Dandroid.Android()%0Atext%3D'a+happy+new+year.+yo+ee+mo+no+wa+ru+ee+mo+no+wo+oh+yo+me+ee+ta+dar+kee+aa+lee+gaa+toe+go+za+ee+ma+su.+ko+toe+see+mo+yo+ro+see+ku+oh+ne+gaa+ee+see+ma+su.'%0Adroid.makeToast('A+Happy+New+Year!%5Cn%E3%80%8E%E8%89%AF%E3%81%84%E3%82%82%E3%81%AE%E3%80%82%E6%82%AA%E3%81%84%E3%82%82%E3%81%AE%E3%80%82%E3%80%8F%E3%82%92%E3%81%8A%E8%AA%AD%E3%81%BF%E9%A0%82%E3%81%8D%E3%81%82%E3%82%8A%E3%81%8C%E3%81%A8%E3%81%86%E3%81%94%E3%81%96%E3%81%84%E3%81%BE%E3%81%99%E3%80%82%5Cn%E4%BB%8A%E5%B9%B4%E3%82%82%E3%82%88%E3%82%8D%E3%81%97%E3%81%8F%E3%81%8A%E9%A1%98%E3%81%84%E3%81%97%E3%81%BE%E3%81%99%E3%80%82')%0Adroid.ttsSpeak(text)%0Adroid.webViewShow('http%3A%2F%2Fhandasse.blogspot.com%2F')"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 230px; height: 230px;" src="http://chart.apis.google.com/chart?cht=qr&amp;chs=230x230&amp;chl=new_year.py%0A%23+-*-+coding%3A+utf-8+-*-%0A%0Aimport+android%0A%0Adroid%3Dandroid.Android()%0Atext%3D'a+happy+new+year.+yo+ee+mo+no+wa+ru+ee+mo+no+wo+oh+yo+me+ee+ta+dar+kee+aa+lee+gaa+toe+go+za+ee+ma+su.+ko+toe+see+mo+yo+ro+see+ku+oh+ne+gaa+ee+see+ma+su.'%0Adroid.makeToast('A+Happy+New+Year!%5Cn%E3%80%8E%E8%89%AF%E3%81%84%E3%82%82%E3%81%AE%E3%80%82%E6%82%AA%E3%81%84%E3%82%82%E3%81%AE%E3%80%82%E3%80%8F%E3%82%92%E3%81%8A%E8%AA%AD%E3%81%BF%E9%A0%82%E3%81%8D%E3%81%82%E3%82%8A%E3%81%8C%E3%81%A8%E3%81%86%E3%81%94%E3%81%96%E3%81%84%E3%81%BE%E3%81%99%E3%80%82%5Cn%E4%BB%8A%E5%B9%B4%E3%82%82%E3%82%88%E3%82%8D%E3%81%97%E3%81%8F%E3%81%8A%E9%A1%98%E3%81%84%E3%81%97%E3%81%BE%E3%81%99%E3%80%82')%0Adroid.ttsSpeak(text)%0Adroid.webViewShow('http%3A%2F%2Fhandasse.blogspot.com%2F')" border="0" alt="" /&gt;&lt;/a&gt;
明けましておめでとうございます。&lt;br /&gt;&lt;br /&gt;

今年は2011年で素数。前回の素数の年は8年前の2003年、次の素数の年は6年後の2017年。だからどうと言うわけでもないのだけど、なんとなく特別な年であるように思えてくる。昨年は自分にとっては大きな節目の年であった。環境も大きく変わって、なんちゃってプログラマから「なんちゃって」を外すことができたのではないかと思う。そして今年は、どんな数にも割り込まれず自分自身を失わない2011年のように、自らの意志をしっかり持って行動する年にしたい。&lt;br /&gt;&lt;br /&gt;

ところで、冒頭のQRコードだけど、いつものように&lt;a href="http://code.google.com/p/android-scripting/"&gt;Android SL4A&lt;/a&gt;のソースコードになっている。SL4Aを起動して、メニューからAddを選択し、Scan Barcodeで取り込むと new_year.py が作られる。大したコードではないのだけど、当ブログからのメッセージが入っている。音が出るので注意。&lt;br /&gt;&lt;br /&gt;

これからもどうぞよろしくお願いします。&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-4277508127926576462?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/4277508127926576462/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=4277508127926576462' title='2 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4277508127926576462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4277508127926576462'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2011/01/20102011.html' title='2010年から2011年へ'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-629749306191105859</id><published>2010-12-29T22:16:00.003+09:00</published><updated>2010-12-29T22:57:46.641+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='アルゴリズム'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>ある重さを量るのに使う錘の組み合わせは何通り?</title><content type='html'>&lt;strong&gt;問題&lt;/strong&gt;: 1, 5, 10, 50, 100, 500グラムの6種類の錘があります。それぞれの錘はいくつでも使うことができます。ある重さを量るときに使う錘の組み合わせは何通りになりますか。それを求めるプログラムを書いてください。ここで量る重さはグラム単位の整数とし、最大で100キログラムとします。10グラムの重さを量る場合、10グラムの錘が1個、5グラムの錘が2個、5グラムの錘1個と1グラムの錘が5個、1グラムの錘が10個の組み合わせになり、全部で4通りとなります。&lt;br /&gt;&lt;br /&gt;

以下、解答例。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;

&lt;strong&gt;weights.cpp&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;// 指定した重さを量るのに使う錘の組み合わせの数を求めるプログラム.
//  錘は 1g, 5g, 10g, 50g, 100g, 500g の6種類.
//  重さ 700,000g 程度までなら桁あふれせずに計算できる.&lt;/span&gt;

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cstdlib&amp;gt;

using namespace std;

const int weights[] = { 1, 5, 10, 50, 100, 500 };  &lt;span style="color:gray;"&gt;// 錘(昇順).&lt;/span&gt;
const int N = sizeof(weights) / sizeof(int);  &lt;span style="color:gray;"&gt;// 錘の種類.&lt;/span&gt;

&lt;span style="color:gray;"&gt;// 求めた錘の組み合わせの数のメモ.
//  下位3ビットは使用する錘(solveのidx)を表す.
//  残りのビットは重さ(solveのv)を表す.&lt;/span&gt;
map&amp;lt;int, unsigned long long&amp;gt; memo;

&lt;span style="color:gray;"&gt;// 指定した重さに対する錘の組み合わせの数を求める.
//  v     : 重さ
//  idx   : 使用する錘
//          N-1の場合はweights[0]からweights[N-1]までの錘を使用する(すべての錘).
//          2の場合はweights[0]からweights[2]までの錘を使用する(1, 5, 10の錘).
//  戻り値: 組み合わせの数&lt;/span&gt;
unsigned long long solve(int v, int idx=N-1)
{
    &lt;span style="color:gray;"&gt;// 既に計算済みであるならその結果を返す.&lt;/span&gt;
    if (memo.find(v&amp;lt;&amp;lt;3|idx) != memo.end()) return memo[v&amp;lt;&amp;lt;3|idx];

    &lt;span style="color:gray;"&gt;// 重さが0になったのならば新たな組み合わせが1つ見つかった.&lt;/span&gt;
    if (v == 0) return 1;

    unsigned long long cnt = 0;
    &lt;span style="color:gray;"&gt;// 錘の種類だけループを回す.&lt;/span&gt;
    for (int i = idx; i &amp;gt;= 0; i--)
        &lt;span style="color:gray;"&gt;// 重さが錘よりも軽い場合は次の錘.&lt;/span&gt;
        if (v &amp;gt;= weights[i])
            &lt;span style="color:gray;"&gt;// 求めた組み合わせの数をカウントに加える.&lt;/span&gt;
            cnt += solve(v - weights[i], i);

    &lt;span style="color:gray;"&gt;// 求めた組み合わせの数をメモしておく.&lt;/span&gt;
    memo[v&amp;lt;&amp;lt;3|idx] = cnt;

    return cnt;
}

int main(int argc, char* argv[])
{
    if (argc &amp;lt; 2) {
        cerr &amp;lt;&amp;lt; "Usage: " &amp;lt;&amp;lt; argv[0] &amp;lt;&amp;lt; " [weight]" &amp;lt;&amp;lt; endl;
        exit(1);
    }

    cout &amp;lt;&amp;lt; solve(atoi(argv[1])) &amp;lt;&amp;lt; endl;

    return 0;
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

この手のアルゴリズムの問題に慣れた人なら簡単だと思う。自分はこれと同類の問題を初めて聞いたときに即座に書けなかったので自戒を込めてここで書いてみた。&lt;br /&gt;&lt;br /&gt;

ところで、このコードで使っているメモ化だが、これをしないと実用的には使うことはできない。例えば、2,000グラムの組み合わせを求めた場合、メモ化していれば自分の環境では0.002秒で答えが出るが、メモ化していないと85秒も掛かる。因みにこの場合の組み合わせは4,443,355通り。&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-629749306191105859?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/629749306191105859/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=629749306191105859' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/629749306191105859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/629749306191105859'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/12/blog-post.html' title='ある重さを量るのに使う錘の組み合わせは何通り?'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-6624507826572073748</id><published>2010-11-08T00:43:00.009+09:00</published><updated>2010-11-08T05:41:32.170+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Android端末による自動通訳を一行のPythonコードで実装する</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BwIshVLoLIU/TNbKBE0ncXI/AAAAAAAAAV4/d5FSg3tUURk/s1600/device01.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 192px; height: 320px;" src="http://4.bp.blogspot.com/_BwIshVLoLIU/TNbKBE0ncXI/AAAAAAAAAV4/d5FSg3tUURk/s320/device01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5536834911721714034" /&gt;&lt;/a&gt;
最近このブログではAndroidの&lt;a href="http://code.google.com/p/android-scripting/"&gt;SL4A&lt;/a&gt;ネタが多いのだけど、それだけ遊べるのだから仕方がない。SL4AのPythonを使えば実質一行で自動通訳プログラムを作ることもできる。日本語で喋った文章が翻訳されて英語の音声で返ってくる。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;import android,urllib,urllib2,simplejson;droid=android.Android();droid.ttsSpeak(simplejson.loads(urllib2.urlopen('http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&amp;amp;q=%s&amp;amp;langpair=%s%%7C%s'%(urllib.quote(droid.recognizeSpeech().result.encode('utf-8')),'ja','en')).read())['responseData']['translatedText'])&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

このプログラムでは日本語を英語に変換するが、コード中の en を変更すれば別の外国語に通訳することもできる。例えば fr に変更すればフランス語に通訳される。ただし、通訳した外国語はGoogleによる翻訳の精度に依存するので、実用的に用いるにはもう少し精度が良くなって欲しいところだ。それでもこれだけ簡単に自動通訳プログラムを作れるのだからAndroidとSL4Aは大したものだと思う。&lt;br /&gt;&lt;br /&gt;

以下にもう少し分かりやすいコードを示しておく。ついでに翻訳結果をダイアログに表示するように変更してある。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;translator.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;# -*- coding: utf-8 -*-

import android,urllib,urllib2,simplejson

def translator(text,from_lang,to_lang):
  url='http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&amp;amp;q=%s&amp;amp;langpair=%s%%7C%s'%(urllib.quote(text.encode('utf-8')),from_lang,to_lang)
  data=simplejson.loads(urllib2.urlopen(url).read())
  return data['responseData']['translatedText']

droid=android.Android()
text=droid.recognizeSpeech().result
t=translator(text,'ja','en')
droid.ttsSpeak(t)
droid.dialogCreateAlert(u'翻訳結果',u"%s\n\n%s"%(text,t))
droid.dialogShow()&lt;/span&gt;&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BwIshVLoLIU/TNcOC2-Qw7I/AAAAAAAAAWA/TWu2KrR4K6A/s1600/translator.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 350px; height: 350px;" src="http://1.bp.blogspot.com/_BwIshVLoLIU/TNcOC2-Qw7I/AAAAAAAAAWA/TWu2KrR4K6A/s400/translator.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5536909709154501554" /&gt;&lt;/a&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-6624507826572073748?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/6624507826572073748/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=6624507826572073748' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/6624507826572073748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/6624507826572073748'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/11/androidpython.html' title='Android端末による自動通訳を一行のPythonコードで実装する'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_BwIshVLoLIU/TNbKBE0ncXI/AAAAAAAAAV4/d5FSg3tUURk/s72-c/device01.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-7736328738129221345</id><published>2010-11-02T01:19:00.006+09:00</published><updated>2010-11-02T05:06:40.232+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>SL4AのPythonを使ってAndroid端末に日本語を喋らせてみた</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BwIshVLoLIU/TM7pkTZiM-I/AAAAAAAAAVc/M_6K_cb_hz0/s1600/speak_ja20101102a.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 192px; height: 320px;" src="http://4.bp.blogspot.com/_BwIshVLoLIU/TM7pkTZiM-I/AAAAAAAAAVc/M_6K_cb_hz0/s320/speak_ja20101102a.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5534617801976853474" /&gt;&lt;/a&gt;
先日、ドコモの&lt;a href="http://www.nttdocomo.co.jp/product/foma/smart_phone/sc02b/"&gt;GALAXY S&lt;/a&gt;を購入した。丁度1年前に購入した&lt;a href="http://www.htc.com/jp/product/ht03a/overview.html"&gt;HT-03A&lt;/a&gt;からの機種変更になる。Androidのバージョンは1.6から2.2となり、性能は格段に良くなった。この1年での技術の進歩には驚かされる。GALAXY Sで&lt;a href="http://code.google.com/p/android-scripting/"&gt;SL4A&lt;/a&gt;が利用できるかは気になるところだが、実は購入する前に既に動作することを確認していたので、その点については心配なかった。もしSL4Aが使えなかったら、多分購入しなかったと思う。&lt;br /&gt;&lt;br /&gt;

この新しいAndroid機を使って、SL4AのPythonで日本語を喋らせてみることにした。英語であればttsSpeakのAPIを使えば一発なのだが漢字かな混じりの日本語では簡単には行かない。まずは日本語の発音を取得しなくてはならないが、それについては&lt;a href="http://developer.yahoo.co.jp/"&gt;Yahoo! デベロッパーネットワーク&lt;/a&gt;の&lt;a href="http://developer.yahoo.co.jp/webapi/jlp/ma/v1/parse.html"&gt;日本語形態素解析&lt;/a&gt;を使うことにした。これなら助詞の「は」も「わ」という発音であることがわかるし、後々、応用が効くだろう。&lt;br /&gt;&lt;br /&gt;

作成したソースコードは最後に示してあるが、短いコードで簡単に実装できた。使い方も簡単で、入力ダイアログから喋らせたい文章を入力するだけだ。それを形態素解析のWeb APIでXMLとして取得し、Beautiful Soupで解析している。取得した平仮名をその発音に近い英語に直すだけだ。ローマ字に置き換えるだけでは日本語として正しく発音されないので、適当にいじってある。&lt;br /&gt;&lt;br /&gt;

実用的なプログラムにしたければ、&lt;a href="http://hil.t.u-tokyo.ac.jp/~galatea/index-jp.html"&gt;Galatea Project&lt;/a&gt;などを利用して、ちゃんとした日本語を喋らせるようにしたほうが良いのだろうけど、形態素解析を使ってみたかったし、英語を無理やり日本語にするところも面白かったので、今回はこれで良しとした。やっぱり自分が楽しめないとね。&lt;br /&gt;&lt;br /&gt;

例によって、このプログラムを使って子供相手に遊んでみた。元の文章を知っていれば問題なく聞き取れるのだが、知らないと思ったよりも聞き取れないことを逆手に取って、何を喋っているかを当てるゲームをしてみた。こんな単純なことなのだが、子供たちは思いのほか楽しんでいたようだ。インチキ外国人ぽい雰囲気が面白いらしい。&lt;br /&gt;&lt;br /&gt;

作成したソースコードは以下の通り。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;strong&gt;speak_ja.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;# -*- coding: utf-8 -*-

import android
import sys,os,urllib,urllib2
from BeautifulSoup import BeautifulSoup

YAHOO_APP_ID='&lt;span style="color:red;"&gt;Yahoo!デベロッパーネットワークのアプリケーションID&lt;/span&gt;'
KEITAISO_API='http://jlp.yahooapis.jp/MAService/V1/parse?appid=%s&amp;amp;results=ma&amp;amp;sentence=%s'

KANA=[u'きゃ',u'きぃ',u'きゅ',u'きぇ',u'きょ',
      u'くぁ',u'くぃ',u'くぅ',u'くぇ',u'くぉ',
      u'ぎゃ',u'ぎぃ',u'ぎゅ',u'ぎぇ',u'ぎょ',
      u'ぐぁ',u'ぐぃ',u'ぐぅ',u'ぐぇ',u'ぐぉ',
      u'しゃ',u'しぃ',u'しゅ',u'しぇ',u'しょ',
      u'すぁ',u'すぃ',u'すぅ',u'すぇ',u'すぉ',
      u'じゃ',u'じぃ',u'じゅ',u'じぇ',u'じょ',
      u'ちゃ',u'ちぃ',u'ちゅ',u'ちぇ',u'ちょ',
      u'てゃ',u'てぃ',u'てゅ',u'てぇ',u'てょ',
      u'とぁ',u'とぃ',u'とぅ',u'とぇ',u'とぉ',
      u'ぢゃ',u'ぢぃ',u'ぢゅ',u'ぢぇ',u'ぢょ',
      u'でゃ',u'でぃ',u'でゅ',u'でぇ',u'でょ',
      u'どぁ',u'どぃ',u'どぅ',u'どぇ',u'どぉ',
      u'にゃ',u'にぃ',u'にゅ',u'にぇ',u'にょ',
      u'ひゃ',u'ひぃ',u'ひゅ',u'ひぇ',u'ひょ',
      u'ふぁ',u'ふぃ',u'ふぅ',u'ふぇ',u'ふぉ',
      u'びゃ',u'びぃ',u'びゅ',u'びぇ',u'びょ',
      u'ぴゃ',u'ぴぃ',u'ぴゅ',u'ぴぇ',u'ぴょ',
      u'みゃ',u'みぃ',u'みゅ',u'みぇ',u'みょ',
      u'りゃ',u'りぃ',u'りゅ',u'りぇ',u'りょ',
      u'うぁ',u'うぃ',u'うぇ',u'うぉ']
PRON=['kya','kyi','kyu','kyei','kio',
      'qa','qi','qwu','qei','qo',
      'gya','gyi','gyu','gyei','gyo',
      'gwa','gwi','gwu','gwei','gwo',
      'sha','syi','shu','shei','sho',
      'swa','swi','swu','swei','swo',
      'ja','zee','juu','jei','jo',
      'cha','tyi','chu','chei','cho',
      'tha','thi','thu','thei','tho',
      'twa','twi','two','twei','two',
      'dya','dyi','dyu','dyei','dyo',
      'dha','dhi','dhu','dhei','dho',
      'dwa','dwi','dwu','dwei','dwo',
      'nya','nyi','nyu','nyei','nyo',
      'hya','hyi','hyu','hyei','hyo',
      'fa','fi','foo','fei','fo',
      'bya','byi','byu','byei','byo',
      'pya','pyi','pyu','pyei','pyo',
      'mya','myi','myu','myei','myo',
      'rya','ryi','ryu','ryei','ryo',
      'wha','wi','wei','wo']
KANA+=u'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわゐゑをんがぎぐげござじずぜぞだぢづでどばびぶべぼぱぴぷぺぽぁぃぅぇぉ'
PRON+=['a','ee','oo','ae','o',
       'ka','kee','koo','kei','ko',
       'sa','she','soo','sei','so',
       'ta','chee','tsu','tei','toe',
       'na','nee','nuu','neigh','no',
       'ha','hee','foo','hey','ho',
       'ma','mee','moo','mei','mo',
       'yah','you','yor',
       'ra','ree','roo','rei','ro',
       'wa','ee','ae','wo','nn',
       'ga','gee','goo','gei','go',
       'za','zee','zoo','zei','zo',
       'da','zee','doo','dei','do',
       'ba','bee','boo','bei','bo',
       'pa','pee','poo','pei','po',
       'a','ee','oo','ae','o']

def get_keitaiso(text):
  xml=urllib2.urlopen(KEITAISO_API % (YAHOO_APP_ID,urllib.quote(text.encode('utf-8')))).read()
  ret=''
  for data in BeautifulSoup(xml)('ma_result'):
    for r,p in zip(data('reading'),data('pos')):
      d,x=r.renderContents(),p.renderContents()
      if x=='助詞' and d=='は': ret+='わ'
      elif x!='特殊': ret+=d
  ret=ret.decode('utf-8')
  T={}
  for i in range(86): T[ord(u'ァ')+i]=unichr(ord(u'ぁ')+i)
  ret=ret.translate(T)
  for k,r in zip(KANA,PRON): ret=ret.replace(k,r+' ')
  rett=ret;
  for i,c in enumerate(ret):
    c=c.encode('utf-8')
    if c=='ー':
      ret=ret.replace(u'ー',rett[i-2]+' ',1)
    if c=='っ':
      if len(rett)&amp;gt;i+1:
        ret=ret.replace(u'っ',rett[i+1],1)
      else:
        ret=ret.replace(u'っ','',1)
  return ret

droid=android.Android()
text=droid.dialogGetInput(u'日本語を喋ります',u'何を喋らせますか?').result.strip()
droid.ttsSpeak(get_keitaiso(text))
droid.makeToast(text)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

インチキ外国人なら喋る内容もインチキっぽくしようと思い付いたので、入力した文章をGoogleの翻訳APIで英訳し、それをまた和訳するという処理を挟んでみた。例えば「一番いいのを頼む。」と入力したら「最高のお問い合わせください。」と喋る。まあ、これはジョークプログラムにしかならないけど、形態素解析を利用した人工無脳に仕立て上げたりするのも楽しいかもしれない。&lt;br /&gt;&lt;br /&gt;

上記のspeak_ja.pyに以下の赤で示したコードを追加すればいい。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;fake_japanese.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;# -*- coding: utf-8 -*-

import android
import sys,os,urllib,urllib2,&lt;span style="color:red;"&gt;simplejson&lt;/span&gt;
from BeautifulSoup import BeautifulSoup

### 途中省略

&lt;span style="color:red;"&gt;def translator(text,from_lang,to_lang):
  url='http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&amp;amp;q=%s&amp;amp;langpair=%s%%7C%s' % (urllib.quote(text.encode('utf-8')),from_lang,to_lang)
  data=simplejson.loads(urllib2.urlopen(url).read())
  return data['responseData']['translatedText']&lt;/span&gt;

droid=android.Android()
text=droid.dialogGetInput(u'日本語を喋ります',u'何を喋らせますか?').result.strip()
&lt;span style="color:red;"&gt;text=translator(translator(text,'ja','en'),'en','ja')&lt;/span&gt;
droid.ttsSpeak(get_keitaiso(text))
droid.makeToast(text)
&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-7736328738129221345?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/7736328738129221345/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=7736328738129221345' title='2 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/7736328738129221345'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/7736328738129221345'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/11/sl4apythonandroid.html' title='SL4AのPythonを使ってAndroid端末に日本語を喋らせてみた'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_BwIshVLoLIU/TM7pkTZiM-I/AAAAAAAAAVc/M_6K_cb_hz0/s72-c/speak_ja20101102a.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-4393000564173910571</id><published>2010-10-09T05:46:00.007+09:00</published><updated>2010-10-10T15:27:40.632+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WWW'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Android端末上に電子掲示板システムを構築する方法</title><content type='html'>最近になって日本においてもAndroid端末が続々と発表されて巷で賑わいを見せている。ドコモからは&lt;a href="http://www.nttdocomo.co.jp/product/foma/smart_phone/sc02b/"&gt;GALAXY S&lt;/a&gt;、auからは&lt;a href="http://www.au.kddi.com/seihin/ichiran/smartphone/is03/index.html"&gt;IS03&lt;/a&gt;、ソフトバンクからは&lt;a href="http://www.htc.com/www/product/desirehd/overview.html"&gt;Desire HD&lt;/a&gt;と立て続けに発売される。このほかにも年末までにタブレット端末を含めた複数のAndroid端末が発売されるようで結構なことだ。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BwIshVLoLIU/TK-DstyUv1I/AAAAAAAAAVI/cVLgsQoYZKE/s1600/android_bbs01.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 394px;" src="http://4.bp.blogspot.com/_BwIshVLoLIU/TK-DstyUv1I/AAAAAAAAAVI/cVLgsQoYZKE/s400/android_bbs01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5525780072035041106" /&gt;&lt;/a&gt;&lt;br /&gt;

Androidも盛り上がってきたし、Android端末を使って手軽で役に立つことができないか考えてみた。そこで今回、Android端末をウェブサーバにして、そこに電子掲示板システム(BBS)を構築してみた。これさえあれば不意にプライベートなBBSを使いたくなったときにいつでも利用することができる。たとえばライトニングトークやセミナーなどのようなイベントの参加者からその場でちょっとしたアンケートや感想を貰いたいときに便利ではないかと思う。ちょっとした話題作りにもなるかも。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
電子掲示板システムの作成に必要なものはAndroid端末と&lt;a href="http://code.google.com/p/android-scripting/"&gt;SL4A&lt;/a&gt;/Pythonだけ。あとは今回作成した android_bbs.py を使えばいい。そして、SL4Aを使ってサーバを立ち上げてから、android_bbs.pyを起動すれば完了だ。SL4Aによるサーバの起動方法とアドレス取得については&lt;a href="http://handasse.blogspot.com/2010/09/pythonandroid5.html"&gt;Pythonを使ってAndroid端末を5分でリモートカメラにする方法&lt;/a&gt;で書いたのでそちらを参考にして欲しい。&lt;br /&gt;&lt;br /&gt;

今回の電子掲示板システムはPythonを使って即席で作ったこともあってエラー処理などを入れずに30行ちょいのシンプルな作りになっている。sqlite3モジュールによるSQLiteデータベースで記事を管理し、wsgiref.simple_serverモジュールを使ってウェブサーバ構築した。簡単だ。ただ、Android独自の機能は使っていないのでAndroid端末専用というわけではないけど。Android独自の機能を入れるとしたら、たとえばTwitterなどの投稿者の位置情報表示に対抗して、投稿者ではなくサーバの位置、さらに向きや速度も一緒に表示されるようにするとか。誰得な機能だけど。&lt;br /&gt;&lt;br /&gt;

以下にソースコードを示す。&lt;br /&gt;&lt;br /&gt;


&lt;strong&gt;android_bbs.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;# -*- coding: utf-8 -*-

import cgi,sqlite3,datetime
from wsgiref.simple_server import make_server

LIMIT=50  # 最大表示記事数.
DB_FILE='/sdcard/bbs.sqlite'

con=sqlite3.connect(DB_FILE)
cur=con.cursor()
cur.execute('CREATE TABLE IF NOT EXISTS bbs (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, user TEXT, datetime TEXT, data TEXT)')
INSERT_DB='INSERT INTO bbs VALUES(NULL,?,?,?)'

def post(user,data):
  if data=='': return
  if user=='': user='匿名'
  dt=datetime.datetime.today().strftime('%Y-%m-%d %H:%M:%S')
  cur.execute(INSERT_DB,(cgi.escape(user.decode('utf-8')),dt,cgi.escape(data.decode('utf-8')).replace('\n','&amp;lt;br /&amp;gt;')))
  con.commit()

def bbs(environ,start_response):
  if environ['PATH_INFO']=='/':
    if environ['REQUEST_METHOD']=='POST':
      fs=cgi.FieldStorage(fp=environ['wsgi.input'],environ=environ,keep_blank_values=1)
      post(fs.getfirst('user','').strip(),fs.getfirst('data','').strip())
    data=u'&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;BBS by Android&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;form action="/" method="post"&amp;gt;&amp;lt;div&amp;gt;&amp;lt;textarea name="data" cols="40" rows="5"&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;span&amp;gt;名前:&amp;lt;input type="text" name="user" size="20" maxlength="30" /&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;&amp;lt;input type="submit" name="submit" value="送信" /&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;&amp;lt;input type="button" value="更新" onclick="location.reload(true);" /&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/form&amp;gt;'
    cur.execute('SELECT * FROM bbs ORDER BY id DESC')
    for i, row in enumerate(cur):
      if i&amp;gt;=LIMIT: break
      data+=('&amp;lt;div&amp;gt;&amp;lt;p&amp;gt;%d &amp;lt;b&amp;gt;%s&amp;lt;/b&amp;gt; %s&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;%s&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;' % row)
    data+='&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;'
    start_response('200 OK',[('Content-type','text/html;charset=utf-8')])
    return [data.encode('utf-8')]

httpd=make_server('',8080,bbs)
httpd.serve_forever()&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-4393000564173910571?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/4393000564173910571/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=4393000564173910571' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4393000564173910571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4393000564173910571'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/10/android.html' title='Android端末上に電子掲示板システムを構築する方法'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_BwIshVLoLIU/TK-DstyUv1I/AAAAAAAAAVI/cVLgsQoYZKE/s72-c/android_bbs01.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-3465022176009273612</id><published>2010-09-14T01:54:00.005+09:00</published><updated>2010-09-14T15:37:08.908+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WWW'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Pythonを使ってAndroid端末を5分でリモートカメラにする方法</title><content type='html'>&lt;a href="http://romberg-iso8.blogspot.com/2010/09/androidspycam3gok.html"&gt;Android端末をリモートカメラにしてしまう方法「Spycam」&lt;/a&gt;という記事経由で&lt;a href="http://leone.panopticdev.com/2010/08/turn-your-android-phone-into-remote-spy.html"&gt;Turn your Android Phone Into a Remote Spy Camera with Ruby in 15 Minutes&lt;/a&gt;を知った。&lt;a href="http://code.google.com/p/android-scripting/"&gt;SL4A&lt;/a&gt;のJRubyを使ってAndroid端末を15分で遠隔操作のスパイカメラにしてしまえるらしい。これは面白い。そこで、Rubyが15分ならPythonを使って5分でリモートカメラにしてしまおうと思い立った。&lt;br /&gt;&lt;br /&gt;
 
まず、Pythonでは標準モジュールのSimpleHTTPServerやwsgiref.simple_serverを使って簡単にWebサーバを構築することができる。そしてSL4Aを使えばAndroid端末をそのままサーバとして起動させることができる。これを組み合わせればできたも同然だ。&lt;br /&gt;&lt;br /&gt;
 
それで書いたのが以下のコードだ。10行そこそこでAndroid端末をWebサーバに仕立て上げ、Webブラウザ経由で写真を撮ることができる。こんなちっこい端末がWebサーバになるなんて世の中進歩したものだ。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;strong&gt;remote_camera.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;import android
from wsgiref.simple_server import make_server

droid=android.Android()
pic='/sdcard/snapshot.jpg'

def camera(env,res):
  if env['PATH_INFO']=='/':
    droid.cameraCapturePicture(pic)
    res('200 OK',[('Content-type','image/jpeg')])
    return [file(pic).read()]

httpd=make_server('',&lt;span style="color:red;"&gt;9998&lt;/span&gt;,camera)
httpd.serve_forever()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
使い方は次の通り。まず、SL4Aを起動し、メニューボタンから"View"を選ぶとダイアログが出てくるので"Interpreters"を選択する。&lt;br /&gt;&lt;br /&gt;
 
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_BwIshVLoLIU/TI5Zs7-r1II/AAAAAAAAAUg/2EnGZQMKbDE/s1600/device0102.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 300px;" src="http://3.bp.blogspot.com/_BwIshVLoLIU/TI5Zs7-r1II/AAAAAAAAAUg/2EnGZQMKbDE/s400/device0102.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516445222125163650" /&gt;&lt;/a&gt;&lt;br /&gt;
 
再度、メニューボタンから今度は"Start Server"、"Public"を選択する。これでAndroid端末がサーバとして機能する。&lt;br /&gt;&lt;br /&gt;
 
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BwIshVLoLIU/TI5Ztr96cgI/AAAAAAAAAUo/cC3LgtI6XDo/s1600/device0304.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 300px;" src="http://1.bp.blogspot.com/_BwIshVLoLIU/TI5Ztr96cgI/AAAAAAAAAUo/cC3LgtI6XDo/s400/device0304.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516445235006829058" /&gt;&lt;/a&gt;&lt;br /&gt;
 
次に、Android画面上部のインジケータ部分を開き、"SL4A Service"をタップする。そうするとScript Monitorが立ち上がるので、Serverのアドレスを確認する。それから先ほど作成したPythonスクリプト(remote_camera.py)を起動する。&lt;br /&gt;&lt;br /&gt;
 
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BwIshVLoLIU/TI5ZuIko7YI/AAAAAAAAAUw/ju_2z8eErnY/s1600/device0506.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 300px;" src="http://1.bp.blogspot.com/_BwIshVLoLIU/TI5ZuIko7YI/AAAAAAAAAUw/ju_2z8eErnY/s400/device0506.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516445242685451650" /&gt;&lt;/a&gt;&lt;br /&gt;
 
サーバのアドレスの最後にソースコードで指定したポート(上記のコードでは9998)を加えたURLをWebブラウザに入力する。例えばサーバのアドレスが your.address.net だとすれば、 http://your.address.net:9998/ がWebブラウザに入力するURLとなる。これで遠隔操作による撮影が可能になった。ページをリロードすればその度に写真が撮影され表示される。試しに最近購入した書籍などを撮影。&lt;br /&gt;&lt;br /&gt;
 
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BwIshVLoLIU/TI5ZuxB8-HI/AAAAAAAAAU4/N8HExHPRWGs/s1600/browser01a.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 351px;" src="http://4.bp.blogspot.com/_BwIshVLoLIU/TI5ZuxB8-HI/AAAAAAAAAU4/N8HExHPRWGs/s400/browser01a.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5516445253545818226" /&gt;&lt;/a&gt;&lt;br /&gt;
 
これを使って次のような遊びをしてみた。Android端末を見つかりにくいところに隠し、あとでWebブラウザから写真を撮る。その画像とかすかに聞こえるシャッター音を頼りに隠したAndroid端末を探すというゲームだ。子供たち相手にやってみたが皆大喜びだった。もっと広い場所でやれば本当に楽しそうだ。必要なものはAndroid端末とWebブラウザの使えるデバイスだけだ。デバイスもPCではなくAndroid端末のようなスマートフォンで良いのだから手軽だ。&lt;br /&gt;&lt;br /&gt;
 
これは使い方の一例だが、Android端末とSL4Aを使えば可能性が本当に広がってくる。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_BwIshVLoLIU/TI5bsG9R4CI/AAAAAAAAAVA/IgS4wwibbfI/s1600/QR_remote_camera.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 350px; height: 350px;" src="http://2.bp.blogspot.com/_BwIshVLoLIU/TI5bsG9R4CI/AAAAAAAAAVA/IgS4wwibbfI/s400/QR_remote_camera.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516447406915444770" /&gt;&lt;/a&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-3465022176009273612?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/3465022176009273612/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=3465022176009273612' title='2 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/3465022176009273612'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/3465022176009273612'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/09/pythonandroid5.html' title='Pythonを使ってAndroid端末を5分でリモートカメラにする方法'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_BwIshVLoLIU/TI5Zs7-r1II/AAAAAAAAAUg/2EnGZQMKbDE/s72-c/device0102.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-4689699715998514289</id><published>2010-09-13T04:32:00.014+09:00</published><updated>2010-10-04T18:47:19.955+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Android端末で青空プログラミング! SL4Aの導入からアプリケーションの作成・公開まで</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BwIshVLoLIU/TI04XWYLfRI/AAAAAAAAAUY/5uvJqit0E4o/s1600/QR_sudoku_solver.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 350px; height: 350px;" src="http://4.bp.blogspot.com/_BwIshVLoLIU/TI04XWYLfRI/AAAAAAAAAUY/5uvJqit0E4o/s400/QR_sudoku_solver.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516127092393868562" /&gt;&lt;/a&gt;
以前、&lt;a href="http://handasse.blogspot.com/2010/02/androidpythonluajavascript.html"&gt;Android上でPython、Lua、JavaScriptなどを実行するスクリプティング環境ASE (Android Script Environment)&lt;/a&gt;について記事にした。現在ASEはSL4A (Scripting Layer for Android)に変更され、機能がさらに進化している。APIの充実やインターフェイスの改良、スクリプト環境は独立になり、HTMLインタプリタ導入やAndroidパッケージ(APK)の作成などもできるようになった。そこで改めてSL4Aのインストール方法、使い方、Pythonによるアプリケーションの作成、QRコードによるソースコードの公開方法などを紹介する。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;strong&gt;導入&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

まず、&lt;a href="http://code.google.com/p/android-scripting/"&gt;SL4Aの公式サイト&lt;/a&gt;で最新版のSL4Aをダウンロードする。現時点では&lt;a href="http://code.google.com/p/android-scripting/downloads/detail?name=sl4a_r2.apk"&gt;sl4a-r2.apk&lt;/a&gt;だが、頻繁に更新されるので注意すること。さらに必要なスクリプト環境をダウンロードする。BeanShell, JRuby, Lua, Perl, Python, Rhino (JavaScript)などがあるが、ここではPythonを選んだ。現時点での最新版は&lt;a href="http://code.google.com/p/android-scripting/downloads/detail?name=python_for_android_r1.apk"&gt;python_for_android_r1.apk&lt;/a&gt;になる。SL4Aとスクリプト環境は独立しているのでそれぞれをインストールする。&lt;br /&gt;&lt;br /&gt;

これらのAndroidパッケージは公式のものではないので、ダウンロード後インストールするためには、Android端末の設定で「アプリケーション」の「提供元不明のアプリ」にチェックを入れておく必要がある。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BwIshVLoLIU/TI0sTKGcs5I/AAAAAAAAATY/L3USpa5-QQA/s1600/device.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://4.bp.blogspot.com/_BwIshVLoLIU/TI0sTKGcs5I/AAAAAAAAATY/L3USpa5-QQA/s400/device.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516113826239263634" /&gt;&lt;/a&gt;&lt;br /&gt;

これでSL4AでPythonを使用するための準備は整った。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;スクリプトの作成&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BwIshVLoLIU/TI0skq3-5zI/AAAAAAAAATg/oFk2EY-3UUk/s1600/sl4a_logo.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 48px; height: 48px;" src="http://4.bp.blogspot.com/_BwIshVLoLIU/TI0skq3-5zI/AAAAAAAAATg/oFk2EY-3UUk/s320/sl4a_logo.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516114127094736690" /&gt;&lt;/a&gt;
SL4Aをインストールすると左のようなアイコンが出てくるのでそれをタップして起動する。Pythonスクリプトファイルの作成するには、メニューボタンから"Add"を選択し(スクリーンショット左側)、表示されるダイアログから"Python"を選ぶ(スクリーショット左から2番目)。そうすると、Pythonスクリプトの雛形を伴った編集画面になるので(スクリーンショット中央)、それを更新して作成する(スクリーンショット右から2番目)。ファイル名は自由に付けることができ、作成したスクリプトファイルは"Save &amp; Run"ですぐに起動できる(スクリーンショット右側)。編集画面時のメニューボタンから"API Browser"を選択すると&lt;a href="http://code.google.com/p/android-scripting/wiki/ApiReference"&gt;API一覧&lt;/a&gt;が表示される。これはコードを書く上で非常に役に立つ。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BwIshVLoLIU/TI0s4YZ07SI/AAAAAAAAATo/cQuJ1B4Umyg/s1600/edit_mod.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 120px;" src="http://1.bp.blogspot.com/_BwIshVLoLIU/TI0s4YZ07SI/AAAAAAAAATo/cQuJ1B4Umyg/s400/edit_mod.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516114465733799202" /&gt;&lt;/a&gt;&lt;br /&gt;

さて、ここで実際にアプリを作ってみる。しばらく前に&lt;a href="http://handasse.blogspot.com/2010/08/python.html"&gt;Pythonワンライナーで数独ソルバを作成した&lt;/a&gt;のでそれをAndroidアプリとして作り替えてみた。ワンライナーではプログラム中に問題を埋め込んでいたが、Androidではそれダイアログから入力するように変えた。入力時に電話のテンキーにすれば素早く片手で入力できるようになるが、HT-03Aではなぜか全角文字になってしまったのでそれをASCII文字に変換するテーブルも追加した。さらに、ワンライナーのようなコンソールへの出力ではバックグラウンドでの動作ができないので、ダイアログで表示させるように変更した。作成したのが以下のスクリプトだ。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;sudoku_solver.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;# -*- coding: utf-8 -*-

import sys,android

droid=android.Android()
L=[]

def S(D):
  if 0 in D:
    L.append(D.index(0))
    A=D[L[-1]//9*9:L[-1]//9*9+9]
    B=D[L[-1]%9:81:9]
    C=[d for n in(0,1,2)for d in D[L[-1]//27*27+L[-1]%9//3*3+n*9:L[-1]//27*27+L[-1]%9//3*3+n*9+3]]
    for i in set(range(1,10))-set(A+B+C):
      D[L[-1]]=i
      S(D)
      D[L.pop()]=0
  else:
    M=''.join(['%d'%d+('\n' if i%9==8 else ' ')for i,d in enumerate(D)]).rstrip()
    droid.dialogCreateAlert(u'解答',M)
    droid.dialogShow()
    sys.exit()

T={ord(u'\n'):None,ord(u' '):None,ord(u'＊'):u'0',ord(u'*'):u'0',ord(u'．'):u'0',ord(u'.'):u'0'}
for i in range(10): T[ord(u'０')+i]=u'%d'%i
P=droid.dialogGetInput(u'数独ソルバ',u'問題を入力してください:').result.strip().translate(T)
S(map(int,P))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

sudoku_solver.pyを起動するにはファイル一覧からsudoku_solver.pyをタップする。すると以下のような5つのアイコン(左から順に、コンソール上での起動、バックグラウンドでの起動、編集、ファイル名の変更、削除)が現れるのでそのうち左の2つのアイコンのどちらかを選ぶことで起動できる。今回はバックグラウンドで起動した。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_BwIshVLoLIU/TI0tSOl2jzI/AAAAAAAAATw/rRbcHADTss0/s1600/sudoku_solver01.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://2.bp.blogspot.com/_BwIshVLoLIU/TI0tSOl2jzI/AAAAAAAAATw/rRbcHADTss0/s400/sudoku_solver01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516114909776482098" /&gt;&lt;/a&gt;&lt;br /&gt;

起動後、以下のようなダイアログが出てくるので問題となる数独を入力する。空白部分は * もしくは . とする。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_BwIshVLoLIU/TI0tSktkatI/AAAAAAAAAT4/OuJdCbIFPqE/s1600/sudoku_solver02.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://2.bp.blogspot.com/_BwIshVLoLIU/TI0tSktkatI/AAAAAAAAAT4/OuJdCbIFPqE/s400/sudoku_solver02.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516114915714427602" /&gt;&lt;/a&gt;&lt;br /&gt;

以下が入力が完了した状態。ここでOKボタンを押すと解析が開始される。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_BwIshVLoLIU/TI0tTUaxZAI/AAAAAAAAAUA/1dwj8TdDoeU/s1600/sudoku_solver03.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://2.bp.blogspot.com/_BwIshVLoLIU/TI0tTUaxZAI/AAAAAAAAAUA/1dwj8TdDoeU/s400/sudoku_solver03.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516114928520487938" /&gt;&lt;/a&gt;&lt;br /&gt;

解析が完了すると以下のような解答が表示される。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_BwIshVLoLIU/TI0tUUTO6fI/AAAAAAAAAUI/LM2g0KGWlKQ/s1600/sudoku_solver04.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://3.bp.blogspot.com/_BwIshVLoLIU/TI0tUUTO6fI/AAAAAAAAAUI/LM2g0KGWlKQ/s400/sudoku_solver04.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516114945668737522" /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;strong&gt;スクリプトの共有&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

これでスクリプトによるAndroidアプリを作成することができた。作成したアプリを別の人にも使ってもらうために公開したい場合はどうすればよいのだろうか。ソースコードをそのまま公開することもできるが、それだと一々Android端末にコピーしなくてはならず、場合によっては打ち込み直すはめになり、面倒だ。そこで、QRコードを使ったスクリプトの共有がある。4,296文字という制限があるが、今回のような短いコードでは十分だ。&lt;br /&gt;&lt;br /&gt;

QRコードを作成するために&lt;a href="http://zxing.appspot.com/generator/"&gt;QR Code Generator&lt;/a&gt;を利用する。ここのサイトで"Contents"を"Text"とし、"Text content"に、先頭行をファイル名(上述のスクリプトの場合 sudoku_solver.py )としたスクリプトのソースコードを入れる。"Barcode size"は"L"とする。これで"Generate"ボタンを押せばQRコードが生成される。&lt;br /&gt;&lt;br /&gt;

次にQRコードからソースファイルに戻す方法だが、Android端末でSL4Aを起動し、メニューボタンから"Add"を選ぶ。表示されるリストから"Scan Barcode"でQRコードを読み込むだけだ。ただし、Android 1.6以前ではバーコードスキャナーが入っていないので、Android MarketからZXingのQRコードスキャナー(無料)をインストールしておくこと。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_BwIshVLoLIU/TI0t6JCvKPI/AAAAAAAAAUQ/s5lm35HmPdM/s1600/scan_barcode.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://3.bp.blogspot.com/_BwIshVLoLIU/TI0t6JCvKPI/AAAAAAAAAUQ/s5lm35HmPdM/s400/scan_barcode.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5516115595481786610" /&gt;&lt;/a&gt;&lt;br /&gt;

これで簡単にコードを共有することができた。因みにこの記事の先頭に表示しているQRコードは今回作成した数独ソルバのソースコードになっている。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;***&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

まだドラフト段階だが&lt;a href="http://code.google.com/p/android-scripting/wiki/SharingScripts#Scripts_as_APKs"&gt;Androidパッケージ(APK)の作成&lt;/a&gt;も可能だし、前回ではPCからadbによるコマンドラインでの操作を取り上げたが、いずれもPCとAndroid端末を繋いでの操作になり青空プログラミングという今回の趣旨から外れてしまうし、一回で説明するには記事が長くなってしまうので今回は割愛することにした。それらについては別の機会に書くかもしれない。&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-4689699715998514289?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/4689699715998514289/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=4689699715998514289' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4689699715998514289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4689699715998514289'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/09/android-sl4a.html' title='Android端末で青空プログラミング! SL4Aの導入からアプリケーションの作成・公開まで'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_BwIshVLoLIU/TI04XWYLfRI/AAAAAAAAAUY/5uvJqit0E4o/s72-c/QR_sudoku_solver.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-7551137974258845852</id><published>2010-09-07T01:59:00.006+09:00</published><updated>2010-09-07T19:50:50.100+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='アルゴリズム'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='Haskell'/><title type='text'>アルゴリズムの素晴らしさに気付かせてくれたのはエラトステネスの篩だった</title><content type='html'>Haskellによる&lt;a href="http://ja.wikipedia.org/wiki/エラトステネスの篩"&gt;エラトステネスの篩(sieve of Eratosthenes)&lt;/a&gt;の美しい実装を見て、初めてアルゴリズムの素晴らしさに気付かせてくれたのがエラトステネスの篩だったことを思い出した。たしか中学生の頃だ。そこで、当時を懐かしみながら簡単な実装を書き留めておくことにした。とりあえず、Haskell, C++, Pythonの実装を以下に示す。コードは比較的短いが、実行効率を優先させているわけではない。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;strong&gt;Haskell&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

これはHaskellのサンプルコードでよく出てくる無限リストと遅延評価による実装だが、とても分かりやすいし、美しいコードだと思う。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;primes = sieve [2..]
sieve (p:xs) = p : sieve [x | x &amp;lt;- xs, x `mod` p /= 0]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

以下のように関数&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;take&lt;/span&gt;を使って必要な分だけ素数を取り出すことができる。ただし、実行効率はあまり良くない。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;take &lt;span style="color:red;"&gt;10&lt;/span&gt; primes
[2,3,5,7,11,13,17,19,23,29]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;C++&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

次にC++を使って実装してみた。ここではC++0xを使っている。そのほうが簡潔で分かりやすく書けるし、これからはC++0xがより使われ、普及して欲しいという意味もある。因みにgccではバージョン4.5以降、Intelコンパイラではバージョン11.0以降、Visual C++では2010(16.0)以降でコンパイルできる。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;std::vector&amp;lt;int&amp;gt; primes;
for (int i = 3; i &amp;lt; &lt;span style="color:red;"&gt;100&lt;/span&gt;; i += 2) primes.push_back(i);

auto end = primes.end();
for (auto x = primes.begin(); *x * *x &amp;lt;= *(end-1); ++x)
    end = std::remove_if(x + 1, end, [&amp;amp;x](int p){ return p % *x == 0; });
primes.erase(end, primes.end());

for (auto p = primes.begin(); p != primes.end(); ++p) std::cout &amp;lt;&amp;lt; *p &amp;lt;&amp;lt; " ";
std::cout &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

上記のコードでは3から100までの素数を表示する。ノートPC (Core 2 Duo T9800 2.93 GHz / Visual Studio 2010)を使って1000万までの素数を生成してファイルに書き出すのにかかった時間は約2秒だった。&lt;br /&gt;&lt;br /&gt;

そういえば以前に&lt;a href="http://handasse.blogspot.com/2009/03/c_13.html"&gt;C++のテンプレートで素数を求めた&lt;/a&gt;ことがあったな。エラトステネスの篩ではなかったけど。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;Python&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

最後にPythonによる実装を示す。効率はあまり良くないかも。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;primes = range(3, &lt;span style="color:red;"&gt;100&lt;/span&gt;, 2)
for i in range(len(primes)):
    if primes[i]**2 &amp;gt; primes[-1]: break
    primes[i+1:] = filter(lambda p: p % primes[i] != 0, primes[i+1:])
print primes&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

これをそのままワンライナーに。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;python -c "import sys;globals().__setitem__('P',range(3,&lt;span style="color:red;"&gt;100&lt;/span&gt;,2));[P.__setslice__(i+1,len(P),filter(lambda p:p%P[i]!=0,P[i+1:]))for i in range(len(P))if i&amp;lt;len(P)and P[i]**2&amp;lt;=P[-1]];[sys.stdout.write('%d '%p)for p in P]"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

上記のコードではそれぞれ、赤字の数値が求める素数の上限値となっている。&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-7551137974258845852?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/7551137974258845852/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=7551137974258845852' title='1 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/7551137974258845852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/7551137974258845852'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/09/blog-post.html' title='アルゴリズムの素晴らしさに気付かせてくれたのはエラトステネスの篩だった'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-1361639904896880338</id><published>2010-08-23T22:20:00.002+09:00</published><updated>2010-08-23T22:34:24.765+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ゲーム'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='アルゴリズム'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Pythonワンライナーで数独を解いてみた</title><content type='html'>&lt;a href="http://gigazine.net/"&gt;GIGAZINE&lt;/a&gt;に&lt;a href="http://gigazine.net/index.php?/news/comments/20100822_hardest_sudoku/"&gt;数学のエキスパートが3ヶ月かけて作成した「世界一難しい数独」&lt;/a&gt;なるものがあったので、即席でPythonワンライナー(一行プログラム)を作ったら1秒かからず解けた。問題を作るのは大変でも解析プログラムで一瞬とは儚い。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;python -c "import sys;L=[];S=lambda D:(0in D)and[L.append(D.index(0)),[(D.__setitem__(L[-1],i),S(D),D.__setitem__(L.pop(),0))for i in set(range(1,10))-set(D[L[-1]/9*9:L[-1]/9*9+9]+D[L[-1]%9:81:9]+[d for n in(0,1,2)for d in D[L[-1]/27*27+L[-1]%9/3*3+n*9:L[-1]/27*27+L[-1]%9/3*3+n*9+3]])]]or([sys.stdout.write('%d'%d+('\n'if i%9==8 else' '))for i,d in enumerate(D)],sys.exit());S([int(c)if c!='.'else 0for c in'..53.....8......2..7..1.5..4....53...1..7...6..32...8..6.5....9..4....3......97..'])"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
コードの最後のリスト内の文字列が9行9列の数独の問題となっていて、上の行の数字から順に入っている。未知の部分はドット(.)で表している。上記のコードではGIGAZINEの問題となっている。&lt;br /&gt;&lt;br /&gt;
 
因みにPythonによるもっと効率の良いアルゴリズムについては&lt;a href="http://norvig.com/sudoku.html"&gt;Solving Every Sudoku Puzzle&lt;/a&gt;が参考になるかも。&lt;br /&gt;&lt;br /&gt;
 
以下、ワンライナーによる出力結果。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;1 4 5 3 2 7 6 9 8
8 3 9 6 5 4 1 2 7
6 7 2 9 1 8 5 4 3
4 9 6 1 8 5 3 7 2
2 1 8 4 7 3 9 5 6
7 5 3 2 9 6 4 8 1
3 6 7 5 4 2 8 1 9
9 8 4 7 6 1 2 3 5
5 2 1 8 3 9 7 6 4&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-1361639904896880338?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/1361639904896880338/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=1361639904896880338' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1361639904896880338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1361639904896880338'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/08/python.html' title='Pythonワンライナーで数独を解いてみた'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-6067780270994698128</id><published>2010-07-21T00:25:00.023+09:00</published><updated>2011-03-21T20:09:57.140+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><title type='text'>日本全国ハンバーガーショップ分布地図</title><content type='html'>以前、&lt;a href="http://processing.org/"&gt;Processing&lt;/a&gt;を使って&lt;a href="http://handasse.blogspot.com/2009/12/blog-post.html"&gt;日本全国のコンビニ店舗の分布地図&lt;/a&gt;を作成したが、今回はハンバーガーショップの分布地図を作成してみた。&lt;br /&gt;&lt;br /&gt;

個人的にハンバーガーショップには小さい頃から思い入れがあって、小学生の頃、初めてマクドナルドのハンバーガーを食べたときに「なんて美味しいんだろう」と感動した覚えがある。しかし、今でこそ安価な食べ物という認識だが、当時は他の食べ物に比べて割高であまり頻繁に食べられなかった。&lt;br /&gt;&lt;br /&gt;

その後はだんだんとハンバーガーから離れていったが、モスバーガーと出会ったときに二度目の感動を覚えた。冷たくてジューシーな野菜と熱々のハンバーグが絶妙にマッチしてそれまで食べたことのないハンバーガーだった。少々食べにくくはあったが、逆にそれが溢れる美味しさを表現していたようにも思う。&lt;br /&gt;&lt;br /&gt;

最近気に入っているハンバーガーは、アトレヴィ 秋葉原2階にある&lt;a href="http://www.chelsea-market.com/"&gt;Chelsea Market&lt;/a&gt;のアボガドバーガーだ。少々値は張るが、普通のチェーン店に比べて味は飛び抜けていると思う。チェーン店ではないハンバーガー専門店なら他にも美味しいハンバーガーがいろいろとありそう。&lt;br /&gt;&lt;br /&gt;

閑話休題。以下に作成したプログラムを示す。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;script type="text/javascript" src="http://www.java.com/js/deployJava.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
/* &lt;![CDATA[ */
var attributes = { code:'fastfood.class', archive: 'http://www.futatsugi.net/develop/processing/fastfood/fastfood.jar', width:539, height:563 } ;
var parameters = { };
var version = '1.5';
deployJava.runApplet(attributes, parameters, version);
/* ]]&gt; */
&lt;/script&gt;

&lt;noscript&gt;
&lt;!--[if !IE]&gt; --&gt;
&lt;object classid="java:fastfood.class" type="application/x-java-applet" archive="http://www.futatsugi.net/develop/processing/fastfood/fastfood.jar" width="539" height="563" standby="Loading Processing software..."&gt;
&lt;param name="archive" value="http://www.futatsugi.net/develop/processing/fastfood/fastfood.jar" /&gt;
&lt;param name="mayscript" value="true" /&gt;
&lt;param name="scriptable" value="true" /&gt;
&lt;param name="image" value="http://www.futatsugi.net/develop/processing/fastfood/loading.gif" /&gt;
&lt;param name="boxmessage" value="Loading Processing software..." /&gt;
&lt;param name="boxbgcolor" value="#FFFFFF" /&gt;
&lt;param name="test_string" value="outer" /&gt;
&lt;!--&lt;![endif]--&gt;

&lt;object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" codebase="http://java.sun.com/update/1.6.0/jinstall-6u18-windows-i586.cab" width="539" height="563" standby="Loading Processing software..."&gt;
&lt;param name="code" value="fastfood" /&gt;
&lt;param name="archive" value="http://www.futatsugi.net/develop/processing/fastfood/fastfood.jar" /&gt;
&lt;param name="mayscript" value="true" /&gt;
&lt;param name="scriptable" value="true" /&gt;
&lt;param name="image" value="http://www.futatsugi.net/develop/processing/fastfood/loading.gif" /&gt;
&lt;param name="boxmessage" value="Loading Processing software..." /&gt;
&lt;param name="boxbgcolor" value="#FFFFFF" /&gt;
&lt;param name="test_string" value="inner" /&gt;

&lt;p&gt;
&lt;strong&gt;This browser does not have a Java Plug-in.&lt;br /&gt;
&lt;a href="http://www.java.com/getjava" title="Download Java Plug-in"&gt;
Get the latest Java Plug-in here.&lt;/a&gt;
&lt;/strong&gt;
&lt;/p&gt;
&lt;/object&gt;
&lt;!--[if IE]&gt; --&gt;
&lt;/object&gt;
&lt;!--&lt;![endif]--&gt;
&lt;/noscript&gt;
&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;左上の店名をクリック: 分布の表示・非表示を切り替える。起動時はマクドナルドのみ表示。
右クリックしたままマウス移動: 地図を平行移動する。
左クリックしたままマウス上下移動: 地図を拡大・縮小する。&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

プログラムの作成方法は&lt;a href="http://handasse.blogspot.com/2009/12/processing.html"&gt;前回のコンビニ店舗分布地図を作成した方法&lt;/a&gt;とほとんど変わらない。まず、住所をネットから取得する。それを&lt;a href="http://code.google.com/intl/ja/apis/maps/"&gt;Google Maps API&lt;/a&gt;で緯度経度変換し、さらにPythonスクリプトでUTM図法に合わせてXY座標変換する。そのデータをtar/gzipでアーカイブし、Processingを使って読み込み、&lt;a href="http://www.kabipan.com/geography/whitemap/index.html"&gt;日本地図&lt;/a&gt;に表示する。ブログへの貼り付けはProcessingでJavaアプレットにエクスポートできるのでそれを使う。&lt;br /&gt;&lt;br /&gt;

今回の分布地図を見て、マクドナルドよりもかなり後に出てきたモスバーガーの店舗が思ったより多く、全国的に展開していて驚いた。また、子供の頃に食べた森永LOVEは既に存在せずロッテリアに買収されたことも今回作成する過程で知った。他には東京に最近出現してきたバーガーキングや沖縄のみに存在するA&amp;Wがやや気になった。沖縄のA&amp;Wへは簡単には行けないが、バーガーキングは今度行ってみよう。&lt;br /&gt;&lt;br /&gt;

住所データさえあればコンビニ店舗やハンバーガーショップに限らずどんな分布地図でも作れることだし、何か面白い題材ないかな。&lt;br /&gt;&lt;br /&gt;

以下、ソースコード。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;fastfood.pde&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;String shopData = "convert_xy.tsv.gz";
String japanMap = "japan.svg";
PShape mapShape;

int totalCount;
Place[] places;
int placeCount = 0;

final float minX = -0.19834;
final float maxX =  0.2425;
final float minY = -0.1875;
final float maxY =  0.1845;

PImage mapImage;
PFont font;

float offset_x = 0.0;
float offset_y = 0.0;
int center_x = 0;
int center_y = 0;
float zoom = 1.0;
int mx_start, my_start, my_pos;

String[] shopName = { "McDonald", "MOS", "Lotteria", "Freshness", "First Kitchen",
                      "DOMDOM", "Burger King", "A&amp;amp;W", "Becker's" };
color[] shopColor = { #ff0000, #007fff, #00ff00, #ffff00, #ff00ff,
                      #00ffff, #7f0000, #0000ff, #007f00 };
boolean[] shopIds = { true, false, false, false, false, false, false, false, false };
int numShops = 9;

public void setup() {
  mapShape = loadShape(japanMap);
  smooth();
  loop();
  size(int(mapShape.width), int(mapShape.height), JAVA2D); // 539, 563
  shapeMode(CORNER);
  mapShape.disableStyle();

  font = loadFont("CourierNewPSMT-14.vlw");
  textMode(SCREEN);
  textFont(font);

  readData();
}

public void draw() {
  background(0);
  noStroke();
  fill(32);
  shape(mapShape, int(offset_x * zoom) + center_x, int(offset_y * zoom) + center_y);
  
  for (int i = 0; i &amp;lt; placeCount; i++) places[i].draw();
  for (int i = 0; i &amp;lt; numShops; i++) {
    fill(shopColor[i]);
    text(shopName[i], 15, 15 * (i + 1));
    if (shopIds[i]) text("*", 5, 15 * (i + 1));
  }
}

float TX(float x) {
  float offset = width * (1.0 - zoom) * 0.5;
  return map(x, minX, maxX, offset, width - offset);
}

float TY(float y) {
  float offset = height * (1.0 - zoom) * 0.5;
  return map(y, minY, maxY, height - offset, offset);
}


void mousePressed() {
  mx_start = int(mouseX - offset_x * zoom);
  my_start = int(mouseY - offset_y * zoom);
  loop();
  for (int i = 0; i &amp;lt; numShops; i++)
    if (mouseX &amp;lt; shopName[i].length() * 8 + 15 &amp;amp;&amp;amp; mouseY &amp;gt; 15 * i &amp;amp;&amp;amp; mouseY &amp;lt; 15 * (i + 1))
      shopIds[i] = !shopIds[i];
  my_pos = mouseY;
}

void mouseReleased() {
  noLoop();
}

void mouseDragged() {
  if (mouseButton == LEFT) {
    offset_x = (mouseX - mx_start) / zoom;
    offset_y = (mouseY - my_start) / zoom;
  } else if (mouseButton == RIGHT) {
    if (mouseY - my_pos &amp;gt; 4) {
      float zoom_out = 9.8 / 10.0;
      mapShape.scale(zoom_out);
      zoom *= zoom_out;
      center_x = int(width * (1.0 - zoom) * 0.5);
      center_y = int(height * (1.0 - zoom) * 0.5);
      my_pos = mouseY;
    } else if (mouseY - my_pos &amp;lt; -4) {
      float zoom_in = 10.0 / 9.8;
      mapShape.scale(zoom_in);
      zoom *= zoom_in;
      center_x = int(width * (1.0 - zoom) * 0.5);
      center_y = int(height * (1.0 - zoom) * 0.5);
      my_pos = mouseY;
    }
  }
}

void readData() {
  new Slurper();
}

Place parsePlace(String line) {
  String pieces[] = split(line, '\t');
  
  int id = int(pieces[0]);
  String name = pieces[1];
  float x = float(pieces[2]);
  float y = float(pieces[3]);
  
  return new Place(id, name, x, y);
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;Place.pde&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;class Place {
  int id;
  String name;
  float x, y;

  public Place(int id, String name, float x, float y) {
    this.id = id;
    this.name = name;
    this.x = x;
    this.y = y;
  }

  public void draw() {
    if (shopIds[this.id]) {
      int xx = (int)TX(this.x) + int(offset_x * zoom);
      int yy = (int)TY(this.y) + int(offset_y * zoom);
      set(xx, yy, shopColor[this.id]);
    }
  }
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;Slurper.pde&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;class Slurper implements Runnable {
  Slurper() {
    Thread thread = new Thread(this);
    thread.start();
  }

  public void run() {
    try {
      BufferedReader reader = createReader(shopData);

      String line = reader.readLine();
      totalCount = int(line);

      places = new Place[totalCount];

      while ((line = reader.readLine()) != null) {
        places[placeCount] = parsePlace(line);
        placeCount++;
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
    noLoop();
  }
}&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-6067780270994698128?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/6067780270994698128/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=6067780270994698128' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/6067780270994698128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/6067780270994698128'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/07/blog-post.html' title='日本全国ハンバーガーショップ分布地図'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-5321687076545058004</id><published>2010-07-13T22:36:00.012+09:00</published><updated>2010-07-14T13:47:13.248+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='科学'/><category scheme='http://www.blogger.com/atom/ns#' term='ActionScript'/><title type='text'>2次元セルオートマトンとエントロピー</title><content type='html'>2状態の2次元&lt;a href="http://ja.wikipedia.org/wiki/セル・オートマトン"&gt;セルオートマトン(Cellular Automaton)&lt;/a&gt;をActionScript 3.0 (AS3)で作ってみた。2次元セルオートマトンで最も有名なのは&lt;a href="http://ja.wikipedia.org/wiki/ライフゲーム"&gt;ライフゲーム(Conway's Game of Life)&lt;/a&gt;だろう。プログラムでは初期ルールをライフゲームとしている。初めにC++で書こうと思ったのだけど、Web上でグラフィカルに操作できないとつまらないと思い、AS3でFlashとして作成し&lt;a href="http://wonderfl.net/"&gt;wonderfl&lt;/a&gt;に登録することにした。ついでに時間的なエントロピー変化を視覚的に認識できたら面白そうだと思ったので、それについても実装した。&lt;br /&gt;&lt;br /&gt;

今回、セルオートマトンのコードを書こうと思ったのは&lt;a href="http://www.topcoder.com/longcontest/?module=ViewProblemStatement&amp;rd=14273&amp;pm=10989"&gt;TCO Marathon Round 2&lt;/a&gt;の問題として出されたからだ。2次元セルオートマトンのサイズと初期配置、それにルールが与えられ、指定した世代で生存セルが最大になるように初期配置を変更しろという問題だ。そして、問題を解くために色々と調べているうちにセルオートマトンが面白く感じられたのでブログ記事にしてみた。とは言っても今回のコードはコンテストのような特別なチューンなどはしていない。ビット配列や剰余テーブルなどは使っているがどちらかというと読みやすさを優先させている。&lt;br /&gt;&lt;br /&gt;

作成したプログラムを以下に示す。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;/span&gt;
&lt;div id="fullpost"&gt;&lt;br /&gt;

&lt;div style="text-align:center;width:465px;"&gt;&lt;iframe title="Cellular Automaton - wonderfl build flash online" scrolling="no" src="http://wonderfl.net/blogparts/6yen" width="465" height="490" style="border:1px black solid;"&gt;&lt;/iframe&gt;&lt;a href="http://wonderfl.net/c/6yen" title="Cellular Automaton - wonderfl build flash online"&gt;Cellular Automaton - wonderfl build flash online&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;1-9   : ルール設定 - セルの周りの生存セル数(0-8)に対応
      : '.' 死亡, '&amp;' 変化, '+' 誕生, '=' 維持
SPACE : 一時停止
ENTER : リセット
V     : メッセージ表示/非表示
Z     : 初期生存セルの割合を減少
X     : 初期生存セルの割合を増加&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

最初に実行されるルールはライフゲームだ。つまり、セルの回りに2つの生存セルがあった場合、そのセルは現状「維持」となり、3つの生存セルがあればそこに生存セルが「誕生」する。生存セルが1つ以下か4つ以上の場合は死亡セルとなる。また、上端と下端、右端と左端は繋がっていて周期境界条件となっている。現在のルール設定はFlash画面の左下に&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;[..=+.....]&lt;/span&gt;と表示される。&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;[]&lt;/span&gt;内の9つの文字はそれぞれ左から順に、セルの回りに生存セルがない場合、1つある場合、2つある場合、…周りがすべて生存セル(8つ)の場合を表していて、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;.&lt;/span&gt; は「死亡」(死亡セルとなる)、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;+&lt;/span&gt; は「誕生」(生存セルとなる)、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;=&lt;/span&gt; は「維持」(現在のセルのまま)、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&amp;&lt;/span&gt; は「変化」(死亡セルであれば生存セルとなり、生存セルであれば死亡セルとなる)となっている。ルールの変更はキーボードの1～9キーに対応しており、キーを押す毎に、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;.&lt;/span&gt; → &lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&amp;&lt;/span&gt; → &lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;+&lt;/span&gt; → &lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;=&lt;/span&gt; と変化していく。&lt;br /&gt;&lt;br /&gt;

ルールの右隣にある数値は初期状態の生存セルの割合で、その右隣の数値は現在の生存セルの割合を示している。初期状態の生存セルの割合はXキーで増加、Zキーで減少させることができる。また、一番上に書かれているFPSは1秒間に表示されるフレーム数を示し、Nは現在の生存セル数、Hはエントロピーを示す。&lt;br /&gt;&lt;br /&gt;

ここでエントロピーについての説明をしておく。時間的なエントロピー&lt;i&gt;H&lt;/i&gt;については下記の&lt;a href="http://ja.wikipedia.org/wiki/情報量#.E5.B9.B3.E5.9D.87.E6.83.85.E5.A0.B1.E9.87.8F.EF.BC.88.E3.82.A8.E3.83.B3.E3.83.88.E3.83.AD.E3.83.94.E3.83.BC.EF.BC.89"&gt;情報エントロピー&lt;/a&gt;の式を用いた。&lt;br /&gt;&lt;br /&gt;

&lt;img style="display:block; margin:0px auto 10px; text-align:center; width: 320px; height: 67px;" src="http://3.bp.blogspot.com/_BwIshVLoLIU/TDxwnJpwHPI/AAAAAAAAAS0/ihqd2M4MlTM/s320/H01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5493389463393410290" /&gt;&lt;br /&gt;

1世代におけるセルの変化は0→0、0→1、1→0、1→1の4通りあり、それらの確率&lt;i&gt;P&lt;sup&gt;k&lt;/sup&gt;&lt;/i&gt;からエントロピーを求めている。またlogの底を4とすることで&lt;i&gt;H&lt;/i&gt;を0～1にスケールしている(参考: &lt;a href="http://www001.upp.so-net.ne.jp/suzudo/c6.pdf"&gt;2次元セルオートマトンの相転移と結晶化&lt;/a&gt;)。&lt;br /&gt;&lt;br /&gt;

表示されるセルの色は生存セル数が多いほど赤く、少ないほど青く表示される。また、背景色はエントロピーに比例しており、エントロピーが高いほど明るい緑となり、低いほど暗くなる。&lt;br /&gt;&lt;br /&gt;

以下にいくつかのルールによる変化を例示する。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;ルール &lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;[..=+.....]&lt;/span&gt; / 初期生存セルの割合 0.3&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

これはライフゲームのルールであり、初期生存セル数の割合は0.3としている。最初は生存セル数が多くエントロピーが高いので背景は比較的明るい緑となっているが、徐々に生存セル数およびエントロピーが減ってきて暗くなってきている。5万ほどいた生存セルも1万を切るぐらいに減ってしまい、活動しているセルも縮小していることが分かる。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BwIshVLoLIU/TDxtd51GygI/AAAAAAAAASU/TcMV76EMGbs/s1600/sb.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 109px;" src="http://4.bp.blogspot.com/_BwIshVLoLIU/TDxtd51GygI/AAAAAAAAASU/TcMV76EMGbs/s400/sb.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5493386005992360450" /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;strong&gt;ルール &lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;[..=++....]&lt;/span&gt; / 初期生存セルの割合 0.9&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

これはライフゲームのルールに周りが4つのセルで「誕生」するルールを加えたものだ。初期生存セル数の割合を0.9と高くして最初にほとんどのセルを死滅させている(周りの生存セル数が高いと次の世代で「死亡」するため)。これにより非常に少ない生存セルから始めることができる。このルールではほんの僅かに残った生存セルからでも驚異的な再生力を示し、最終的にはすべてに生存セルが生息し、エントロピーは0.98以上と非常に高い値を示す。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BwIshVLoLIU/TDxtezcvPhI/AAAAAAAAASc/wLAEi18Y8cc/s1600/sbb.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 109px;" src="http://1.bp.blogspot.com/_BwIshVLoLIU/TDxtezcvPhI/AAAAAAAAASc/wLAEi18Y8cc/s400/sbb.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5493386021459410450" /&gt;&lt;/a&gt;&lt;br /&gt;

&lt;strong&gt;ルール &lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;[..=+====.]&lt;/span&gt; / 初期生存セルの割合 0.1&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

これは現状維持の性質を非常に強く持ったルールで、初期生存セル数の割合を0.1から始めるとスピードは遅いが侵食するように生存セルが繁殖していき、最終的には全体の75%ほどを生存セルで占める。動きはほとんど見られないのでエントロピーは0.4ほどであるが、生存セル数が多いので画面は赤くなる。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BwIshVLoLIU/TDxtfzVqfhI/AAAAAAAAASk/hCBxIp0Z8bo/s1600/sbssss.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 109px;" src="http://4.bp.blogspot.com/_BwIshVLoLIU/TDxtfzVqfhI/AAAAAAAAASk/hCBxIp0Z8bo/s400/sbssss.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5493386038609608210" /&gt;&lt;/a&gt;&lt;br /&gt;

この他にも&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;[..+=.....] 0.9&lt;/span&gt;、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;[..=+=....] 0.9&lt;/span&gt;、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;[..=+&amp;....] 0.9&lt;/span&gt;なども面白いので試してみて欲しい。セルの繁殖する様は、実際の細胞増殖を顕微鏡で眺めているかのようであり、とても興味深い。&lt;br /&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-5321687076545058004?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/5321687076545058004/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=5321687076545058004' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/5321687076545058004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/5321687076545058004'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/07/2.html' title='2次元セルオートマトンとエントロピー'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_BwIshVLoLIU/TDxwnJpwHPI/AAAAAAAAAS0/ihqd2M4MlTM/s72-c/H01.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-8015454934055291026</id><published>2010-06-14T01:19:00.003+09:00</published><updated>2010-06-14T01:37:14.719+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='教育'/><category scheme='http://www.blogger.com/atom/ns#' term='アルゴリズム'/><category scheme='http://www.blogger.com/atom/ns#' term='雑記'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>小学3年生の授業参観でアルゴリズムに出会った</title><content type='html'>小学3年生の娘の日曜授業参観に行ってきた。算数の授業だ。授業の後半、以下のような問題が出された。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;問題&lt;/strong&gt;: ゴマダラチョウとトノサマバッタがあるゲームをしている。0から9までの数字の書かれたカードがそれぞれ一枚ずつ全部で10枚あって、それを使って3桁の数を2つ作り、その差をできるだけ小さくした方が勝ちとなるゲームだ。ただし百の位は0にできない。できるだけ小さくするにはどのようにカードを選べばよいだろうか。&lt;br /&gt;&lt;br /&gt;

これを約30人の生徒に考えさせていた。解き方を先に教えるということはしない。生徒が問題を考えている間、先生は生徒たちを見回り、質問などに答える。しばらくするといろいろと答えが挙がってきた。自分の娘は以下のように考えたようだ。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
百の位は差が1であればどの数字でも良いので、まず十の位を最小にする数字を考えてみると、最小の数字0から最大の数字9を引いた場合が最も小さくなる。同様に一の位では0と9以外の最小・最大の数字を選ぶ。つまり1から8を引いた場合が最小になる。百の位は残りの数字カードから差が1となるものを選ぶ。解答例は「501-498」や「701-698」となり、差は3になる。&lt;br /&gt;&lt;br /&gt;

これはまさにアルゴリズムだ。Pythonであれば以下のコードと同じだろう。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;digits = range(10)
ten = (digits.pop(), digits.pop(0))
one = (digits.pop(), digits.pop(0))
idx = random.randrange(len(digits) - 1)
hund = (digits.pop(idx), digits.pop(idx))
a = reduce(lambda x, y: x * 10 + y, map(array, (hund, ten, one)))
print "%d - %d = %d" % (a[1], a[0], a[1] - a[0])&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

出力例:&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;601 - 598 = 3&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

小学3年生の算数だと思ってそれほど期待せずに観に行ったのだが、生徒たちはみな楽しそうに問題に取り組んでいたし、なかなか面白い授業をしているようで何だか安心した。因みに、今回のようなクラス全員で考える問題は普段からよく行っているとのことだった。&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-8015454934055291026?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/8015454934055291026/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=8015454934055291026' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8015454934055291026'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8015454934055291026'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/06/3.html' title='小学3年生の授業参観でアルゴリズムに出会った'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-4625753497100586615</id><published>2010-06-03T01:37:00.003+09:00</published><updated>2010-06-03T10:14:36.437+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='科学'/><category scheme='http://www.blogger.com/atom/ns#' term='Fortran'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Fortress'/><title type='text'>スーパーコンピュータとプログラミング言語</title><content type='html'>世の中には星の数ほどのプログラミング言語があるかもしれないが、スーパーコンピュータで利用するとなるとかなり限られてくる。ライフサイエンス分野のデータの処理にはPython、Ruby、Perlなどのいわゆるスクリプト言語はよく使われるし、データベース関連だとJavaなどもあるだろう。しかし科学技術計算に限れば、FortranやC/C++が多数を占めると思う。やはり実行効率と既存資産の存在は大きい。&lt;br /&gt;&lt;br /&gt;

数値計算プログラムでは、とにかく速さが求められる。どれだけの速さがあれば十分かだって? この質問はナンセンスだと思う。たとえ現在の最高速度を誇るスーパーコンピュータの1億倍の速度があったとしても十分ではない。使える資源でできる範囲の計算をするだけなのだから。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
自分の携わる分野では一回の計算に半年かかることもざらなので、計算速度は死活問題だ。1分で終わる処理なら倍に高速化したとしても差は30秒でしかない。しかし、半年となるとその差は3ヶ月にもなる。だから、Fortranを使うにしても、よりモダンなFortran90/95だけではなく、&lt;a href="http://handasse.blogspot.com/2008/12/fortran.html"&gt;古めかしいが実行速度の速いFORTRAN77&lt;/a&gt;もよく使われる。まあ、最近は速度差も縮まってきているのでそこまでFORTRAN77にこだわらなくなってきているけど。本当にクリティカルな部分にはアセンブリ言語なども使われている。&lt;br /&gt;&lt;br /&gt;

また、汎用コンピュータのコンパイラに比べてスーパーコンピュータに最適化されたコンパイラではバグの入っていることが多く、C++でもBoostなどの外部ライブラリは避けた方が無難だ。使うにしても&lt;a href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Boost"&gt;GoogleのC++コーディング規約&lt;/a&gt;のように一部に限定して使う方が良い。実際にBoost絡みのコンパイラバグによる混乱を見るに、STLなどの標準ライブラリのみをシンプルに利用していた方がまだ安全だ(それでもバグるけど)。そうは言ってもBoostは便利なのでできるだけ早くC++0xの標準化がされてほしい。標準化されていればコンパイラベンダも言い訳できないしね。&lt;br /&gt;&lt;br /&gt;

そういえば&lt;a href="http://projectfortress.sun.com/Projects/Community"&gt;Fortress&lt;/a&gt;なんて言語もあるね。面白い試みではあるけれど現状で利用するのは難しいような気がするなぁ。特に冒頭でも述べた、Fortranと同程度の実行効率が出せるかという点と既存資産の代替が存在するのかという点で。因みにFortressでの"Hello, World!"は以下のように書くみたい。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;hello.fss&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;component hello
export Executable
run() = println("Hello, World!")
end&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-4625753497100586615?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/4625753497100586615/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=4625753497100586615' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4625753497100586615'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4625753497100586615'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/06/blog-post.html' title='スーパーコンピュータとプログラミング言語'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-83766629822357759</id><published>2010-05-16T01:23:00.010+09:00</published><updated>2010-10-03T00:22:53.068+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>OAuthを使ってAndroidからPythonでTwitterに投稿する</title><content type='html'>以前、&lt;a href="http://handasse.blogspot.com/2010/02/androidpythonluajavascript.html"&gt;ASE (Android Scripting Environment)&lt;/a&gt;を使って「&lt;a href="http://handasse.blogspot.com/2010/03/androidpythontwitter.html"&gt;AndroidからPythonでTwitterに投稿する&lt;/a&gt;」という記事を書いたが、来月末にTwitterのBASIC認証が廃止されるので使えなくなる。そこで、OAuthを利用するコードに書き直してみた。最近はAndroidの&lt;a href="http://twicca.r246.jp/"&gt;twicca&lt;/a&gt;がとても使いやすいのでPythonスクリプトによるTwitterへの投稿もあまりないかもしれないが、Android端末単体でOAuthを利用したTwitterの認証ができることを示す意味でも公開することにした。&lt;br /&gt;&lt;br /&gt;

まず、「&lt;a href="http://handasse.blogspot.com/2010/05/oauthtwitterpython.html"&gt;コマンドラインで動作するOAuth対応TwitterクライアントをPythonで作ってみた&lt;/a&gt;」という記事で用意したoauth.pyとoauthtwitter.pyをAndroid機の/sdcard/ase/scripts/ディレクトリにコピーする。これは&lt;a href="http://handasse.blogspot.com/2010/02/androidpythonluajavascript.html"&gt;adb pushでコピー&lt;/a&gt;してもいいし、SDカードに直接コピーしてもいい。oauthtwitter.pyは一部修正してあるので上述記事の該当箇所を参照して書き直して欲しい。&lt;br /&gt;&lt;br /&gt;

次に、それらのライブラリを利用して以下のようなコードを書いた。赤字で示した認証コードの部分はTwitterのサイトの&lt;a href="http://twitter.com/oauth_clients"&gt;OAuthクライアント登録&lt;/a&gt;で取得する必要がある。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;tw_oauth_ase.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os, pickle, time, android
from oauthtwitter import *

CONSUMER_KEY    = "&lt;span style="color:red;"&gt;CONSUMER_KEY&lt;/span&gt;"
CONSUMER_SECRET = "&lt;span style="color:red;"&gt;CONSUMER_SECRET&lt;/span&gt;"
KEY_FILE        = "/sdcard/ase/scripts/twitter_key.dat"

droid = android.Android()

def twitter():
  if os.path.isfile(KEY_FILE):
    access_token = pickle.load(file(KEY_FILE))
  else:
    tw = OAuthApi(CONSUMER_KEY, CONSUMER_SECRET)
    request_token = tw.getRequestToken()
    authorization_url = tw.getAuthorizationURL(request_token)
    droid.view(authorization_url)
    tw = OAuthApi(CONSUMER_KEY, CONSUMER_SECRET, request_token)
    time.sleep(60)
    oauth_verifier = droid.getInput(u"What is the PIN?", u"暗証番号を入力してください")["result"].strip()
    access_token = tw.getAccessTokenWithPin(oauth_verifier)
    pickle.dump(access_token, file(KEY_FILE, "w"))
  return OAuthApi(CONSUMER_KEY, CONSUMER_SECRET, access_token)

def main():
  tw = twitter()
  post = droid.getInput(u"Tweet", u"いまどうしてる?")["result"].strip()
  if post:
    tw.PostUpdate(post.encode("utf-8"))
    droid.makeToast(post)

if __name__ == "__main__": main()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

使い方は以下の通り。一番最初の起動時だけOAuthによる認証をする必要がある。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
まずはASEを起動して以下の画面で先に示したtw_oauth_ase.pyを実行する。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BwIshVLoLIU/S-7LQuse81I/AAAAAAAAARA/Cm_-hrpYhyU/s1600/device01.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://1.bp.blogspot.com/_BwIshVLoLIU/S-7LQuse81I/AAAAAAAAARA/Cm_-hrpYhyU/s400/device01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5471534085574685522" /&gt;&lt;/a&gt;&lt;br /&gt;

実行させると以下のようにブラウザが起動してTwitterのOAuth認証画面が表示される。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_BwIshVLoLIU/S-7LQ1yKOJI/AAAAAAAAARI/OJy64Ut5YHk/s1600/device03.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://2.bp.blogspot.com/_BwIshVLoLIU/S-7LQ1yKOJI/AAAAAAAAARI/OJy64Ut5YHk/s400/device03.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5471534087477541010" /&gt;&lt;/a&gt;&lt;br /&gt;

「許可する」を選ぶと以下の画面が表示される。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BwIshVLoLIU/S-7LRRnHNiI/AAAAAAAAARQ/9mRAS0XoiQI/s1600/device04.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://1.bp.blogspot.com/_BwIshVLoLIU/S-7LRRnHNiI/AAAAAAAAARQ/9mRAS0XoiQI/s400/device04.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5471534094947399202" /&gt;&lt;/a&gt;&lt;br /&gt;

表示された暗証番号をメニューボタンから「テキストを選択してコピー」を選び、番号をコピーする。ここまでの作業を一分以内に完了させる必要がある。ただし、tw_oauth_ase.pyの&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;time.sleep(60)&lt;/span&gt;の数値(秒)を設定することで完了させるまでの時間を変更することができる。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_BwIshVLoLIU/S-7LRg3UJtI/AAAAAAAAARY/SfuQ-EeHzPg/s1600/device05.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://3.bp.blogspot.com/_BwIshVLoLIU/S-7LRg3UJtI/AAAAAAAAARY/SfuQ-EeHzPg/s400/device05.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5471534099041887954" /&gt;&lt;/a&gt;&lt;br /&gt;

一分後に以下のダイアログボックスが表示されるので、クリップボードにコピーした暗証番号を入力する。クリップボードから入力するにはテキストエリアを長押しすれば良い。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_BwIshVLoLIU/S-7LR1sSVEI/AAAAAAAAARg/U-fAtMtbeGc/s1600/device08.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://2.bp.blogspot.com/_BwIshVLoLIU/S-7LR1sSVEI/AAAAAAAAARg/U-fAtMtbeGc/s400/device08.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5471534104632775746" /&gt;&lt;/a&gt;&lt;br /&gt;

認証作業が完了すると以下のメッセージ入力ダイアログボックスが表示される。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BwIshVLoLIU/S-7LZw64YfI/AAAAAAAAARo/RVr_bUzMdUQ/s1600/device09.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://4.bp.blogspot.com/_BwIshVLoLIU/S-7LZw64YfI/AAAAAAAAARo/RVr_bUzMdUQ/s400/device09.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5471534240790766066" /&gt;&lt;/a&gt;&lt;br /&gt;

入力したメッセージがAndroid画面に表示され、Twitterに投稿される。次以降の起動時には最初から上記のダイアログボックスが表示され、認証作業の必要はない。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;追記&lt;/strong&gt;(2010/5/16):&lt;br /&gt;&lt;br /&gt;

&lt;a href="http://code.google.com/p/python-twitter/"&gt;twitter.py&lt;/a&gt;は日本語だと140文字の半分ほどでツイートできなくなってしまうので、文字コードをutf-8、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;len(status)&lt;/span&gt;を&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;len(status.decode("utf-8"))&lt;/span&gt;に変更してバイトコンパイル、Android端末の/sdcard/ase/extras/にコピーして使っている。&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-83766629822357759?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/83766629822357759/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=83766629822357759' title='3 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/83766629822357759'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/83766629822357759'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/05/oauthandroidpythontwitter.html' title='OAuthを使ってAndroidからPythonでTwitterに投稿する'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_BwIshVLoLIU/S-7LQuse81I/AAAAAAAAARA/Cm_-hrpYhyU/s72-c/device01.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-5283777949220773528</id><published>2010-05-12T01:06:00.007+09:00</published><updated>2010-12-29T03:59:48.918+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>C++0xでYコンビネータ</title><content type='html'>&lt;a href="http://stackoverflow.com/"&gt;stackoverflow.com&lt;/a&gt;の&lt;a href="http://stackoverflow.com/questions/152084/fixed-point-combinators-in-c/154267#154267"&gt;Fixed point combinators in C++&lt;/a&gt; (C++による不動点結合子)に載っていたC++とboostで書かれたYコンビネータ(Y combinator)のサンプルコードをC++0xを使って書き直してみた。C++0xだと標準ライブラリだけでこれだけ簡潔に書ける。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;functional&amp;gt;
 
using namespace std;
 
// Y-combinator for the int type
function&amp;lt;int(int)&amp;gt; y(function&amp;lt;int(function&amp;lt;int(int)&amp;gt;, int)&amp;gt; f)
{ return bind(f, bind(&amp;amp;y, f), placeholders::_1); }
 
int main()
{
    // Y-combinator compatible factorial
    auto fact = [](function&amp;lt;int(int)&amp;gt; f, int v){ return v == 0 ? 1 : v * f(v - 1); };
    auto factorial = y(fact);
    cout &amp;lt;&amp;lt; factorial(5) &amp;lt;&amp;lt; endl;
    return 0;
}&lt;/span&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-5283777949220773528?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/5283777949220773528/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=5283777949220773528' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/5283777949220773528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/5283777949220773528'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/05/c0xy.html' title='C++0xでYコンビネータ'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-1950124618760979492</id><published>2010-05-06T01:20:00.009+09:00</published><updated>2010-05-06T03:11:25.134+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WWW'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='インターネット'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>一行でテキストに含まれるURLをすべて短縮URLに変換するPythonスクリプト</title><content type='html'>テキストに含まれるURLをすべてbit.lyの短縮URLに変換するPythonワンライナーを書いた。使用しているモジュールはre、urllib、urllib2、simplejson。コードに一行追加するだけだし、Twitterなどのメッセージを処理するのに便利だと思う。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;for link in sorted(re.findall(r"(http(?:s?)\:\/\/(?!bit\.ly[\/\ ])[^\/\ ]+\/?.*?)(?:[\ &amp;lt;&amp;gt;\"\{\}\|\\\^\[\]\`]|$)", msg), reverse=True): msg = (lambda x: re.sub(re.escape(x), (lambda y: str(simplejson.loads(urllib2.urlopen("http://api.bit.ly/shorten?version=2.0.1&amp;amp;longUrl=%s&amp;amp;login=&lt;span style="color:red;"&gt;BITLY_ID&lt;/span&gt;&amp;amp;apiKey=&lt;span style="color:red;"&gt;BITLY_API_KEY&lt;/span&gt;" % urllib.quote(y)).read())["results"][y]["shortUrl"]))(x), msg))(link)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

bit.lyのAPIキーは&lt;a href="http://bit.ly/a/sign_up"&gt;別途取得&lt;/a&gt;しておく必要がある。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;msg = u"ここのブログのURLはhttp://handasse.blogspot.com/ です。記事のURLはhttp://handasse.blogspot.com/2010/05/urlurlpython.html です。"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

としてURLを含むメッセージを処理すると以下のように変換される。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;print msg
ここのブログのURLはhttp://bit.ly/PQa9F です。記事のURLはhttp://bit.ly/cVRVu6 です。&lt;/span&gt;&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
上記のコードを展開したものが以下のコード。簡単なエラー処理を追加してある。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;def bitly(x):
    try:
        data = urllib2.urlopen("http://api.bit.ly/shorten?version=2.0.1&amp;amp;longUrl=%s&amp;amp;login=&lt;span style="color:red;"&gt;BITLY_ID&lt;/span&gt;&amp;amp;apiKey=&lt;span style="color:red;"&gt;BITLY_API_KEY&lt;/span&gt;" % urllib.quote(x)).read()
        return str(simplejson.loads(data)["results"][x]["shortUrl"])
    except:
        return x
replace_url = lambda x: re.sub(re.escape(x), bitly(x), msg)
links = sorted(re.findall(r"(http(?:s?)\:\/\/(?!bit\.ly[\/\ ])[^\/\ ]+\/?.*?)(?:[\ &amp;lt;&amp;gt;\"\{\}\|\\\^\[\]\`]|$)", msg), reverse=True)
for link in links:
    msg = replace_url(link)&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-1950124618760979492?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/1950124618760979492/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=1950124618760979492' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1950124618760979492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1950124618760979492'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/05/urlurlpython.html' title='一行でテキストに含まれるURLをすべて短縮URLに変換するPythonスクリプト'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-6761028967044025509</id><published>2010-05-02T00:07:00.007+09:00</published><updated>2010-05-02T03:53:09.538+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='セキュリティ'/><category scheme='http://www.blogger.com/atom/ns#' term='Twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>コマンドラインで動作するOAuth対応TwitterクライアントをPythonで作ってみた</title><content type='html'>6月末にTwitter APIのBASIC認証が終了してしまうので、OAuth対応のTwitterクライアントを作ってみることにした。とりあえず最もシンプルだと思われるコマンドラインで動作するクライアントをPythonで作成してみた。&lt;br /&gt;&lt;br /&gt;

まず、Twitterクライアントを作成する前に、Twitterのサイトで&lt;a href="http://twitter.com/oauth_clients"&gt;OAuthクライアントの登録&lt;/a&gt;を行わなくてはならない。ここで、「アプリケーーション名」、「アプリケーションの説明」、「アプリケーションのウェブサイトURL」を記入する必要がある。また、今回はPC上で実行するクライアントで読み書きを行いたかったので、「あなたの招待状」には「送信」、"Default Accdess type"には"Read &amp; Write"を選択した。&lt;br /&gt;&lt;br /&gt;

登録を済ますと、"Application Details"のページで"Consumer key"と"Consumer secret"が与えられるので、これを作成するアプリケーションで利用する(CONSUMER_KEY, CONSUMER_SECRET)。&lt;br /&gt;&lt;br /&gt;

次にコーディングだが、できるだけ短くシンプルに作りたかったので、Python外部モジュールの&lt;a href="http://code.google.com/p/python-twitter/"&gt;twitter&lt;/a&gt;、&lt;a href="http://oauth.googlecode.com/svn/code/python/oauth/"&gt;oauth&lt;/a&gt;、&lt;a href="http://oauth-python-twitter.googlecode.com/svn/trunk/"&gt;oauthtwitter&lt;/a&gt;を利用させてもらうことにした。ただ、&lt;a href="http://agileweb.wordpress.com/2009/04/28/how-to-use-oauth-python-twitter/"&gt;作者のページ&lt;/a&gt;のコードでは途中でエラーになってしまうので、 oauthtwitter.pyのOAuthApiクラスのgetAccessTokenメソッドの下に以下のgetAccessTokenWithPinメソッドを追加した。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;def getAccessTokenWithPin(self, pin, url=ACCESS_TOKEN_URL):
    token = self._FetchUrl(url, parameters={"oauth_verifier": pin}, no_cache=True)
    return oauth.OAuthToken.from_string(token)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

今回作成したTwitterクライアントの使い方だが、&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;tw.py&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

で最新のタイムラインを表示する。取得件数はここでは20件にしているが、ソースコード中の引数で変更することができる。Twitterへの投稿は以下のようにすれば良い。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;tw.py "つぶやき"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

最初の起動時に、アクセストークンを取得するための認証URLが表示される。それをブラウザで開いて表示された認証番号(PIN)を入力する必要がある。取得したアクセストークンは指定されたファイル(KEY_FILE)に保存され、次回以降はそのファイルからアクセストークンを取得する。&lt;br /&gt;&lt;br /&gt;

ソースコードを以下に示す。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;strong&gt;tw.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os, pickle
from oauthtwitter import *

CONSUMER_KEY    = "&lt;span style="color:red;"&gt;CONSUMER_KEY&lt;/span&gt;"
CONSUMER_SECRET = "&lt;span style="color:red;"&gt;CONSUMER_SECRET&lt;/span&gt;"
KEY_FILE        = "&lt;span style="color:red;"&gt;twitter_key.dat&lt;/span&gt;"

def twitter():
    if os.path.isfile(KEY_FILE):
        access_token = pickle.load(file(KEY_FILE))
    else:
        tw = OAuthApi(CONSUMER_KEY, CONSUMER_SECRET)
        request_token = tw.getRequestToken()
        authorization_url = tw.getAuthorizationURL(request_token)
        print authorization_url
        tw = OAuthApi(CONSUMER_KEY, CONSUMER_SECRET, request_token)
        oauth_verifier = raw_input("What is the PIN? ")
        access_token = tw.getAccessTokenWithPin(oauth_verifier)
        pickle.dump(access_token, file(KEY_FILE, "w"))
    return OAuthApi(CONSUMER_KEY, CONSUMER_SECRET, access_token)

def main(args):
    tw = twitter()
    if len(args) &amp;lt; 2:
        for status in tw.GetFriendsTimeline(count=20):
            print status.GetUser().GetScreenName() + ":", status.GetText().encode("cp932", "replace")
    else:
        post = " ".join(args[1:])
        tw.PostUpdate(post.decode("cp932").encode("utf-8"))

if __name__ == "__main__": main(sys.argv)&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-6761028967044025509?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/6761028967044025509/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=6761028967044025509' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/6761028967044025509'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/6761028967044025509'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/05/oauthtwitterpython.html' title='コマンドラインで動作するOAuth対応TwitterクライアントをPythonで作ってみた'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-8494336318364530580</id><published>2010-04-29T21:24:00.010+09:00</published><updated>2010-05-06T01:41:15.524+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WWW'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='セキュリティ'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Google App EngineとSafe Browsing APIを利用して短縮URLチェッカーを作ってみた</title><content type='html'>最近、Twitterやはてなブックマークなどを使うことが多くなり、字数制限付きのコメントを書くことが増えた。コメント中にURLを入れたいこともままあるのだが、長いURLだと字数をオーバーしてしまう。そのため短縮URLを使っているが、コメント内に直接短縮URLを入れるのがやっかいだった。そこで、&lt;a href="http://code.google.com/intl/ja/appengine/"&gt;Google App Engine (GAE)&lt;/a&gt;を使ってシンプルで高速な短縮URL変換ツールを作成してみた。&lt;br /&gt;&lt;br /&gt;

しかし、短縮URLは悪意のあるサイトの判別が難しいという欠点がある。見た目で判断できないので開くのを躊躇してしまう。そこで、今回作成したツールでは、短縮URLに変換するだけではなく、それを元のURLに戻す機能も追加した。さらに、&lt;a href="http://code.google.com/intl/ja/apis/safebrowsing/"&gt;Google Safe Browsing API&lt;/a&gt;を利用してフィッシングサイトなどの悪意あるサイトの判別も試みている。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;&lt;a href="http://nox.appspot.com/short_url"&gt;短縮URLチェッカー&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

使い方はシンプルで、テキストボックスに変換したいURLを入力すればbit.lyの短縮URLがテキストボックス表示され、bit.lyの短縮URLを入力すればもとのURLが表示される。テキストボックスをクリックすれば表示されているURLが選択されるのでクリップボードへのコピーも簡単だ。テキストボックスの下には展開されたURLがリンク付きで表示されるが、もしフィッシングサイトだと疑われる場合は、以下のように警告してくれる。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BwIshVLoLIU/S9l8tubjyGI/AAAAAAAAAQ4/8Aeqi31hnqU/s1600/short_url_warning03a.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 179px;" src="http://1.bp.blogspot.com/_BwIshVLoLIU/S9l8tubjyGI/AAAAAAAAAQ4/8Aeqi31hnqU/s400/short_url_warning03a.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5465536747790387298" /&gt;&lt;/a&gt;

&lt;span id="fullpost"&gt;&lt;br /&gt;
さて、今回のコード作成だが、bit.lyによる短縮URLの変換は簡単だった。というのも&lt;a href="http://handasse.blogspot.com/2009/05/python-urlbitlyapi.html"&gt;以前にPythonスクリプトで作った&lt;/a&gt;ことがあるからだ。それをそのままGAEに載せればいい。しかし、Safe Browsing APIによる悪意あるサイトの検出は思ったよりも面倒だった。まず、APIの使い方がよく分からない。使う前は任意のURLをAPIで確認するのかと思っていたのだが、そうではなく、MD5でハッシュ化したリストをダウンロードして、調べたいURLをハッシュ化してそれと照らし合わせなくてはならない。今回問題になったのはそのダウンロード容量で、フィッシングサイト用データが700KB程、マルウェア用データが10MBを超えていた。GAEでは一回のダウンロード容量の上限が1MBと決まっており、フィッシングサイト用データは何とかダウンロードできても、マルウェア用データは途中で切れてしまった。仕方がないので今回はフィッシングサイト用のみを使うことにした。ただし現在、Safe Browsing APIの新しいバージョンが開発中であり、それを使えば何とかなるかもしれない。自分がちょっと試したときには何故か新しいAPIが使えなかったので、今回は見送ることにした。気が向いたら試してみるかも。&lt;br /&gt;&lt;br /&gt;

APIによりダウンロードされるリストはMD5でハッシュ化されているが、ハッシュ化される前のURLは&lt;a href="http://code.google.com/intl/ja/apis/safebrowsing/developers_guide_v2.html#PerformingLookups"&gt;標準化された参照URL&lt;/a&gt;だ。そこで、調べるURLについても標準化して参照URLを作成しなくてはならない。あまり時間を掛けたくなかったので、今回は標準化については実装しなかった。参照URLの作成については&lt;a href="http://github.com/theju/safebrowsing-python"&gt;Thejaswi Puthraya氏が作成したコード&lt;/a&gt;を利用させてもらった。URLの標準化についてはGoogleのサイトにも参考例が載っているので、時間ができたら作ってみたい。&lt;br /&gt;&lt;br /&gt;

ダウンロードされたリストはデータストアに格納している。ハッシュ化されたURLを個別にデータストアに入れるとデータ数が非常に多くなってしまうので、ハッシュ化リストの先頭の2桁(16進数)をインデックスにし、256分割のデータとして格納した。また、リスト更新中にデータストア上のURLの参照が行われても問題が発生しないように、データモデルを2つ用意して更新は交互に行うことにした。加えて、データストアへのアクセスを減らすためMemcacheを利用している。&lt;br /&gt;&lt;br /&gt;

フィッシングサイト用リストはcronを使って30分毎に更新している。しかし、リスト更新の際に処理が30秒を超えることがあり、そのため更新に失敗してしまうことがある。これはGAEの制限なのだが、前述のデータ取得の容量制限と合わせて、このあたりが改善されるとより素晴らしくなるのだけどなぁ。無料で使わせてもらっているのに贅沢かもしれないけど。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;追記&lt;/strong&gt;(2010/5/2): リスト更新の半分以上が30秒を超えてエラーになったので、&lt;a href="http://code.google.com/intl/ja/appengine/docs/python/taskqueue/"&gt;TaskQueue&lt;/a&gt;による処理に変更した。&lt;br /&gt;&lt;br /&gt;

最後に今回作成したソースコード(実装したクラスのみ)を示しておく。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;class Bitly():
    def __init__(self):
        self.apiurl = "http://api.bit.ly/%s?version=2.0.1&amp;amp;%s=%s&amp;amp;login=&lt;span style="color:red;"&gt;BITLY_ID&lt;/span&gt;&amp;amp;apiKey=&lt;span style="color:red;"&gt;BITLY_API_KEY&lt;/span&gt;"
        self.api_index  = { "shorten": "longUrl", "expand": "shortUrl" }
        self.info_index = { "shorten": "shortUrl", "expand": "longUrl" }

    def is_error(self, url, url_info):
        return url_info["statusCode"] != "OK" or (url_info["results"][url].has_key("statusCode") and url_info["results"][url]["statusCode"] != "OK")

    def bitly(self, url, api):
        url_data = urllib2.urlopen(self.apiurl % (api, self.api_index[api], urllib.quote(url))).read()
        url_info = simplejson.loads(url_data)
        if api == "expand": url = url.replace("http://bit.ly/", "")
        if self.is_error(url, url_info): return ""
        return url_info["results"][url][self.info_index[api]]

    def get(self, url):
        if url.lower().split(":")[0] not in ("http", "https", "ftp"): url = "http://" + url
        bitly_url = ""
        if re.match(r"http://bit.ly/", url) and not re.search("[^a-z^A-Z^0-9]", url.replace("http://bit.ly/", "")):
            self.shorten_url = url
            bitly_url = self.bitly(url, "expand")
            self.long_url = bitly_url
        if not bitly_url:
            self.long_url = url
            bitly_url = self.bitly(url, "shorten")
            self.shorten_url = bitly_url
        return bitly_url

class DataProperty(db.Model):
    phishing = db.IntegerProperty()
    malware = db.IntegerProperty()

class PhishingDataA(db.Model):
    index = db.IntegerProperty()
    hash_urls = db.TextProperty()

class PhishingDataB(db.Model):
    index = db.IntegerProperty()
    hash_urls = db.TextProperty()

class MalwareDataA(db.Model):
    index = db.IntegerProperty()
    hash_urls = db.TextProperty()

class MalwareDataB(db.Model):
    index = db.IntegerProperty()
    hash_urls = db.TextProperty()

class ShortUrl(webapp.RequestHandler):
    def __init__(self):
        self.html = u"""&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&amp;gt;
&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja"&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta http-equiv="content-type" content="text/html; charset=utf-8" /&amp;gt;
    &amp;lt;meta http-equiv="content-script-type" content="text/javascript" /&amp;gt;
    &amp;lt;meta http-equiv="content-style-type" content="text/css" /&amp;gt;
    &amp;lt;link type="text/css" rel="stylesheet" href="/stylesheets/main.css" /&amp;gt;
    &amp;lt;title&amp;gt;短縮URLチェッカー&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;form action="/short_url" method="post"&amp;gt;
      &amp;lt;div&amp;gt;URL: &amp;lt;input type="text" name="content" size="31" maxlength="1024" value="%s" onclick="this.select()" /&amp;gt; &amp;lt;input type="submit" value="変換" /&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/form&amp;gt;
    %s
    &amp;lt;div style="font-size:xx-small;"&amp;gt;&amp;lt;p&amp;gt;短縮URLの変換およびフィッシングサイトの判別を行います。[ver.20100502]&amp;lt;br /&amp;gt;&amp;lt;a href="http://code.google.com/apis/safebrowsing/safebrowsing_faq.html#whyAdvisory"&amp;gt;Advisory provided by Google.&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;"""

    def lookup_by_url(self, url):
        """from http://github.com/theju/safebrowsing-python"""
        url_re = re.compile("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?")
        url = url.lower()

        url_components = url_re.match(url).groups()
        lookup_list = set()
        hostname = url_components[3]
        hostname_comp = hostname.split(".")
        if not hostname_comp:
            raise AttributeError("Invalid URL.")

        for i in xrange(len(hostname_comp) - 1):
            filtered_hostname_comp = ".".join(hostname_comp[i:])
            lookup_list.add(filtered_hostname_comp + "/")
            if url_components[4]:
                path = url_components[4].split('/')
                for j in xrange(len(path) + 1):
                    filtered_paths = '/'.join(path[:j])
                    if not '.' in filtered_paths:
                        lookup_list.add(filtered_hostname_comp + "%s/" % filtered_paths)
                lookup_list.add(filtered_hostname_comp + url_components[4])
                if url_components[5]:
                    lookup_list.add(filtered_hostname_comp + ''.join(url_components[4:6]))
                    if url_components[7]:
                        lookup_list.add(filtered_hostname_comp + ''.join(url_components[4:6]) + url_components[7])
        return lookup_list

    def get(self):
        self.response.out.write(self.html % ("", ""))

    def post(self):
        content = self.request.get('content')
        bitly = Bitly()
        bitly_url = bitly.get(content)
        dp = memcache.get("dp")
        if not dp:
            dp = DataProperty().all().get()
            memcache.add("dp", dp)
        phishing, malware = dp.phishing, dp.malware
        P = PhishingDataA if phishing == 0 else PhishingDataB
        M = MalwareDataA if malware == 0 else MalwareDataB
        urls = self.lookup_by_url(bitly.long_url)
        hash_urls = [hashlib.md5(x).hexdigest() for x in urls]
        is_phishing, is_malware = False, False
        for hash_url in hash_urls:
            idx = int(hash_url[:2], 16)
            phishing_list = memcache.get("phishing_%d" % idx)
            if not phishing_list:
                phishing_list = P().all().filter("index =", idx).get().hash_urls
                memcache.add("phishing_%d" % idx, phishing_list)
            if re.search(hash_url, phishing_list): is_phishing = True
            """
            malware_list = memcache.get("malware_%d" % idx)
            if not malware_list:
                malware_list = M().all().filter("index =", idx).get().hash_urls
                memcache.add("malware_%d" % idx, malware_list)
            if re.search(hash_url, malware_list): is_malware = True
            """
        link = '&amp;lt;a href="%s"&amp;gt;%s&amp;lt;/a&amp;gt;' % (bitly.long_url, bitly.long_url)
        if is_phishing: link += u'&amp;lt;br /&amp;gt;フィッシングサイトの疑いがあります。&amp;lt;span style="font-size:xx-small;color:gray;"&amp;gt;詳しくは&amp;lt;a href="http://www.antiphishing.org"&amp;gt;AntiPhishing.org&amp;lt;/a&amp;gt;を参照してください。&amp;lt;/span&amp;gt;'
        if bitly_url:
            self.response.out.write(self.html % (bitly_url, "&amp;lt;div&amp;gt;&amp;lt;p&amp;gt;%s&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;" % link))
        else:
            self.response.out.write(self.html % ("", u"&amp;lt;div&amp;gt;&amp;lt;p&amp;gt;変換できませんでした。&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;"))

class SafeBrowsing(webapp.RequestHandler):
    def __init__(self):
        self.urls = {
            "phishing": "http://sb.google.com/safebrowsing/update?client=api&amp;amp;apikey=&lt;span style="color:red;"&gt;SAFE_BROWSING_API_KEY&lt;/span&gt;&amp;amp;version=goog-black-hash:1:-1",
            "malware":  "http://sb.google.com/safebrowsing/update?client=api&amp;amp;apikey=&lt;span style="color:red;"&gt;SAFE_BROWSING_API_KEY&lt;/span&gt;&amp;amp;version=goog-malware-hash:1:-1" }

    def get_data(self, target, selection):
        idx = 0
        txt = ""
        all_data = urllib2.urlopen(self.urls[target]).read().split()
        for l in all_data:
            l = l.strip()
            if len(l) == 33 and l[0] == '+':
                current_idx = int(l[1:3], 16)
                if idx != current_idx:
                    taskqueue.add(url="/short_url/safe_browsing/process",
                                  params={"target": target, "selection": selection,
                                          "idx": idx, "txt": txt})
                    txt = ""
                idx = current_idx
                txt += l
        taskqueue.add(url="/short_url/safe_browsing/process",
                      params={"target": target, "selection": selection,
                              "idx": idx, "txt": txt})

    def get(self):
        target = self.request.get("target")
        if target in ("phishing", "malware"):
            dp = DataProperty().all().get()
            if not dp:
                dp = DataProperty(phishing=0, malware=0)
                dp.put()
                dp = dp.all().get()
            if target == "phishing":
                selection = abs(dp.phishing - 1)
                self.get_data(target, selection)
                dp.phishing = selection
            else:
                selection = abs(dp.malware - 1)
                self.get_data(target, selection)
                dp.malware = selection
            self.response.out.write("Done: %s, %d" % (target, selection))
            dp.put()
            memcache.delete("dp")
        else:
            self.response.out.write("Incorrect: %s" % target)

class SafeBrowsingProcess(webapp.RequestHandler):
    def __init__(self):
        self.data = {
            "phishing": lambda (idx): PhishingDataA if idx == 0 else PhishingDataB,
            "malware":  lambda (idx): MalwareDataA if idx == 0 else MalwareDataB }

    def post(self):
        target = self.request.get("target")
        selection = int(self.request.get("selection"))
        idx = int(self.request.get("idx"))
        txt = self.request.get("txt")
        M = self.data[target](selection)
        model = M().all().filter("index =", idx).get()
        if not model: model = M(index=idx, hash_urls="")
        model.hash_urls = txt
        model.put()
        memcache.delete("phishing_%d" % idx)&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-8494336318364530580?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/8494336318364530580/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=8494336318364530580' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8494336318364530580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8494336318364530580'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/04/google-app-enginesafe-browsing-apiurl.html' title='Google App EngineとSafe Browsing APIを利用して短縮URLチェッカーを作ってみた'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_BwIshVLoLIU/S9l8tubjyGI/AAAAAAAAAQ4/8Aeqi31hnqU/s72-c/short_url_warning03a.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-1645187263302252123</id><published>2010-04-14T23:24:00.007+09:00</published><updated>2010-04-15T02:09:34.021+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WWW'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>一つのGoogle App Engineアプリケーションで複数のWaveボットを作成する</title><content type='html'>&lt;a href="http://code.google.com/intl/ja/appengine/"&gt;Google App Engine (GAE)&lt;/a&gt;では現在10個までのアプリケーションを登録することができる。そして、GAEを利用してGoogle Waveのボットを作成することができる。しかし、多種多様な利用方法が存在するWaveボットをGAEアプリケーションとして登録していてはすぐに上限に達してしまうだろう。そこで、&lt;a href="http://groups.google.co.jp/group/google-wave-api-japan/browse_thread/thread/ea83813e34d170ca?hl=ja"&gt;一つのGAEアプリケーションで複数のWaveボットを作成するためのいくつかの方法&lt;/a&gt;を利用することになる。サブドメインを利用する方法が一般的だろうか。&lt;br /&gt;&lt;br /&gt;

しばらく前からサブドメインを利用して複数のWaveボットを作成しようと思っていたのだが、ちょっと面倒に思えて手を付けていなかった。そうしたら、&lt;a href="http://d.hatena.ne.jp/technohippy/"&gt;technohippy&lt;/a&gt;氏による&lt;a href="http://github.com/technohippy/appengine_multi_robot_runner/"&gt;appengine_multi_robot_runner&lt;/a&gt;というライブラリが公開され、それがとても便利そうだったので簡単なWaveボットを作成してみた。ただし、&lt;a href="http://code.google.com/p/wave-robot-python-client/downloads/list"&gt;Google Wave Robots API v2&lt;/a&gt;が3月30日にアップデートされて実装の一部が変更されたことにより、appengine_multi_robot_runner.pyがそのままでは動作しなかったので該当箇所を修正した。それについては最後に記述しておく。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;追記&lt;/strong&gt;(2010/4/15): 現在、&lt;a href="http://github.com/technohippy/appengine_multi_robot_runner/"&gt;appengine_multi_robot_runner&lt;/a&gt;は最新版に更新されているので、後述の修正は必要無くなっている。&lt;br /&gt;&lt;br /&gt;

作成したのはインチキ日本語ボット(fake-japanese.robotic-wave@appspot.com)とインチキ英語ボット(fake-english.robotic-wave@appspot.com)の二つのWaveボットだ。インチキ日本語ボットでは、書き込んだ日本語を一度Googleにより英語に翻訳し、それを再度日本語に翻訳し直して表示する。インチキ英語ボットは、日本語を英語に翻訳したものを表示する。インチキと書いたが、特にでたらめにするために細工しているわけではない。現在の翻訳技術ではインチキっぽく見えてしまうというだけである。因みに、これらのボットのアイコン画像は&lt;a href="http://www.atjp.net/"&gt;Atnet Japan!&lt;/a&gt;を利用させてもらった。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
どのように動作するか、以下の例文を実際に入力して動作を見てみよう。&lt;br /&gt;&lt;br /&gt;

「花便り」&lt;br /&gt;
「ここ数日は暖かな日が続いていますが、その後いかがお過ごしでしょうか。」&lt;br /&gt;
「待ちに待った桜の花もそろそろ咲き始めております。近いうちにお花見にお誘いしようと思っておりますが、ご都合はいかがでしょうか。」&lt;br /&gt;&lt;br /&gt;

インチキ日本語ボット(fake-japanese.robotic-wave@appspot.com)では以下の通り。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BwIshVLoLIU/S8XRZ3UOtKI/AAAAAAAAAQo/kg_wqEPqe9I/s1600/wave20100414a01.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 238px;" src="http://1.bp.blogspot.com/_BwIshVLoLIU/S8XRZ3UOtKI/AAAAAAAAAQo/kg_wqEPqe9I/s400/wave20100414a01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5460000365532329122" /&gt;&lt;/a&gt;&lt;br /&gt;

インチキ英語ボット(fake-english.robotic-wave@appspot.com)では以下の通り。&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_BwIshVLoLIU/S8XRmJVXDQI/AAAAAAAAAQw/iWraj7BYHYo/s1600/wave20100414b01.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 242px;" src="http://3.bp.blogspot.com/_BwIshVLoLIU/S8XRmJVXDQI/AAAAAAAAAQw/iWraj7BYHYo/s400/wave20100414b01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5460000576527338754" /&gt;&lt;/a&gt;&lt;br /&gt;

後述のソースコードを見てもらっても分かるように、この程度の内容であれば非常に短いコードで済んでしまう。Waveボットは一般的なネットボットよりもインタラクティブに利用でき、効果も大きいので、このように簡単に作成できることは非常に重要だと思う。簡単に作成できると言えば、technohippy氏による&lt;a href="http://jp.as-a-robot.appspot.com/"&gt;as-a-robot@appspot.com&lt;/a&gt;を利用することで、GAEを意識せずにGoogle Wave上で簡単にWaveボットを作成することができる。&lt;br /&gt;&lt;br /&gt;

以下にWaveボットのソースコードを示す。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;robotic-wave.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""robotic-wave.py  by nox, 2010.4.14"""

import urllib, urllib2
from waveapi import robot
from waveapi import events
from waveapi import simplejson
import appengine_multi_robot_runner

def Translate(text, from_lang, to_lang):
    url = "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&amp;amp;q=%s&amp;amp;langpair=%s%%7C%s" % (urllib.quote(text.encode("utf-8")), from_lang, to_lang)
    data = simplejson.loads(urllib2.urlopen(url).read())
    return data["responseData"]["translatedText"]

class FakeJapaneseRobot(robot.Robot):
    def __init__(self):
        robot.Robot.__init__(self, "FakeJapanese",
                             image_url="http://robotic-wave.appspot.com/assets/edodaikan03i.gif",
                             profile_url="http://robotic-wave.appspot.com/")
        self.register_handler(events.BlipSubmitted, self.OnBlipSubmitted)

    def OnBlipSubmitted(self, event, wavelet):
        blp = event.blip
        text = blp.text.strip()
        text = Translate(Translate(text, "ja", "en"), "en", "ja")
        blp.range(1, len(blp.text)).replace(text.encode("utf-8"))

class FakeEnglishRobot(robot.Robot):
    def __init__(self):
        robot.Robot.__init__(self, "FakeEnglish",
                             image_url="http://robotic-wave.appspot.com/assets/202aschwarzenegger.gif",
                             profile_url="http://robotic-wave.appspot.com/")
        self.register_handler(events.BlipSubmitted, self.OnBlipSubmitted)

    def OnBlipSubmitted(self, event, wavelet):
        blp = event.blip
        text = blp.text.strip()
        text = Translate(text, "ja", "en")
        blp.range(1, len(blp.text)).replace(text.encode("utf-8"))

def main():
    appengine_multi_robot_runner.compound_and_run([
        ("fake-japanese", FakeJapaneseRobot()),
        ("fake-english", FakeEnglishRobot())
        ])

if __name__ == "__main__": main()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;app.yaml&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;application: robotic-wave
version: 1
runtime: python
api_version: 1

handlers:
- url: /_wave/(.*)
 script: robotic-wave.py

- url: /assets
 static_dir: assets

- url: /(.*)
 script: index.py&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

Google Wave Robots API v2が3月30日にアップデートされ実装が一部変更された。そのため、appengine_multi_robot_runner.pyが動作しなくなったのでそれを修正するために加えた変更を以下に示しておく。&lt;br /&gt;&lt;br /&gt;

変更箇所その1。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;class GetHandler(appengine_robot_runner.GetHandler):
  def __init__(self, method, contenttype):
    appengine_robot_runner.GetHandler.__init__(self, method, contenttype)

  def get(self):
    self.response.headers['Content-Type'] = self._contenttype
    self.response.out.write(self._method(host=self.request.host))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

上記のコードを以下のように変更する。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;class &lt;span style="color:red;"&gt;CapabilitiesHandler&lt;/span&gt;(appengine_robot_runner.&lt;span style="color:red;"&gt;CapabilitiesHandler&lt;/span&gt;):
  def __init__(self, method, contenttype):
    appengine_robot_runner.&lt;span style="color:red;"&gt;CapabilitiesHandler&lt;/span&gt;.__init__(self, method, contenttype)

  def get(self):
    self.response.headers['Content-Type'] = self._contenttype
    self.response.out.write(self._method(host=self.request.host))


class &lt;span style="color:red;"&gt;ProfileHandler&lt;/span&gt;(appengine_robot_runner.&lt;span style="color:red;"&gt;ProfileHandler&lt;/span&gt;):
  def __init__(self, method, contenttype):
    appengine_robot_runner.&lt;span style="color:red;"&gt;ProfileHandler&lt;/span&gt;.__init__(self, method, contenttype)

  def get(self):
    self.response.headers['Content-Type'] = self._contenttype
    &lt;span style="color:red;"&gt;if self.request.get('name'):
      self.response.out.write(self._method(self.request.get('name'), host=self.request.host))
    else:&lt;/span&gt;
      self.response.out.write(self._method(host=self.request.host))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

変更箇所その2。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;([('/_wave/capabilities.xml',
   lambda: GetHandler(robot.capabilities_xml,
                      'application/xml')),
  ('/_wave/robot/profile',
   lambda: GetHandler(robot.profile_json,
                      'application/json')),&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

上記のコードを以下のように変更する。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;([('/_wave/capabilities.xml',
   lambda: &lt;span style="color:red;"&gt;CapabilitiesHandler&lt;/span&gt;(robot.capabilities_xml,
                      'application/xml')),
  ('/_wave/robot/profile',
   lambda: &lt;span style="color:red;"&gt;ProfileHandler&lt;/span&gt;(robot.profile_json,
                      'application/json')),&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-1645187263302252123?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/1645187263302252123/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=1645187263302252123' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1645187263302252123'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1645187263302252123'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/04/google-app-enginewave.html' title='一つのGoogle App Engineアプリケーションで複数のWaveボットを作成する'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_BwIshVLoLIU/S8XRZ3UOtKI/AAAAAAAAAQo/kg_wqEPqe9I/s72-c/wave20100414a01.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-9113558696394174962</id><published>2010-04-06T02:30:00.005+09:00</published><updated>2010-06-27T17:38:05.320+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Android Scripting Environment (ASE) Python API簡易リファレンス</title><content type='html'>&lt;a href="http://code.google.com/p/android-scripting/"&gt;Android Scripting Environment (ASE)&lt;/a&gt;について、「&lt;a href="http://handasse.blogspot.com/2010/02/androidpythonluajavascript.html"&gt;Android上でPython、Lua、JavaScriptなどを実行するスクリプティング環境が凄い&lt;/a&gt;」で紹介した。今回はAndroid端末の機能を利用するためのPython APIの使い方をリファレンスとしてまとめてみた。Python 2.6の標準モジュールは最初から利用できる。また、twitterモジュールなど、いくつかの標準外のモジュールがデフォルトでインストールされている。詳細については利用する環境で確認して欲しい。&lt;br /&gt;&lt;br /&gt;
 
ASE上のPythonスクリプトの編集画面でメニューボタンを押し、そこから"API Browser"で簡単なリファレンスを読むことができる。また、ASEのWikiとして&lt;a href="http://code.google.com/p/android-scripting/w/list"&gt;Wiki pages - android-scripting&lt;/a&gt;、サンプルプログラムとして&lt;a href="http://code.google.com/p/android-scripting/source/browse/python/ase/scripts/test.py"&gt;test.py&lt;/a&gt;が参考になる。&lt;br /&gt;&lt;br /&gt;

以下にASE Python APIの簡易リファレンスを示すが、APIのすべてを記しているわけではない。足りない部分については随時追加していく予定だ。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;strong&gt;Androidモジュールを使用する&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;import android
droid = android.Android()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;現在のクリップボード取得と貼り付け&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;clip = droid.getClipboard()["result"]
droid.setClipboard("Hello, world!")&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;GDataの使用&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;import gdata.docs.service
 
client = gdata.docs.service.DocsService()  # クライアント.
client.ClientLogin(username, password)  # 接続.
feed = client.GetDocumentListFeed()  # ドキュメントリストのAtomをフィード.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;GPSで現在の位置情報を取得&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.startLocating()  # 開始.
location = droid.readLocation()["result"]
lat = location["network"]["latitude"]  # 緯度.
lng = location["network"]["longitude"]  # 経度.
droid.stopLocating()  # 停止.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

位置情報取得などのイベントを受け取るタイプのAPIでは、開始直後にイベントを取得できない場合がある。そこで以下のように数回リトライできるようにする。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;for i in range(10):
  location = droid.readLocation()["result"]
  if location: break
  time.sleep(1)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;Android端末のセンサー情報を取得&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.startSensing()  # 開始.
sensors = droid.readSensors()["result"]
droid.stopSensing()  # 停止.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
センサー情報はディクショナリとして保存されている。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;sensors
  accuracy 精度.
  azimuth  方位(0-360)
  pitch    縦の傾き.
  roll     横の傾き.
  xforce   X方向の加速度.
  yforce   Y方向の加速度.
  zforce   Z方向の加速度.
  xmag     X方向の磁場.
  ymag     Y方向の磁場.
  zmag     Z方向の磁場.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;Android端末で音声出力&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.speak("Hello, world!")&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;Android端末で音声認識&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;result = droid.recognizeSpeech()["result"]
droid.makeToast(result)  # 認識した語句を表示.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;指定したURLでウェブブラウザを開く&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.view("http://www.google.com/")&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;指定した語句をネット検索&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;result = droid.recognizeSpeech()["result"]  # 音声認識で語句を指定.
droid.webSearch(result)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;通話状況の取得&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.startTrackingPhoneState()  # 開始.
result = droid.readPhoneState()["result"]
droid.stopTrackingPhoneState()  # 停止.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;サイレントモードのトグル&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.toggleRingerSilentMode()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;電話呼び出し音の音量情の取得と変更&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;vol = droid.getRingerVolume()
droid.setRingerVolume(0)  # 音量を0にする.
droid.setRingerVolume(vol["result"])  # 元に戻す.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;最後に取得した位置情報の取得&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;result = droid.getLastKnownLocation()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;ジオコード情報を取得&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;result = droid.geocode(35.698, 139.774)  # 緯度, 経度.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;Wi-Fiのトグル&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.toggleWifiState()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;Android端末に文字を表示&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.makeToast("Hello, world!")&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;Android端末を振動させる&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.vibrate()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;Android端末にメッセージを送る&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.notify("Hello, world!")&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;実行中のAndroidパッケージの取得&lt;/strong&gt;(com.android.phoneなど)&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;result = droid.getRunningPackages()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;テキスト入力ダイアログの表示&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;result = droid.getInput("title", "message")["result"]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;ボタン付きダイアログの表示&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.dialogCreateAlert("title", "message")
droid.dialogSetPositiveButtonText("Yes")
droid.dialogSetNegativeButtonText("No")
droid.dialogSetNeutralButtonText("Cancel")
droid.dialogShow()
response = droid.dialogGetResponse()["result"]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;response["which"]には以下の値が入る:
  Yesボタン:    "positive"
  Noボタン:     "negative"
  Cancelボタン: "neutral"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
指定したボタンが1つ(例えばdroid.dialogSetPositiveButtonText("Yes"))なら1つのボタンのみを表示する。&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;スピナープログレス(回転型進捗アイコン)付きダイアログ&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.dialogCreateSpinnerProgress("title", "message")
droid.dialogShow()
time.sleep(2)
droid.dialogDismiss()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;プログレスバー付きダイアログ&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.dialogCreateHorizontalProgress("title", "message", 50)  # 50分割.
droid.dialogShow()
for i in range(50):
  time.sleep(0.1)
  droid.dialogSetCurrentProgress(i)
droid.dialogDismiss()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;リスト付きダイアログの表示&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;droid.dialogCreateAlert("title")
droid.dialogSetItems(["foo", "bar", "baz"])
droid.dialogShow()
response = droid.dialogGetResponse()["result"]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
response["item"]にはリストの順に0からの数値が入る。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;追記&lt;/strong&gt;(2010/6/27):&lt;br /&gt;&lt;br /&gt;

ASE r22からreadLocation()のデータ構造が変更されたようなので、それに合わせて上述のコードを修正した。&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-9113558696394174962?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/9113558696394174962/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=9113558696394174962' title='3 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/9113558696394174962'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/9113558696394174962'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/04/android-scripting-environment-ase.html' title='Android Scripting Environment (ASE) Python API簡易リファレンス'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-8191870015975245003</id><published>2010-03-16T04:29:00.008+09:00</published><updated>2010-03-17T15:46:08.180+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WWW'/><category scheme='http://www.blogger.com/atom/ns#' term='メール'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><category scheme='http://www.blogger.com/atom/ns#' term='インターネット'/><title type='text'>Google Waveを使わないと人生損する</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_BwIshVLoLIU/S56L12vzSSI/AAAAAAAAAQI/t0BWSnVIhQE/s1600-h/wavelogo.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 200px;" src="http://3.bp.blogspot.com/_BwIshVLoLIU/S56L12vzSSI/AAAAAAAAAQI/t0BWSnVIhQE/s200/wavelogo.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5448946356510214434" /&gt;&lt;/a&gt;
「Google Waveを使わないと人生損する」…これはちょっと大げさかもしれないが、1年半前に書いたブログ記事「&lt;a href="http://handasse.blogspot.com/2008/09/evernote.html"&gt;Evernoteを使わないと人生損する&lt;/a&gt;」で言及したEvernoteと同じぐらい、Google Waveが重要な技術になると感じたのは本当だ。当初、&lt;a href="http://wave.google.com/"&gt;Google Wave&lt;/a&gt;は自分にとって、&lt;a href="http://code.google.com/intl/ja/appengine/"&gt;Google App Engine (GAE)&lt;/a&gt;などでプログラミングできる単なるおもちゃだった。しかしながら、今になってやっと、オンラインコラボレーションプラットフォームであるGoogle Waveの潜在能力の高さを知ることになった。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;大量のメールに埋もれる情報と予定の立たないミーティング&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

最近、仕事上のメールのやりとりが頻繁ですぐに流されてしまうことが多く、大事な知らせを失念してしまったり、大量のメールから目的の情報を探し出さなくてはならなくなったりと、メールの扱いに手を焼いていた。さらに、直接顔を合わせるミーティングにおいても、この時期は皆忙しいようで、集まりたいときにすぐに集まれない状況が続き、何かと仕事が滞りがちになっていた。&lt;br /&gt;&lt;br /&gt;

そこで、昨年にアカウントを取得していたGoogle Waveに目を付けたのだ。今までGoogle Waveを仕事で利用しなかったのは、もともと&lt;a href="http://handasse.blogspot.com/2009/11/google-wave.html"&gt;GAEと連動させてWaveボットなどを開発する&lt;/a&gt;のが目的であり、コラボレーションツールとしてはあまり興味を持っていなかったからだ。それに、知人や同僚にもアカウントを持っている人がいなかった。&lt;br /&gt;&lt;br /&gt;

しかし、メールでのやりとりがひどく非効率に感じられ、必要なときにミーティングもできないというこの状況を打破すべく、Google Waveのアカウントを職場で配ってみることにした。そこでまずは、IT関連の最新技術に明るい同僚に利用してもらい、しばらく一緒に使ってみたのだが、特に問題となるような点もなく、使い勝手もそれほど悪くはなかったので、他のメンバーにもアカウントを配ることにした。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;/span&gt;
&lt;div id="fullpost"&gt;
&lt;strong&gt;本当に新しいことは理解され難い&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

Google Waveアカウントを配ったメンバーに早速使ってみてもらったところ、おおむね好評を得られて…というほど甘くはなく、やはり第一印象は「難しい」、「使いづらい」、「メールでいいんじゃないの」という意見だった。誰もGoogle Waveの名前すら聞いたことがないというのだからそれも仕方が無いのだろう。これらの意見については、&lt;a href="http://completewaveguide.com/guide/Google_Wave_コンプリートガイド"&gt;Google Waveコンプリートガイド&lt;/a&gt;による&lt;a href="http://completewaveguide.com/guide/Google_Wave_の紹介#Wave_.E3.81.AE.E6.AC.A0.E7.82.B9.EF.BC.9A_.E3.83.A6.E3.83.BC.E3.82.B6.E3.83.BC.E3.82.92.E6.B7.B7.E4.B9.B1.E3.81.95.E3.81.9B.E3.82.8B.E6.9C.80.E5.88.9D.E3.81.AE.E4.BD.BF.E3.81.84.E5.BF.83.E5.9C.B0"&gt;Google Waveの紹介&lt;/a&gt;でも全く同じように述べられている。&lt;br /&gt;&lt;br /&gt;

&lt;blockquote&gt;“Google Wave の最大の欠点は、新しいユーザーが試しに使ってみた場合にあまりに理解しづらくて困惑してしまうことにあるといえます。&lt;a href="http://easiertounderstandthanwave.com/"&gt;EasierToUnderstandThanWave.com&lt;/a&gt;のようなパロディーサイトでは、炭素年代測定法や新古典派経済学、ポリモーダルな半音階理論といった頭脳を要求する話題でさえ、Wave に比べれば理解しやすいとされています。この冗談が真に迫っているのは、初めて Wave に触れたときにほとんどの人が困惑を経験するといえるからです。あなたがはじめて友人や同僚からうけとる wave は得てして、「なんだこれは」や「これは変なツールだね」といったものになることでしょう。” (Google Waveコンプリートガイドより引用)&lt;/blockquote&gt;&lt;br /&gt;

そして、難しいと思わせる理由として以下の4つが挙げられている。&lt;br /&gt;&lt;br /&gt;

&lt;ul&gt;
&lt;li&gt;「ドキュメントが会話」という前例のない新しいパラダイム&lt;/li&gt;
&lt;li&gt;無秩序な木構造の会話、ノンリニアなメッセージスレッド&lt;/li&gt;
&lt;li&gt;ドキュメントのバージョン管理&lt;/li&gt;
&lt;li&gt;Waveはまだ未完成、足りない機能の存在&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;

確かにもっともな理由ではあるが、それでも使ってもらわないことには何も始まらないので、自分の関わっているプロジェクトのいくつかについてGoogle Waveを使ってもらうことにした。新しいパラダイムは常に受け入れられがたいものだ。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;利用して初めて分かること&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

まず最初にしたことは、メンバーと共同で進めている自分の仕事の連絡をWaveで管理することだった。&lt;br /&gt;&lt;br /&gt;

今までは自分の担当分の結果が出たら、その報告と結果データの場所(通常はコンピュータ上のディレクトリ)などをメールで知らせていたのだが、そのやりとりが頻繁になると以前の報告やデータを把握しづらくなってくるし、ある仕事の成果を引き継いで別の仕事を行う場合、その対応付けも大変だった。また、別のメンバーが新たに参加する場合、これまでの進捗を説明するのも一苦労だ。&lt;br /&gt;&lt;br /&gt;

しかし、Waveを利用することによって、それぞれのメンバーの進捗状況は一目でわかるし、仕事のデータも簡単に取り出せる。画像やPDFなどのファイルもドラッグ&amp;ドロップで簡単に共有できる。さらに、ToDoリストとして使うことで、既にメンバーの誰かが完了していた仕事を別のメンバーが繰り返したり、既にあるユーティリティスクリプトを作成してしまうことがなくなった。車輪の再発明の防止だ。加えて、他のメンバーの状況と照らし合わせて、ここまでは急ぎで行わなければならないとか、これはまだ急がなくても良いだろうとかの判断が、簡単にできるようになった。&lt;br /&gt;&lt;br /&gt;

次に、ある商品の名称を決める打ち合わせを例に挙げてみよう。&lt;br /&gt;&lt;br /&gt;

まずは、Waveを使わない通常のミーティングの場合、メンバー全員を同じ時間に拘束する必要があり、さらに、その時間内で名称候補を考えつかなくてはならない。しかしそれでは考えつくすべての候補が出る前に時間切れとなってしまうかもしれない。では、時間を決めずに各自に考えてもらってそれを持ち寄るのではどうだろうか。これだと、各個人がバラバラに考えているので、別メンバーのアイデアから触発されることはない。実際に自分の場合、ミーティングやメールでアイデアを募ってみたが、ほとんど反応はなかった。ミーティングでは時間が短すぎたし、メールでは良いアイデアを思いつかないまま別の仕事に追いやられてしまうわけだ。&lt;br /&gt;&lt;br /&gt;

ではWaveを使った場合はどうだろうか。まずはいくつのかの候補の議論がなされ、それに触発されて他のメンバーからも新たな候補が挙がるようになった。それによりさらにアイデアが浮かんでくる。まさにブレインストーミングだ。しかも、Waveであれば、全員が同じ情報を共有し漏れが無く、時間にも拘束されない。進行のまとめもWave上で編集していけばよいし、何かを決めるときには投票などのガジェットも使える。実際に上記で挙がった候補から最終的な名称を決めるために投票ガジェットは大いに役立った。&lt;br /&gt;&lt;br /&gt;

どれだけWaveが有用に使えるか、使用する前にすべてを見積もっていた訳ではない。車輪の再発明の防止やアイデアの触発などは、実際に導入して初めて実感できたことだった。頭では分かったつもりでも使ってみるまでは本当に分かったことにはならない。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;TwitterとEvernoteで相乗効果&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;iframe src="http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=FFFFFF&amp;IS2=1&amp;npa=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=footawwwservi-22&amp;o=9&amp;p=8&amp;l=as1&amp;m=amazon&amp;f=ifr&amp;asins=4839934444" style="width:120px;height:240px;float:right;margin:0 10px 10px 0;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"&gt;&lt;/iframe&gt;
「&lt;a href="http://www.amazon.co.jp/gp/product/4839934444?ie=UTF8&amp;tag=footawwwservi-22&amp;linkCode=as2&amp;camp=247&amp;creative=1211&amp;creativeASIN=4839934444"&gt;iPhoneとツイッターで会社は儲かる&lt;/a&gt;&lt;img src="http://www.assoc-amazon.jp/e/ir?t=footawwwservi-22&amp;l=as2&amp;o=9&amp;a=4839934444" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;」(*1)の著者である&lt;a href="http://www.ecstudio.jp/"&gt;EC Studio&lt;/a&gt;の山本氏によると、会社のコミュニケーションは電話、チャット、メール、Twitterで成り立っているそうだ。電話はSkypeでもGoogle Voiceもいいし、Twitterの替りにGoogle Buzzを使ってもいいだろう。しかし、チャットとメールはGoogle Waveに置き換えられていくのではないかと感じている。もちろん、すべてのメールとチャットが置き換わるとは思っていないが、多くの部分でWaveが使われるのではないだろうか。&lt;br /&gt;&lt;br /&gt;

そして、ここで重要になってくるのが、WaveとTwitterとの共存だ。Twitterは今までにないゆるいつながりのコミュニケーションツールとしての立場を確立してる。実際に自分も使っているが、コミュニケーションツールの他に情報収集の手段としてとても有用だ。しかし、Twitterの情報はノイズが多く発散しやすい。最新の情報を肌で感じることができても、それを保存して加工する目的には向いていない。しかし、Waveを使えばそれを補うことができる。例えば、重要だと思われるTwitterの発言があったらそのポインタだけ示しておけば、その情報を共有できる。そして、それが古くなったりただのノイズだと判断したのならそれを捨てればいいだけだ。編集可能なWaveでは、チャットやメールのスパムのように邪魔になったりしない。リアルタイムの今の情報を共有できるのだ。因みに、&lt;a href="http://daggle.com/add-twitter-google-wave-1424"&gt;Wave上でTwitterを動作させるボット&lt;/a&gt;もあるし、&lt;a href="http://wave-samples-gallery.appspot.com/about_app?app_id=107014"&gt;特定のキーワードでつぶやきを表示させるガジェット&lt;/a&gt;もある。&lt;br /&gt;&lt;br /&gt;

さらに、Google Waveは&lt;a href="http://www.evernote.com/about/intl/jp/"&gt;Evernote&lt;/a&gt;とも非常に相性が良い。というのもGoogle Wave自体が複数人で共有できるEvernoteのようなものだからだ。もともと個人のWebスクラップノートとして利用されているものだからEvernoteを複数人で共有して利用するのは難しいが、Waveを利用することでその弱点を克服できる。&lt;br /&gt;&lt;br /&gt;

自分の場合、仕事ごとにEvernoteのノートを作って管理している。そして、ある仕事が共同作業であった場合、Waveを作ってそのURLをEvernoteに貼り付けておく。個人的な細々したメモや形になっていないアイデアなどはEvernoteに書いておき、皆で共有する情報はWaveに書く。Waveには他のメンバーも書き込んだり、編集したりするので情報は常に遷移していくのだが、その遷移する情報のすべてを一つのURLだけで保存できる。そして、EvernoteからWaveの情報にアクセスするのはクリック一つだ。これは画期的だ。因みに、&lt;a href="http://blog.arpitnext.com/2009/11/google-wave-gadget-for-evernote-note-capture.html"&gt;Wave上でEvernoteを動作させるガジェット&lt;/a&gt;も存在する。&lt;br /&gt;&lt;br /&gt;

ここで示したように、Google WaveとTwitterとEvernoteを活用することで、とても大きな効果を期待できるだろう。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;使用するにあたって&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

Google Waveの導入について、セキュリティを心配する声を聞く。外部にデータを持つのは不安だという。この点についても問題ない。と言うのも、&lt;a href="http://www.waveprotocol.org/"&gt;Waveサーバプロトコル&lt;/a&gt;が公開されているからだ。自社サーバでメールサーバを立ち上げるように、Waveサーバを立ち上げることができる。現在はプレビュー版ということもありGoogle以外での実装は見かけないが、将来的には普通に立ち上げることができるだろう。&lt;br /&gt;&lt;br /&gt;

もっとも、自社でサーバを持つことと、セキュリティが確保できることとは同義ではない。セキュリティを保つためにはそれ相応の技術が必要だ。その点で自社でサーバを管理するよりも、Googleなどのような高い技術を持つ企業に任せた方が安全であるとする見方もある。もし、Gmailなどを仕事で使っているのであれば、Google Waveもそれと同程度にはセキュリティが確保されていると考えられる。&lt;br /&gt;&lt;br /&gt;

Waveを使用するにあたり、次に問題になるのは、各ユーザが感じる使い勝手だろう。いくら便利だからといって、使い勝手が悪ければ使われない。例えば、SkypeやGmailであればチャットやメールが届いたときにその旨が画面にポップアップして知らせてくれるユーティリティがある。Google WaveにもFirefoxやGoogle Chrome用にブラウザ上で知らせてくれるAdd-onがあるが、それだと常時ブラウザを立ち上げている必要がある。また、新しいWaveが来たときにメールで知らせてくれるオプションもあるが、メールの代わりにWaveを使うという目的からして本末転倒だ。そこで、&lt;a href="http://sourceforge.net/projects/wave-notify/"&gt;Google Wave Notifier&lt;/a&gt;の使用をお勧めしたい。これを使えば新しいWaveが届いたときにポップアップして通知してくれる。また、Mac OS XやiPhoneなら&lt;a href="http://www.getwaveboard.com/"&gt;Waveboard&lt;/a&gt;というWaveクライアントが利用でき、通知機能もあるようだ。&lt;br /&gt;&lt;br /&gt;

どちらにしろ現在はまだプレビュー段階であり、リリースまでにはもうしばらくかかるだろう。それまでにはもっと便利でより良い環境が揃ってくることになると思う。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;最後に&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

Google Waveはまだ正式にリリースされたわけではない。まだまだ発展途上のプレビュー版だ。機能も不完全であり、開発環境も安定していない。なので、今すぐにWaveアカウントを手に入れられなくても気にすることはないと思う。将来のGoogle Waveがどのように使えて、どのように便利なのかを知ってもらい、Google Waveが利用されることなく廃れてしまうことが私たちにとってどれだけ「損する」ことなのかを理解してもらうことが、これを記した理由だ。なので、今すぐ使い始めなければ「損する」というわけではない。しかし、それでも今すぐに使いたい方もいると思う。残念ながら自分の持っている招待枠は使ってしまったので、「&lt;a href="http://completewaveguide.com/guide/Wave_を始めよう#Wave.E3.83.97.E3.83.AC.E3.83.93.E3.83.A5.E3.83.BC.E7.89.88.E3.81.AE.E6.8B.9B.E5.BE.85.E3.82.92.E5.BE.97.E3.82.8B"&gt;Waveプレビュー版の招待を得る&lt;/a&gt;」などを参考にアカウントを手に入れて欲しい。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;参考サイト&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

Google Waveの最新情報や便利な使い方を知りたい場合には、以下のサイトが参考になる。&lt;br /&gt;&lt;br /&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://wave.google.com/"&gt;Google Wave公式サイト&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://completewaveguide.com/guide/Google_Wave_コンプリートガイド"&gt;Google Wave コンプリートガイド&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.lifehacker.jp/2010/02/100209googlewavegina.html"&gt;「GoogleWaveは実生活でどう使えるか？」Ginaオススメの事例10選&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://b.hatena.ne.jp/articles/201002/869"&gt;実は簡単？「Google Wave」を使った情報管理を試してみる&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.gakushuin.ac.jp/~881791/misc/wavememo.html"&gt;Google Wave についての簡潔なメモ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;

&lt;strong&gt;紹介ビデオ&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

Google Waveの使い方が簡潔にまとめられているビデオ(約5分半)。&lt;br /&gt;&lt;br /&gt;

&lt;object width="640" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/xBzuuWZPaXc&amp;hl=ja_JP&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/xBzuuWZPaXc&amp;hl=ja_JP&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;

(*1) 「&lt;a href="http://www.amazon.co.jp/gp/product/4839934444?ie=UTF8&amp;tag=footawwwservi-22&amp;linkCode=as2&amp;camp=247&amp;creative=1211&amp;creativeASIN=4839934444"&gt;iPhoneとツイッターで会社は儲かる&lt;/a&gt;&lt;img src="http://www.assoc-amazon.jp/e/ir?t=footawwwservi-22&amp;l=as2&amp;o=9&amp;a=4839934444" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;」&lt;br /&gt;&lt;br /&gt;

&lt;a href="http://blogclub.jp/"&gt;ブログクラブ&lt;/a&gt;より献本して頂いた。Twitterの利点がわかりやすくまとめられており、今回のテーマとも一部共通するので紹介させてもらった。Twitterの利点や利用方法を知りたい方は読んでおいて損はないと思う。個人的には最後の社員アンケートが面白かった。&lt;br /&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-8191870015975245003?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/8191870015975245003/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=8191870015975245003' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8191870015975245003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8191870015975245003'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/03/google-wave.html' title='Google Waveを使わないと人生損する'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_BwIshVLoLIU/S56L12vzSSI/AAAAAAAAAQI/t0BWSnVIhQE/s72-c/wavelogo.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-5986743485441442758</id><published>2010-03-09T04:38:00.006+09:00</published><updated>2010-06-14T01:33:03.626+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='良いもの。'/><category scheme='http://www.blogger.com/atom/ns#' term='教育'/><category scheme='http://www.blogger.com/atom/ns#' term='Scratch'/><title type='text'>Scratchでプログラミングする子供たち</title><content type='html'>&lt;iframe src="http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=FFFFFF&amp;IS2=1&amp;npa=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=footawwwservi-22&amp;o=9&amp;p=8&amp;l=as1&amp;m=amazon&amp;f=ifr&amp;asins=4877832173" style="width:120px;height:240px;float:left;margin:0 10px 10px 0;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"&gt;&lt;/iframe&gt;
小学2年生の娘に&lt;a href="http://www.amazon.co.jp/gp/product/4877832173?ie=UTF8&amp;tag=footawwwservi-22&amp;linkCode=as2&amp;camp=247&amp;creative=1211&amp;creativeASIN=4877832173"&gt;スクラッチアイデアブック&lt;/a&gt;&lt;img src="http://www.assoc-amazon.jp/e/ir?t=footawwwservi-22&amp;l=as2&amp;o=9&amp;a=4877832173" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;を渡したら、あっという間に読み終え、&lt;a href="http://scratch.mit.edu/"&gt;Scratch&lt;/a&gt;を使って自由気ままにいろいろなプログラムを作り始めた。書籍を参考にシューティングゲームを作ったり(砲台は戦闘機の替りに何故か女の子)、耳コピの「かえるのうた」を音の制御で鳴らして口パクで歌う女の子たちのアニメーションを作ったり(表情や髪型、足の微妙な動きなど、芸が細かい)、好きなように想像して、好きなように作っていた。保育園に通う息子は、書籍は読めないけど、Scratchで絵を描いたり、プログラムの部品をいじることはできるので、やはり多様な想像力を働かせて、大人では考えつかないようなスプライトを作り、回転させたり、大きくしたり、音を鳴らせたりして、楽しんでいた。&lt;br /&gt;&lt;br /&gt;

これはたった一日の出来事だ。子供の吸収力は凄いと感心させられる。以前、「&lt;a href="http://handasse.blogspot.com/2009/08/scratch.html"&gt;Scratchで素数を求めてみた&lt;/a&gt;」というブログ記事で、Scratchによるプログラミングが子供の頃に遊んだレゴブロックみたいで楽しいと述べたのだが、その楽しさを表現する能力についてはやはり現役の子供には敵わなかったようだ。それにしても、子供たちが描いた絵が思うさまに動いているのを見ると、自分が子供の頃にもScratchがあったらなぁ、とつくづく思ってしまう。当時、自分で絵を描いたり、ブロックを組み立てたりすることだけでも楽しかったのに、それを好きなように動かせることができたのなら本当にワクワク・ドキドキしたことだったろう。&lt;br /&gt;&lt;br /&gt;

今回一つ思ったことは、子供たちには最小限のことだけを教えておいて、後は子供たちだけで何とかさせるというスタンスがとても良さそうであるということだ。疑問に思ったことは試行錯誤で解決しようとするし、それでも分からなければ本で調べる。ネットで調べてもいいのだけど、Scratchは日本語のまとまった資料が少ないのでそれが難しい。今後、多くの子供たちにScratchに触れてもらい、たくさんの人たちにScratchを知ってもらいたい。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
以下に、参考サイトを挙げておく。&lt;br /&gt;&lt;br /&gt;

&lt;a href="http://scratch.mit.edu/"&gt;Scratch公式サイト - 想像・プログラム・共有&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.atmarkit.co.jp/news/200801/17/mit.html"&gt;子どもたちがOSS活動、プログラミング言語「Scratch」が開く未来&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.atmarkit.co.jp/fwcr/design/benkyo/program01/01.html"&gt;非プログラマのためのプログラミング講座&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://scratch-ja.org/"&gt;こどもプログラミングサークル‘スクラッチ’&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://cheebow.info/chemt/archives/2009/08/post_334.html"&gt;小学生にプログラミングの楽しさを伝えてみた&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;

最後に、娘の作ったScratchプログラムの一つを載せておく。スペースキーを押せば始まるが、音が出るので注意して欲しい。&lt;br /&gt;&lt;br /&gt;

&lt;applet id='ProjectApplet' style='display:block' code='ScratchApplet' codebase='http://scratch.mit.edu/static/misc' archive='ScratchApplet.jar' height='387' width='482'&gt;&lt;param name='project' value='../../static/projects/logos/921149.sb'&gt;&lt;/applet&gt; &lt;a href='http://scratch.mit.edu/projects/logos/921149'&gt;このプロジェクトについてもっと知る&lt;/a&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-5986743485441442758?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/5986743485441442758/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=5986743485441442758' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/5986743485441442758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/5986743485441442758'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/03/scratch.html' title='Scratchでプログラミングする子供たち'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-8358719689644516564</id><published>2010-03-07T01:49:00.007+09:00</published><updated>2010-10-31T20:42:32.632+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WWW'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Google Wave Robots API v2でボットを書き直してみた</title><content type='html'>先日、&lt;a href="http://code.google.com/intl/ja/apis/wave/guide.html"&gt;Google Wave&lt;/a&gt;の&lt;a href="http://wave-robot-python-client.googlecode.com/svn/trunk/pydocs/index.html"&gt;Robots APIバージョン2&lt;/a&gt;がリリースされた。更新内容の概要は「&lt;a href="http://d.hatena.ne.jp/technohippy/20100306#1267850372"&gt;Google Wave Robot API v2のリリースエントリを翻訳しました&lt;/a&gt;」が分かりやすい。そこで、以前に作成した&lt;a href="http://handasse.blogspot.com/2009/11/google-wave.html"&gt;「てめーはおれを怒らせた」ボット&lt;/a&gt;をバージョン2のAPIで書き直してみることにした。しかし、すぐに修正できるだろうとナメていたら意外と時間が掛かってしまった。&lt;br /&gt;&lt;br /&gt;

初っ端でいきなり動かない。ほとんど&lt;a href="http://code.google.com/intl/ja/apis/wave/extensions/robots/python-tutorial.html"&gt;チュートリアル&lt;/a&gt;と同じ程度に削っても動かない。エラーを頼りにAPIのソースコードなどを読んだりしているうちに、自分のプログラムの問題ではないんじゃないかと思い、動作確認していた環境をSandboxからPreviewに変更してみたら、問題なく動くようになった。ここでかなりの時間ロス。さらに、インターフェースも大きく変わっていて、新しいAPIでどのように実装するのか調べるだけでも結構時間を取られた。&lt;br /&gt;&lt;br /&gt;

書き直したソースコードは以下の通り。本当にこれで良いのか自信はないが、ちゃんと動作するようだ。因みに、ボットのアカウントは&lt;strong&gt;billowlet@appspot.com&lt;/strong&gt;だ。Google WaveのContactsパネルから登録すれば使うことができる。&lt;br /&gt;&lt;br /&gt;

慣れていないので手こずってしまったが、以前のバージョンに比べて、できることは増えているし、APIの構成も分かりやすくなっている。それに、ちゃんと&lt;a href="http://wave-robot-python-client.googlecode.com/svn/trunk/pydocs/index.html"&gt;Google Wave Robot Python API v2&lt;/a&gt;のドキュメントが整備されているのが嬉しいところ。何か面白いボットを作ってみたいな。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;strong&gt;追記&lt;/strong&gt;(2010/3/15):&lt;br /&gt;&lt;br /&gt;

Sandboxでも正常に動作するようになったみたい。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;billowlet.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""billowlet.py  by nox, 2010.3.6"""

import re, urllib, urllib2, random
from google.appengine.ext import db
from waveapi import events
from waveapi import robot
from waveapi import blip
from waveapi import appengine_robot_runner
from waveapi import simplejson

HIRAGANA = u"あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをん"
KATAKANA = u"アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン"

class UserData(db.Model):
    user = db.StringProperty(multiline=False)
    status = db.StringProperty(multiline=False)

def OnWaveletParticipantsChanged(event, wavelet):
    participants = event.participants_added
    for participant in participants:
        if participant != "billowlet@appspot.com":
            wavelet.reply(u"%s、おれを怒らせるなよ。" % participant.split("@")[0])

def OnWaveletSelfAdded(event, wavelet):
    wavelet.reply(u"おれはbillowlet。よろしくな。")

def Translate(text, from_lang, to_lang):
    url = "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&amp;amp;q=%s&amp;amp;langpair=%s%%7C%s" % (urllib.quote(text.encode("utf-8")), from_lang, to_lang)
    data = simplejson.loads(urllib2.urlopen(url).read())
    return data["responseData"]["translatedText"]

def BeCat(text):
    text = re.sub(u"。", u"にゃ。", text)
    text = re.sub(u"？|\?", u"にゃぁ？", text)
    text = re.sub(u"！|\!", u"にゃっ！", text)
    text = re.sub(u"な", u"にゃ", text)
    if text[-1] not in (u"。", u"？", u"?", u"！", u"!"):
        text += u"にゃ"
    return text

def DotMarks(text):
    from_kana = u"かきくけこさしすせそたちつてとはひふへほカキクケコサシスセソタチツテトハヒフヘホ"
    to_kana   = u"がぎぐげござじずぜぞだぢづでどばびぶべぼガギグゲゴザジズゼゾダヂヅデドバビブベボ"
    for f, t in zip(from_kana, to_kana):
        text = text.replace(f, t)
    return text

def Laugh(text, ch):
    for i in range(len(text), 0, -1):
        text = text[:i] + ch + text[i:]
    return text.replace(u"。", u"").replace(u"、", u"") + ch * random.randint(3, 10)

def Kana(idx):
    return HIRAGANA[idx] + u"|" + KATAKANA[idx]

def OnBlipSubmitted(event, wavelet):
    blp = event.blip
    user = blp.creator
    opq = wavelet.get_operation_queue()

    data = UserData.all().filter("user =", user).get()
    if data:
        status = data.status
    else:
        status = None

    text = blp.text.strip()
    if status and re.match(u"ごめんなさい|ゆるして", text):
        json = opq.blip_create_child(blp.wave_id, blp.wavelet_id, blp.blip_id)
        child_blp = blip.Blip(json, wavelet.blips, opq)
        child_blp.append(u"ゆるしてやる。やれやれだぜ。".encode("utf-8"))
        if data: data.delete()

    st = (u"ネコになれ。",            # 0
          u"英国紳士になれ。",        # 1
          u"台湾に住め。",            # 2
          u"インチキ日本人になれ。",  # 3
          u"大事なことは2回言え。",   # 4
          u"笑え。",                  # 5
          u"濁れ。",                  # 6
          u"逆さまになれ。",          # 7
          u"どもれ。",                # 8
          u"うざくなれ。")            # 9
    fn = {st[0]: BeCat,
          st[1]: lambda (text): Translate(text, "ja", "en"),
          st[2]: lambda (text): Translate(text, "ja", "zh-TW"),
          st[3]: lambda (text): Translate(Translate(text, "ja", "en"), "en", "ja"),
          st[4]: lambda (text): text + text + u"\n大事なことなので2回言いました。",
          st[5]: lambda (text): Laugh(text, u"ｗ"),
          st[6]: DotMarks,
          st[7]: lambda (text): text[::-1],
          st[8]: lambda (text): (text[0] + u"、") * 3 + text,
          st[9]: lambda (text): Laugh(text, u"っ")}

    angry = range(len(HIRAGANA))
    random.shuffle(angry)
    if status:
        text = fn[status](text)
        blp.range(1, len(blp.text)).replace(text.encode("utf-8"))
    else:
        userdata = UserData()
        for i in range(len(st)):
            if re.search(Kana(angry[i]), text):
                status = st[i]
                break
        if status:
            json = opq.blip_create_child(blp.wave_id, blp.wavelet_id, blp.blip_id)
            child_blp = blip.Blip(json, wavelet.blips, opq)
            text = u"てめーはおれを怒らせた。%s" % status
            child_blp.append(text.encode("utf-8"))
            userdata.user = user
            userdata.status = status
            userdata.put()

def main():
    myRobot = robot.Robot("billowlet",
                          image_url="http://billowlet.appspot.com/assets/icon.png",
                          profile_url="http://billowlet.appspot.com/")
    myRobot.register_handler(events.WaveletParticipantsChanged, OnWaveletParticipantsChanged)
    myRobot.register_handler(events.BlipSubmitted, OnBlipSubmitted)
    myRobot.register_handler(events.WaveletSelfAdded, OnWaveletSelfAdded)
    appengine_robot_runner.run(myRobot)

if __name__ == "__main__": main()&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-8358719689644516564?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/8358719689644516564/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=8358719689644516564' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8358719689644516564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/8358719689644516564'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/03/google-wave-robots-api-v2.html' title='Google Wave Robots API v2でボットを書き直してみた'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-2555722194492380022</id><published>2010-03-01T01:30:00.007+09:00</published><updated>2010-03-02T16:13:24.152+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>AndroidからPythonでTwitterに投稿する</title><content type='html'>&lt;a href="http://handasse.blogspot.com/2010/02/androidpythonluajavascript.html"&gt;AndroidにASE (Android Scripting Environment)を入れて&lt;/a&gt;から楽しくてちょくちょくいじっているのだけど、Pythonに初めから&lt;a href="http://code.google.com/p/python-twitter/"&gt;twitterモジュール&lt;/a&gt;が入っていることに気が付いたので使ってみた。&lt;br /&gt;&lt;br /&gt;

普段はAndroid端末(HT-03A)のTwitterクライアントとして&lt;a href="http://twidroid.com/"&gt;Twidroid&lt;/a&gt;を使っているのだけど、最初の立ち上げでタイムラインを取得しに行くのでちょっとだけ待たされる。あまり気にはならないけど、つぶやきをサクっと投稿したいだけならタイムライン取得は無駄なので、投稿専用のスクリプトを組んでみることにした。以下がそのコードだ。起動するとダイアログが出るのでつぶやきを書いて送信するだけだ。送信が完了したら画面に投稿したつぶやきが表示されて終了する。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;# -*- coding: utf-8 -*-

import android, twitter

droid = android.Android()
t = droid.getInput(u"Tweet", u"いまどうしてる?")["result"].strip()
if t:
  api = twitter.Api("&lt;span style="color:red;"&gt;username&lt;/span&gt;", "&lt;span style="color:red;"&gt;password&lt;/span&gt;")
  api.PostUpdate(t)
  droid.makeToast(t)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

これはすべてAndroid上でコーディングしたものだ。短いし、せいぜい数分もあれば書ける。書き終わったらそれをそのままホーム画面にショートカットアイコンとして登録すればいい。他にも、&lt;a href="http://handasse.blogspot.com/2009/05/python-urlbitlyapi.html"&gt;bit.lyのAPIを使って短縮URLを実装&lt;/a&gt;したり、サービスとして起動させ、特定のキーワードに引っかかったときにAndroidに通知させたり、メールを送信させたりすることも簡単だろう。やはり、スクリプトが使えるといろいろできて面白いな。&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-2555722194492380022?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/2555722194492380022/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=2555722194492380022' title='4 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/2555722194492380022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/2555722194492380022'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/03/androidpythontwitter.html' title='AndroidからPythonでTwitterに投稿する'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-356936511928937769</id><published>2010-02-25T01:45:00.011+09:00</published><updated>2010-06-27T17:35:13.894+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Android上でPython、Lua、JavaScriptなどを実行するスクリプティング環境が凄い</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BwIshVLoLIU/S4Vi4qZmv2I/AAAAAAAAAPk/ZEaNqvANdl4/s1600-h/android_logo.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 150px;" src="http://4.bp.blogspot.com/_BwIshVLoLIU/S4Vi4qZmv2I/AAAAAAAAAPk/ZEaNqvANdl4/s200/android_logo.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5441864450340994914" /&gt;&lt;/a&gt;
&lt;a href="http://code.google.com/p/android-scripting/"&gt;ASE (Android Scripting Environment)&lt;/a&gt;を使って簡単にPython, Perl, JRuby, Lua, BeanShell, JavaScript (Rhino), それにシェル(将来的にはさらにたくさんのスクリプト言語)を&lt;a href="http://code.google.com/p/android-scripting/wiki/AndroidFacadeAPI"&gt;Android上で実行できる&lt;/a&gt;のはご存じだろうか。ASEのインストールからスクリプトの作成、実行まで、すべてAndroid単体でできる。もちろん、PC上でコーディングしたい場合は、USBで繋げてPC上のスクリプトをAndroid端末上で実行することもできるし、PC上のコードをAndroid端末にコピーすることもコマンド一発だ。さらに、各種センサー、位置情報、SMS、テキストの読み上げなどもスクリプト上で操作できるというのだからこれを使わない手はない。&lt;br /&gt;&lt;br /&gt;
 
そこで、試しにPythonスクリプトを書いてみた。Android端末のGPS機能で&lt;a href="http://code.google.com/p/android-scripting/wiki/PythonAndroidAPI#Finding_Your_Location"&gt;緯度経度を取得&lt;/a&gt;して、&lt;a href="http://creco.net/2009/03/07/implement_google_reverse_geocoding_api/"&gt;逆ジオコーディング&lt;/a&gt;で現在の住所を表示するスクリプトだ。Google Maps APIのURLなどを含めた雛形だけPCからAndroid端末(&lt;a href="http://www.nttdocomo.co.jp/product/foma/pro/ht03a/"&gt;HT-03A&lt;/a&gt;)にコピーして、通勤時の電車の中で書いたのが以下のコードだ。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;# -*- coding: utf-8 -*-
 
import android, time, json, urllib
 
url = "http://maps.google.com/maps/geo?ll=%.16f,%.16f&amp;amp;sensor=false&amp;amp;output=json&amp;amp;hl=ja&amp;amp;oe=UTF8&amp;amp;key=&lt;span style="color:red;"&gt;Google_Maps_API_Key&lt;/span&gt;"
 
droid = android.Android()
droid.startLocating()
for i in range(10):
  location = droid.readLocation()
  if location["result"]: break
  time.sleep(1)
lat, lng = location["result"]["network"]["latitude"], location["result"]["network"]["longitude"]
geo_data = json.loads(urllib.urlopen(url % (lat, lng)).read())["Placemark"]
geo_data = sorted(geo_data, key=lambda x: -x["AddressDetails"]["Accuracy"])
address = geo_data[0]["address"]
droid.makeToast(address)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
シェル環境が使えるので、print文を使ったデバッグも簡単だった。苦労すると思われたソフトキーボードもQWERTY配列と予測変換が使えるので思ったよりも楽だったし、トラックボールでのカーソル移動、コピー、カット、ペーストもある。できればキーボード付き端末が欲しいところだが、これからAndroid端末はたくさん出てくると予想されるので、そちらに期待している。早いところ、&lt;a href="http://www.motorola.com/Consumers/US-EN/Consumer-Product-and-Services/Mobile-Phones/Motorola-DROID-US-EN"&gt;Droid&lt;/a&gt;や&lt;a href="http://www.sonyericsson.com/cws/products/mobilephones/overview/xperiax10minipro"&gt;Xperia X10 mini pro&lt;/a&gt;が日本で出て欲しいところだ。因みに、作成したスクリプトはASE上でも実行できるし、ホーム画面にショートカットアイコンを作って直接実行もできる。&lt;br /&gt;&lt;br /&gt;
 
本格的なAndroidアプリケーションを作成するにはAndroid SDKとJavaになるのだろうが、個人的に使う小さなスクリプトならASEの方が端末上でサクッと書けるし、比較的大きなコードでもPC上で作成し、PCに端末を接続してデバッグ、完成したら端末にコピーすればいい。それに、様々なスクリプト言語が使えるので、自分が使いたいスクリプトを選べるのも魅力だ。対話環境も利用できるのでスクリプト言語の勉強のために使うのもありだと思う。&lt;br /&gt;&lt;br /&gt;
 
以下に、ASEの導入方法と使い方を書いておく。&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;準備&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
ASEはGoogleの公式プロジェクトではあるが、ちゃんと認証を受けたソフトウェアではないので(将来的にはAndroid Marketで配布されるらしい)、Androidの設定で[アプリケーション]-[提供元不明のアプリ]のチェックをオンにしておく必要がある(インストール後にはオフに戻しておくこと)。次に、Android端末で&lt;a href="http://code.google.com/p/android-scripting/"&gt;android-scripting&lt;/a&gt;のFeatured downloadsにある最新版をダウンロードして、そのままインストールする。&lt;br /&gt;&lt;br /&gt;
 
ASEを起動し、メニューを開くと[Interpreters]の項目があるので、それを選択する。初期状態ではShellだけが選択できるようになっているが、他のスクリプト言語を入れたい場合は、メニューを開いて[Add]を選択する。ダイアログが開き、複数のスクリプト言語が表示されるので、使いたい言語を選択する。選択すると自動的にダウンロードされインストールされる。&lt;br /&gt;&lt;br /&gt;
 
PCとAndroid端末を連動させたい場合は、PCにAndroid SDKをインストールする必要がある。以前に&lt;a href="http://handasse.blogspot.com/2009/10/android.html"&gt;導入方法を書いた&lt;/a&gt;ので、そちらを参考にして欲しい。&lt;br /&gt;&lt;br /&gt;
 
さて、これでASEを使用する準備が整った。&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;使い方&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
まず、端末上でのコーディング方法を説明する。ASEを起動してメニューを開き、[Add]を選択するとダイアログが開くので、作成したいスクリプト言語を選ぶ。ソースコードの雛形が出るので、それにコードを書いていく。書き上がったら適当なファイル名で保存する。実行するにはASEを起動した最初の画面で表示されるスクリプトファイルを選択してもいいし、スクリプトのショートカットを作っておいてホーム画面から起動してもいい。因みにショートカットを作成するには、ASE起動時に表示されるスクリプトファイルを長押しして[Add Shortcut]を選べばいい。&lt;br /&gt;&lt;br /&gt;
 
次に、PCとAndroid端末をUSBで接続してAndroid上で実行する方法を示す。これについては、&lt;a href="http://d.hatena.ne.jp/makotof2005/20090720/1248112314"&gt;Android ASE (Android Scripting Environment)入門&lt;/a&gt;を参考にさせてもらった。&lt;br /&gt;&lt;br /&gt;

最初にPCとAndroid端末をUSBで接続する。このとき端末側の設定で[アプリケーション]-[開発]-[USBデバッグ]をオンにしておく。&lt;br /&gt;&lt;br /&gt;

ASEのインタプリタ起動時に表示されるAP_PORTが55063で、PC上のポートを4321(任意の未使用ポートを選べる)とする場合は、Windowsでは以下のようにコマンドプロンプト(シェル)上で設定する。因みに%はプロンプトを示している。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;% adb forward tcp:&lt;span style="color:red;"&gt;4321&lt;/span&gt; tcp:&lt;span style="color:red;"&gt;55063&lt;/span&gt;
&lt;span style="color:gray;"&gt;* daemon not running. starting it now *
* daemon started successfully *&lt;/span&gt;
% set AP_PORT=&lt;span style="color:red;"&gt;4321&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
最後にデーモンを停止してポートを閉じる場合は、以下のコマンドを使う。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;% adb kill-server&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
また、PCにもAndroidで使用するスクリプト環境が入っている必要がある。例えばPythonを使う場合はあらかじめPCにもPyhton 2.6をインストールしておき、さらに&lt;a href="http://code.google.com/p/android-scripting/source/browse/python/ase/android.py"&gt;android.py&lt;/a&gt;を、PythonをインストールしたディレクトリのLib/site-packages/にコピーしておく。これでPC上で実行したスクリプトがAndroid端末で動作する。&lt;br /&gt;&lt;br /&gt;
 
最後に、PC上で作成したスクリプトをAndroid端末にコピーする方法だが以下のようにすればコピーできる。ただし、将来にわたってコピー先のディレクトリが不変である保証はない。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;% adb push 作成したスクリプトファイル /sdcard/ase/scripts/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
Android端末上のディレクトリを調べたい場合、&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;% adb shell&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
とすればリモートシェルが立ち上がるので、以下のように調べることができる。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;$ cd  /sdcard/ase/scripts
&lt;span style="color:gray;"&gt;cd  /sdcard/ase/scripts&lt;/span&gt;
$ ls
&lt;span style="color:gray;"&gt;ls
saychat.py
notify_weather.py
speak.py
weather.py
saytime.py
sayweather.py
test.py&lt;/span&gt;
$&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;注意事項&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
今回、HT-03Aで使用したのだが、入力方法がiWnn IMEやOpenWnnになっているとASEのインタプリタ上で文字を入力できなかった。入力するには入力方法をAndroidキーボードにする必要がある。ただし、スクリプトの作成・編集についてはどの入力方法でも問題はなかった。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;追記&lt;/strong&gt;(2010/4/5):&lt;br /&gt;&lt;br /&gt;

記事を書いた時点でのASEは&lt;a href="http://android-scripting.googlecode.com/files/ase_r16.apk"&gt;ase_r16.apk&lt;/a&gt;だったが、現在の最新版は&lt;a href="http://android-scripting.googlecode.com/files/ase_r20.apk"&gt;ase_r20.apk&lt;/a&gt;になっている。頻繁に更新されるのでダウンロードする際は最新版の確認をして欲しい。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;追記&lt;/strong&gt;(2010/6/27):&lt;br /&gt;&lt;br /&gt;

ASE r22からreadLocation()のデータ構造が変更されたようなので、それに合わせて上述のコードを修正した。&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-356936511928937769?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/356936511928937769/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=356936511928937769' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/356936511928937769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/356936511928937769'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/02/androidpythonluajavascript.html' title='Android上でPython、Lua、JavaScriptなどを実行するスクリプティング環境が凄い'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_BwIshVLoLIU/S4Vi4qZmv2I/AAAAAAAAAPk/ZEaNqvANdl4/s72-c/android_logo.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-4945285785173960560</id><published>2010-02-15T21:32:00.006+09:00</published><updated>2010-04-08T14:18:22.675+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='書籍'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><title type='text'>「ハッカーと画家」を読んで</title><content type='html'>&lt;iframe src="http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=FFFFFF&amp;IS2=1&amp;npa=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=footawwwservi-22&amp;o=9&amp;p=8&amp;l=as1&amp;m=amazon&amp;f=ifr&amp;asins=4274065979" style="width:120px;height:240px; float:left;margin:0 10px 10px 0;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"&gt;&lt;/iframe&gt;
&lt;a href="http://ja.wikipedia.org/wiki/ポール・グレアム"&gt;ポール・グレアム&lt;/a&gt;の「&lt;a href="http://www.amazon.co.jp/gp/product/4274065979?ie=UTF8&amp;tag=footawwwservi-22&amp;linkCode=as2&amp;camp=247&amp;creative=1211&amp;creativeASIN=4274065979"&gt;ハッカーと画家 コンピュータ時代の創造者たち&lt;/a&gt;&lt;img src="http://www.assoc-amazon.jp/e/ir?t=footawwwservi-22&amp;l=as2&amp;o=9&amp;a=4274065979" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;」を読んだ。もっと以前に読んでいてしかるべき本であったが、読みそびれていた。もっと技術的な話だと思っていたのだが、(プログラマであれば)気軽に読めるとても面白いエッセイ集だった。&lt;br /&gt;&lt;br /&gt;

個人的に興味深かったのは、第4章の「&lt;a href="http://www.paulgraham.com/gba.html"&gt;天邪鬼の価値&lt;/a&gt;」で、ジョブズとウォズニアックが錠開け装置をいじっている写真だ。リチャード・ファインマンがマンハッタン計画で重要書類の入っている金庫を破ることを楽しみにしていたり、グレアムが学生の頃のMITではピッキングが流行ったりしていたそうだが、それを読んで、自分が子供の頃のことを思い出した。古いダイアル式南京錠が家にあったのだが、それは錠が閉まっていて親に聞いても番号が分からないという。そこでどうやったら開けることが出来るかを考えるうちに面白くなり、次第に夢中になっていった。ダイアルは3桁の数字なので、10*10*10=1000通りを試したら必ず開けられる。しかし、そんな面倒なことはしたく無い。悪用されても困るので詳しくは書かないけど、しばらくいじっているうちにキーの動きに法則があることに気がついた。こうなれば簡単で、最大で10+10+10=30通りを試すだけでよくなる。これで開かない錠前を開けることができた。今で言えばセキュリティに欠陥があるということなのだろうが、困難だと思われることに挑戦し、それを克服することは、とても強い達成感となる。子供ながらにそう感じた。ファインマンもMITの学生もそう感じたのだろう。これがハックする理由の原点だろうか。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
第13章の「&lt;a href="http://practical-scheme.net/trans/icad-j.html"&gt;オタク野郎の復讐&lt;/a&gt;」を読めば分かるけど、ポール・グレアムは熱心なLispハッカーで、Lispを非常に高く評価している。確かに他の言語の意義についても説いてはいるけど、「他のプログラミング言語はとうとう(Lispの生まれた)1958年に追いつこうとしている」と書いていることからもLispが最も良い言語だと考えているのが分かる。確かにLispは良い言語だと思うけど、個人的には万人にLispを勧めることはできないなぁ。&lt;br /&gt;&lt;br /&gt;

自分の場合は、普段はC++とPythonを利用していて(多くの場合はこれで事足りる)、最近は並列化のためにTBBやCUDA、それとErlangを使ったりしている。もちろんAndroidで開発するときはJavaだし、グラフィカルなことをしたいときはProcessingやActionScriptを使うし、他にも様々な言語をいじったりするけども、結局、言語の選択はその環境と目的に強く依存されると思う。それに、複数の言語から選択できる方が一つの言語に固執するより健全じゃないかなぁ(一つの言語を極めてみるというのには異存はないけど)。複数の言語を覚えるのは大変そうに思えるかもしれないけど、最初に2～3の言語を習得すれば、それ以降はあまり苦労せずに覚えることができると思う。&lt;br /&gt;&lt;br /&gt;

ところでLispの開発環境についてだけど、WindowsでCommon Lispを使いたいときはEmacs Lispの代わりにCommon Lispライクなマクロ言語を搭載した&lt;a href="http://www.jsdlab.co.jp/~kamei/"&gt;xyzzy&lt;/a&gt;が手軽だ。Linuxなら&lt;a href="http://clisp.cons.org/"&gt;CLISP&lt;/a&gt;や&lt;a href="http://www.sbcl.org/"&gt;SBCL&lt;/a&gt;がよく使われている。&lt;br /&gt;&lt;br /&gt;

ポール・グレアムの最新のエッセイは&lt;a href="http://www.paulgraham.com/articles.html"&gt;Paul Graham - Essays&lt;/a&gt;から読むことができる。日本語訳は&lt;a href="http://practical-scheme.net/wiliki/wiliki.cgi?naoya_t:%E3%83%9D%E3%83%BC%E3%83%AB%E3%83%BB%E3%82%B0%E3%83%AC%E3%82%A2%E3%83%A0%E3%81%AE%E3%82%A8%E3%83%83%E3%82%BB%E3%82%A4%E3%81%A8%E5%92%8C%E8%A8%B3%E4%B8%80%E8%A6%A7"&gt;naoya_t:ポール・グレアムのエッセイと和訳一覧&lt;/a&gt;でまとめられているので、興味のある方は読んでみてはどうだろうか。あと、第13章「&lt;a href="http://practical-scheme.net/trans/icad-j.html"&gt;オタク野郎の復讐&lt;/a&gt;」で納得しかねたPythonユーザはPaul Prescodの「&lt;a href="http://practical-scheme.net/trans/IsPythonLisp-j.html"&gt;PythonとLispの関係について&lt;/a&gt;」(川合史朗訳)を読んでみることをお勧めしたい。&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-4945285785173960560?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/4945285785173960560/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=4945285785173960560' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4945285785173960560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4945285785173960560'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/02/blog-post.html' title='「ハッカーと画家」を読んで'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-6286610868603525047</id><published>2010-02-05T00:40:00.044+09:00</published><updated>2010-02-25T13:36:39.327+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><title type='text'>Erlang基礎文法最速マスター</title><content type='html'>先日、&lt;a href="http://handasse.blogspot.com/2010/02/lua.html"&gt;Lua基礎文法最速マスター&lt;/a&gt;を書いてみたが、予想以上に自分自身への学習効果が高かった。そこで、普段使っているPythonと同じぐらいに使いこなしたいと思っていたErlang(アーラン)の基礎文法最速マスターを書いてみることにした。&lt;br /&gt;&lt;br /&gt;

Erlangが関数型プログラミング言語であることもあり、書き下すのは思ったよりも大変だったが、学習効果はかなりあったと思う。言語を習得したいときはこのようなまとめを書いてみるのが良さそうだ。&lt;br /&gt;&lt;br /&gt;

ただ、手続き型プログラミング言語に比べて異なる部分があまりにも大きいので、すべてを説明することは難しく、これを読んだだけですぐに使えたりはしないかもしれない。また、間違っていたり、足りない部分などがあったら、教えて頂けるとありがたい。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;/span&gt;
&lt;div id="fullpost"&gt;&lt;br /&gt;
&lt;hr /&gt;
&lt;h3&gt;1. 基礎&lt;/h3&gt;

&lt;h4&gt;対話環境&lt;/h4&gt;

コマンドラインからerlを実行すると対話環境(シェル)になります。コマンドの最後にはピリオド(.)が必要です。help()で簡単な説明を読むことができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;$ erl
1&amp;gt; A = 10.     &lt;span style="color:gray;"&gt;% Aに10を束縛.&lt;/span&gt;
10
2&amp;gt; B = A * 5.  &lt;span style="color:gray;"&gt;% BにA * 5 (50)を束縛.&lt;/span&gt;
50
3&amp;gt; hello.      &lt;span style="color:gray;"&gt;% helloはアトム(定数のようなもの/後述).&lt;/span&gt;
hello
4&amp;gt; b().        &lt;span style="color:gray;"&gt;% 現在の束縛変数を表示.&lt;/span&gt;
A = 10
B = 50
ok
5&amp;gt; A = 20.     &lt;span style="color:gray;"&gt;% 変数は一度しか代入(束縛)できない.&lt;/span&gt;
** exception error: no match of right hand side value 20
6&amp;gt; f().        &lt;span style="color:gray;"&gt;% 束縛変数をすべてクリアする.&lt;/span&gt;
ok
7&amp;gt; A = 20.     &lt;span style="color:gray;"&gt;% 再度束縛できる.&lt;/span&gt;
20
8&amp;gt; q().        &lt;span style="color:gray;"&gt;% Erlangシェルを終了させる.&lt;/span&gt;
ok&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;画面出力&lt;/h4&gt;

io:formatで書式指定の出力を行うことができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;~p : 整形して出力.
~s : 文字列を出力.
~f : 浮動小数点数を出力.
~w : Erlangの標準構文として出力(アトムならそのまま、文字列ならリストとして出力など)
~n : 改行.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;io:format("Hello world~n").  &lt;span style="color:gray;"&gt;% "Hello world"&lt;/span&gt;

&lt;span style="color:gray;"&gt;%% "5    ,  1.235, 30, erlang"&lt;/span&gt;
io:format("~-5w, ~6.3f, ~p, ~s~n", [5, 1.23456, 30, "erlang"]).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;コメント&lt;/h4&gt;

%から行末までがコメントになります。ブロックコメントはありません。慣例として%%がよく使われます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;% コメント.
%% コメント.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;変数の代入&lt;/h4&gt;

Erlangの変数の先頭は必ずアルファベットの大文字かアンダーバー(_)になります。また、一度代入すると後から変更できません。代入した変数のことを束縛変数と呼びます。まだ代入されていない変数のことを未束縛変数と呼びます。変数は動的型付けなので宣言は必要ありません。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;A = 5.   &lt;span style="color:gray;"&gt;% ※1&lt;/span&gt;
B = "Hello world".
C = atom.
A = 10.  &lt;span style="color:gray;"&gt;% Aは束縛変数のためエラーになる.&lt;/span&gt;
A = 5.   &lt;span style="color:gray;"&gt;% ※2 Aは束縛変数だが、パターン照合が一致するのでエラーにならない.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

実のところ、=は代入演算子ではなく、パターン照合演算子になります。例えば、※1のA = 5のAは未束縛変数であり、どんな値にもパターンが一致するので5の照合が成功して、Aに5が束縛できるのです。※2のA = 5では、既にAは5に束縛されているので、数値の5か、5の値を持つ変数とだけ照合が成功します。&lt;br /&gt;&lt;br /&gt;

Erlangで重要な要素の一つにアトムがあります。アトムとは数値以外の不変値を表すのに使用します。C言語の列挙子などが近い存在です。アトムは通常アルファベットの小文字から始まります。変数に代入することができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;atom.
A = hello.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

また、先頭文字を大文字にしたり、空白や特殊記号を使用したい場合はシングルクォーテーション(')で囲むことでアトムとなります。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;'Atom'.
'This is an atom.'.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

ある項目を一つにまとめたい場合はタプルを使います。タプルは中括弧({})で括ることにより作れます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;A = {pen, book, desk}.
B = {b, {language, "erlang"}, 10}.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

タプルから値を取り出す場合、パターン照合を使います。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;C = A.           &lt;span style="color:gray;"&gt;% 未束縛変数Cに {pen, book, desk}が入る.&lt;/span&gt;
{b, D, 10} = B.  &lt;span style="color:gray;"&gt;% Bのタプルと未束縛変数Dを含む{b, D, 10}はパターンが一致するのでDに{language, "erlang"}が入る.&lt;/span&gt;
{b, E, 20} = B.  &lt;span style="color:gray;"&gt;% パターンが一致しないのでエラーになり、Eは未束縛変数のまま.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

いくつかのパターン照合の例を以下に挙げておきます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;{X, 123} = {10, 123}.        &lt;span style="color:gray;"&gt;% 成功. Xに10が入る.&lt;/span&gt;
{X, 12} = {10, 123}.         &lt;span style="color:gray;"&gt;% 失敗. 12と123が一致しない.&lt;/span&gt;
{A, B, A} = {5, 10, 5}.      &lt;span style="color:gray;"&gt;% 成功. Aに5、Bに10が入る.&lt;/span&gt;
{A, B, C} = {5, 20, 8}.      &lt;span style="color:gray;"&gt;% 失敗. Bには10が束縛されている.&lt;/span&gt;
{A, _, _} = {5, 20, 8}.      &lt;span style="color:gray;"&gt;% 成功. アンダーバー(_)はどの値とも一致する.&lt;/span&gt;
[H|T] = [1, 2, 3].           &lt;span style="color:gray;"&gt;% 成功. Hに1、Tに[2, 3]が入る.&lt;/span&gt;
[A, B|T] = [1, 2, 3, 4, 5].  &lt;span style="color:gray;"&gt;% 成功. Aに1、Bに2、Tに[3, 4, 5]が入る.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;スクリプトの実行&lt;/h4&gt;

コンパイルして実行するには以下のようにコマンドを実行します。ただし、ソースコードをコンパイルするためには、先頭でモジュール名をアトムで指定する必要があり、モジュール名は基本的に拡張子を除いたファイル名が使われます。例えば、ソースコードのファイル名がtest.erlであれば、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;-module(test).&lt;/span&gt;と指定します。-exportについては後述の関数の説明を参照してください。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;test.erl&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;-module(test).
-export([hello/0]).
hello() -&amp;gt; io:format("Hello world~n").&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

シェル上で実行:&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;1&amp;gt; c(test).  &lt;span style="color:gray;"&gt;% コンパイル.&lt;/span&gt;
{ok,test}
2&amp;gt; test:hello().
Hello world
ok&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

コマンドラインで実行:&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;$ erlc test.erl  &lt;span style="color:gray;"&gt;% コンパイル.&lt;/span&gt;
$ erl -noshell -s test hello -s init stop
Hello world&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

ここでは、-noshellにより対話型シェルを立ち上げず、-s test helloでtest:hello()を実行し、-s init stopで終了させています。&lt;br /&gt;&lt;br /&gt;

次に示すように、escriptで実行すればコンパイルは不要ですが、実行時に呼ばれる関数はmain/1となります。-moduleと-exportは必要ありません。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;test.erl&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#!/usr/bin/env escript
main(_) -&amp;gt; io:format("Hello world~n").&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

実行:&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;$ ./test.erl
Hello world&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

コマンドライン引数を取る場合は、以下のようにします。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;test.erl&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;-module(test).
-export([main/0, main/1]).

main() -&amp;gt;   &lt;span style="color:gray;"&gt;% 引数なしの場合.&lt;/span&gt;
    io:format("No argument.~n").
main(A) -&amp;gt;  &lt;span style="color:gray;"&gt;% 引数ありの場合.&lt;/span&gt;
    [io:format("~s ", [X]) || X &amp;lt;- A],
    io:nl().&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

実行:&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;$ erlc test.erl  &lt;span style="color:gray;"&gt;% コンパイル.&lt;/span&gt;
$ erl -noshell -s test main Hello world -s init stop
Hello world
$ erl -noshell -s test main -s init stop
No argument.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;2. 数値&lt;/h3&gt;

&lt;h4&gt;数値の表現&lt;/h4&gt;

Erlangは最後がピリオド(.)になるので、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;1.&lt;/span&gt;は整数の1であり、実数の1.0ではありません。実数の1.0にするには、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;1.0.&lt;/span&gt;と書く必要があります。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;A = 1.    &lt;span style="color:gray;"&gt;% これは整数の1&lt;/span&gt;
B = 1.0.  &lt;span style="color:gray;"&gt;% これは実数の1.0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;各種演算&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;5 + 2.    &lt;span style="color:gray;"&gt;% 7&lt;/span&gt;
5 - 2.    &lt;span style="color:gray;"&gt;% 3&lt;/span&gt;
5 * 2.    &lt;span style="color:gray;"&gt;% 10&lt;/span&gt;
5 / 2.    &lt;span style="color:gray;"&gt;% 2.5&lt;/span&gt;
5 div 2.  &lt;span style="color:gray;"&gt;% 2 (商)&lt;/span&gt;
5 rem 2.  &lt;span style="color:gray;"&gt;% 1 (剰余)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;インクリメントとデクリメント&lt;/h4&gt;

Erlangは関数型言語で代入は一度しかできないので当然インクリメントはできません。&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;3. リスト&lt;/h3&gt;

Erlangでは角括弧によりリストを作ることができます。また、単一代入なので一度作成したリストを変更することはできません。変更したい場合は別のリストを作成します。&lt;br /&gt;&lt;br /&gt;

&lt;h4&gt;リストの作成&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;A = [1, 2, 3].&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;h4&gt;リストの要素への参照&lt;/h4&gt;

Erlangではリストの先頭のインデックスは1となります。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;%% 要素の参照.&lt;/span&gt;
lists:nth(1, A).  &lt;span style="color:gray;"&gt;% リストAの先頭の要素. hd(A)と同じ.&lt;/span&gt;
lists:nth(2, A).  &lt;span style="color:gray;"&gt;% リストAの2番目の要素.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;リストの個数&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;N = length([1, 2, 3]).  &lt;span style="color:gray;"&gt;% 3&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;リストの操作&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;A = [1, 2, 3].

&lt;span style="color:gray;"&gt;%% 先頭を取り出す.&lt;/span&gt;
F = hd(A).        &lt;span style="color:gray;"&gt;% Fは 1&lt;/span&gt;
[H|T] = A.        &lt;span style="color:gray;"&gt;% Hは 1、Tは[2, 3]&lt;/span&gt;

&lt;span style="color:gray;"&gt;%% 先頭に追加.&lt;/span&gt;
B = [5|T].        &lt;span style="color:gray;"&gt;% Bは[5, 2, 3]&lt;/span&gt;

&lt;span style="color:gray;"&gt;%% 末尾を取り出す.&lt;/span&gt;
C = lists:last(B) &lt;span style="color:gray;"&gt;% Cは3&lt;/span&gt;
[L|R] = lists:reverse(B).  &lt;span style="color:gray;"&gt;% Lは3、Rは[2, 5]&lt;/span&gt;

&lt;span style="color:gray;"&gt;%% 末尾に追加.&lt;/span&gt;
D = A ++ [9].     &lt;span style="color:gray;"&gt;% Dは[1, 2, 3, 9]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;4. 文字列&lt;/h3&gt;

&lt;h4&gt;文字列表現&lt;/h4&gt;

ダブルクォーテーション(")で括ると文字列になりますが、Erlangではそれを整数のリストとして扱っています。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;"abc".      &lt;span style="color:gray;"&gt;% [97, 98, 99] と同じ意味.&lt;/span&gt;
io:format("~p~n", [[97, 98, 99]]).  &lt;span style="color:gray;"&gt;% "abc"と表示される. &lt;/span&gt;
A = "abc".  &lt;span style="color:gray;"&gt;% [97, 98, 99]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;文字列操作&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;%% 結合.&lt;/span&gt;
A = string:concat("aaa", "bbb").           &lt;span style="color:gray;"&gt;% "aaabbb"&lt;/span&gt;
A = lists:concat(["aaa", "bbb", "ccc"]).   &lt;span style="color:gray;"&gt;% "aaabbbccc"&lt;/span&gt;

join([]) -&gt; [];
join([H|T]) -&gt; string:concat(H, join(T)).  &lt;span style="color:gray;"&gt;% バイナリ文字列の場合は、Hをbitstring_to_list(H)に.&lt;/span&gt;
A = join(["aaa", "bbb", "ccc"]).           &lt;span style="color:gray;"&gt;% "aaabbbccc"&lt;/span&gt;

&lt;span style="color:gray;"&gt;%% 分割.&lt;/span&gt;
A = re:split("aaa, bbb, ccc", "\s*,\s*").  &lt;span style="color:gray;"&gt;% [&amp;lt;&amp;lt;"aaa"&amp;gt;&amp;gt;, &amp;lt;&amp;lt;"bbb"&amp;gt;&amp;gt;, &amp;lt;&amp;lt;"ccc"&amp;gt;&amp;gt;] バイナリ文字列.&lt;/span&gt;
A = regexp:split("aaa, bbb, ccc", "\s*,\s*").  &lt;span style="color:gray;"&gt;% ["aaa", "bbb", "ccc"] regexpは遅いのでre推奨.&lt;/span&gt;

&lt;span style="color:gray;"&gt;%% 長さ.&lt;/span&gt;
L = length("abc").                &lt;span style="color:gray;"&gt;% 3&lt;/span&gt;

&lt;span style="color:gray;"&gt;%% 切り出し.&lt;/span&gt;
S = string.substr("abcd", 1, 2).  &lt;span style="color:gray;"&gt;% "ab" 文字列の先頭は1、先頭から2文字を切り出し.&lt;/span&gt;

&lt;span style="color:gray;"&gt;%% 検索.&lt;/span&gt;
R = string:rstr("abcd", "cd").    &lt;span style="color:gray;"&gt;% 見つかった場合はその位置(この場合は3)、見つからなかった場合は0が返る.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;比較演算子&lt;/h4&gt;

整数と浮動小数点数の比較以外では==と/=の代わりに、=:=と=/=を使うようにしましょう。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;N1 == N2   &lt;span style="color:gray;"&gt;% N1はN2と等しい.&lt;/span&gt;
N1 =:= N2  &lt;span style="color:gray;"&gt;% N1はN2と同一.&lt;/span&gt;
N1 /= N2   &lt;span style="color:gray;"&gt;% N1はN2と等しくない.&lt;/span&gt;
N1 =/= N2  &lt;span style="color:gray;"&gt;% N1はN2と同一でない.&lt;/span&gt;
N1 &amp;lt; N2    &lt;span style="color:gray;"&gt;% N1はN2より小さい.&lt;/span&gt;
N1 &amp;gt; N2    &lt;span style="color:gray;"&gt;% N1はN2より大きい.&lt;/span&gt;
N1 =&amp;lt; N2   &lt;span style="color:gray;"&gt;% N1はN2以下.&lt;/span&gt;
N1 &amp;gt;= N2   &lt;span style="color:gray;"&gt;% N1はN2以上.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;5. 関数&lt;/h3&gt;

Erlangでは関数名は必ずアルファベットの小文字から始まり、以下のような構文になります。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;関数名(引数) [when ガード] -&amp;gt;
    式1,
    式2,
    ...
    式N.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

式はカンマ(,)で区切り、関数の終端はピリオド(.)になります。また、モジュールの外部から関数を呼び出すためには-exportアノテーションで関数を指定しなければなりません。例えばmain([A])を呼び出したい場合、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;-export([main/1]).&lt;/span&gt;のように指定します。これは引数が1つあるmain関数をエクスポートするという意味です。Erlangでは引数の個数のことをアリティと呼びます。因みに、-exportで関数を指定しなくても、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;-compile(export_all).&lt;/span&gt;とすれば、モジュール内の関数すべてを外部から呼び出せます。&lt;br /&gt;&lt;br /&gt;

以下のsum関数は再帰を使ってリスト内のすべての要素を足し合わせます。ここでは引数のパターンが[]と[H|T]の2種類出てきていますが、上から順にパターンが照合され一致する関数が評価されます。それぞれの関数の区切りにはセミコロン(;)が使用されます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;sum([]) -&amp;gt; 0;
sum([H|T]) -&amp;gt; H + sum(T).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

関数名の右側にあるwhenとはガードと呼ばれるパターンを補助する仕掛けで、このガードの条件を満たさなければパターンが一致していても式は評価されません。以下のsum_odd関数はカードを使って奇数のみを足し合わせています。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;sum_odd([]) -&amp;gt; 0;
sum_odd([H|T]) when H rem 2 =:= 1 -&amp;gt; H + sum_odd(T);
sum_odd([_|T]) -&amp;gt; sum_odd(T).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

サンプルプログラムとして、ガードを使って&lt;a href="http://www.aoky.net/articles/jeff_atwood/why_cant_programmers_program.htm"&gt;FizzBuzz問題&lt;/a&gt;を解いてみます。1から100までを表示する場合は&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;fizzbuzz(100).&lt;/span&gt;と実行します。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;fizzbuzz(N) when N rem 15 =:= 0 -&amp;gt;
    io:format("FizzBuzz~n");
fizzbuzz(N) when N rem 3 =:= 0 -&amp;gt;
    io:format("Fizz~n");
fizzbuzz(N) when N rem 5 =:= 0 -&amp;gt;
    io:format("Buzz~n");
fizzbuzz(N) -&amp;gt;
    io:format("~p~n", [N]).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

パターン照合を使って以下のように書くこともできます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;fb(_, 0, 0) -&amp;gt; "FizzBuzz";
fb(_, 0, _) -&amp;gt; "Fizz";
fb(_, _, 0) -&amp;gt; "Buzz";
fb(N, _, _) -&amp;gt; N.

fizzbuzz(0) -&amp;gt; ok;
fizzbuzz(N) -&amp;gt; fizzbuzz(N-1), io:format("~p~n", [fb(N, N rem 3, N rem 5)]).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


Erlangでは、funを使うことによって無名関数を使うことができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;F = fun(X) -&amp;gt; X * 3 end.
F(10).  &lt;span style="color:gray;"&gt;% 30&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;6. 制御文&lt;/h3&gt;

&lt;h4&gt;case文&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;case 条件 of
    パターン1 [when ガード1] -&amp;gt; 式1;
    パターン2 [when ガード2] -&amp;gt; 式2;
    ...
    パターンN [when ガードN] -&amp;gt;式N
end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

パターンを1から順に条件と照合して一致するパターンの式を返します。case文でもガードを使うことができます。&lt;br /&gt;&lt;br /&gt;

以下に例を示します。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;X = case {20, 30, 40} of
    {10, A, B} -&amp;gt; one;
    {20, A, B} when A &amp;gt; 50 -&amp;gt; two;
    {20, A, _} when A &amp;lt; 50 -&amp;gt; three;
    true -&amp;gt; others
end.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

タプル{20, 30, 40}とパターンを比べると、2番目のパターン{20, A, B}と一致してますが、ガードのA &gt; 50の条件に一致しないので式は評価されません。次のパターン{20, A, _}も一致していて、さらにガードA &lt; 50の条件とも一致するので式が評価され、アトムthreeがXに入ることになります。最後のパターンtrueはすべての条件と一致します。&lt;br /&gt;&lt;br /&gt;

上述のFizzBuzz問題をcase文とリスト内包表記(後述)を使って1行で書くこともできます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;[io:format("~p~n",[case{N,N rem 3,N rem 5}of{_,0,0}-&amp;gt;fizzbuzz;{_,0,_}-&amp;gt;fizz;{_,_,0}-&amp;gt;buzz;{N,_,_}-&amp;gt;N end])||N&amp;lt;-lists:seq(1,100)].&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;if 文&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;if
    ガード1 -&amp;gt; 式1;
    ガード2 -&amp;gt; 式2;
    ...
    ガードN -&amp;gt; 式N
end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

例を挙げます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;N = 10.

&lt;span style="color:gray;"&gt;%% Resultにはsameが入る.&lt;/span&gt;
Result = if
    N &amp;lt; 10 -&amp;gt; low;
    N =:= 10 -&amp;gt; same;
    N &amp;gt; 10 -&amp;gt; high
end.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;for/while文&lt;/h4&gt;

Erlangにはfor文もwhile文もありません。その代わりに関数を使って表現できます。&lt;br /&gt;&lt;br /&gt;

例えばC言語のfor文を使った以下のようなコードについて考えます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;for (i = 1; i &amp;lt;= 10; i++) {
    printf("%d\n", i * i);
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

Erlangではこれを以下のように書くことができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;for(M, M, F) -&amp;gt; [F(M)];
for(I, M, F) -&amp;gt; [F(I) | for(I+1, M, F)].

for(1, 10, fun(I) -&amp;gt; io:format("~p~n", [I*I]) end).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;7. ファイル入出力&lt;/h3&gt;

ファイル操作にはfileモジュールを利用します。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;{ok, S} = file:open("input.txt", read).    &lt;span style="color:gray;"&gt;% 入力をinput.txtから読み込む.&lt;/span&gt;
{ok, W} = file:open("output.txt", write).  &lt;span style="color:gray;"&gt;% 出力をoutput.txtに書き出す.&lt;/span&gt;

&lt;span style="color:gray;"&gt;%% input.txtから数値を読み込んで、その数値を倍にしてoutput.txtに書き込む.&lt;/span&gt;
for_write(eof, _, _) -&amp;gt; ok;
for_write(L, S, W) -&amp;gt;
    {N, _} = string:to_integer(L),
    io:format(W, "~p~n", [N*2]),
    for_write(io:get_line(S, ''), S, W).

for_write(io:get_line(S, ''), S, W).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

バイナリデータinput.datを読み込んで、それをoutput.datに2回書き込みます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;{ok, B} = file:read_file("input.dat").
file:write_file("output.dat", B).
file:write_file("output.dat", B, [binary, append]).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;8. 平行プログラミング&lt;/h3&gt;

my_server.erlに以下のコードを作成します。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;-module(my_server).
-export([loop/0]).

loop() -&gt;
    receive
        hello -&gt;
            io:format("Hello!~n"),
            loop();
        bye -&gt;
            io:format("See you!~n")
    end.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

これをシェル上でspawn関数を使って、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;spawn(fun モジュール名:関数名/アリティ)&lt;/span&gt;のように呼び出します。因みに&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;fun モジュール名:関数名/アリティ&lt;/span&gt;は、関数の参照を行うための記法になります。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;1&amp;gt; Pid = spawn(fun my_server:loop/0).
2&amp;gt; Pid ! hello.
Hello!
3&amp;gt; Pid ! bye.
See you!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

spawn関数によりmy_server:loop関数をプロセス上に実行させて、プロセスIDのPidを取得し、送信演算子 ! でhelloメッセージを送ることによりプログラムから返事が返ってきます。上記のプログラムではbyeと送ることで終了します。&lt;br /&gt;&lt;br /&gt;

spawn関数は上記の方法のほかに、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;spawn(モジュール名, 関数名, [引数])&lt;/span&gt;のように使うこともできます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;1&amp;gt; Pid = spawn(my_server, loop, []).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

平行プログラミングはErlangの存在を際立たせているのですが、ここで扱うには規模が大きすぎるので割愛します。詳しくは参考サイトなどを参照してください。&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;知っておいた方がよい文法&lt;/h3&gt;

&lt;h4&gt;真偽値&lt;/h4&gt;

真偽値の型はありませんが、アトムのtrueとfalseが真偽値のリテラルとして使われます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;not true.  &lt;span style="color:gray;"&gt;% falseアトムが返される.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;論理演算子&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;true or false.   &lt;span style="color:gray;"&gt;% true&lt;/span&gt;
true and false.  &lt;span style="color:gray;"&gt;% false&lt;/span&gt;
not true.        &lt;span style="color:gray;"&gt;% false&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;ビット演算子&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;1 bor 2.   &lt;span style="color:gray;"&gt;% 3&lt;/span&gt;
5 band 3.  &lt;span style="color:gray;"&gt;% 1&lt;/span&gt;
5 bxor 3.  &lt;span style="color:gray;"&gt;% 6&lt;/span&gt;
bnot 3.    &lt;span style="color:gray;"&gt;% -4&lt;/span&gt;
1 bsl 2.   &lt;span style="color:gray;"&gt;% 4 - 左シフト&lt;/span&gt;
16 bsr 2.  &lt;span style="color:gray;"&gt;% 4 - 右シフト&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;map関数&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;%% [1, 4, 9, 16 ,25] - リストの要素をそれぞれ2乗にする.&lt;/span&gt;
lists:map(fun(X) -&amp;gt; X * X end, [1, 2, 3, 4, 5]).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

定義された関数を使う場合は以下のようになります。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;square(X) -&amp;gt; X * X.  &lt;span style="color:gray;"&gt;% 2乗を返す関数.&lt;/span&gt;
lists:map(fun square/1, [1, 2, 3, 4, 5]).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;h4&gt;filter関数&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;%% [1, 3, 5] - 奇数のみのリストを作る.&lt;/span&gt;
lists:filter(fun(X) -&amp;gt; X rem 2 =:= 1 end, [1, 2, 3, 4, 5]).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;リスト内包表記&lt;/h4&gt;

上述のmap関数はリスト内包表記を使って以下のように書くことができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;[X * X || X &amp;lt;- [1, 2, 3, 4, 5]].  &lt;span style="color:gray;"&gt;% [1, 4, 9, 16, 25]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

上述のfilter関数は以下のように書くことができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;[X || X &amp;lt;- [1, 2, 3, 4, 5], X rem 2 =:= 1].  &lt;span style="color:gray;"&gt;% [1, 3, 5]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;h4&gt;数値と文字列の変換&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;list_to_integer("12").   &lt;span style="color:gray;"&gt;% 12&lt;/span&gt;
list_to_float("123.5").  &lt;span style="color:gray;"&gt;% 123.5&lt;/span&gt;
integer_to_list(12).     &lt;span style="color:gray;"&gt;% "12"&lt;/span&gt;
float_to_list(123.5).    &lt;span style="color:gray;"&gt;% "1.23500000000000000000e+002"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;参考サイト&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.erlang.org/"&gt;Erlang Programming Language, Official Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.erlang.org/doc/man/"&gt;Erlang/OTP Manual Page Index&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://erlangworld.web.fc2.com/sequential_programming/"&gt;Erlang World&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ancient.s6.xrea.com/erlang/cookbook.html"&gt;Erlang クエックブック&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://erlang.shibu.jp/efficiency_guide/index.html"&gt;Erlang Efficiency Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.kmonos.net/alang/etc/erlang.php"&gt;Erlang Land&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.overlasting.net/2007-05-09-1.html"&gt;これから15分でErlangを始めるための資料 - Blog::Overlasting::Net&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;

&lt;h3&gt;基礎文法最速マスターまとめサイト&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/gifnksm/20100202/1265105961"&gt;(基礎|変態)文法最速マスターシリーズのまとめ - なんとなく日記&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://wiki.onakasuita.org/pukiwiki/?%E5%9F%BA%E7%A4%8E%E6%96%87%E6%B3%95%E6%9C%80%E9%80%9F%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%BC"&gt;基礎文法最速マスター - おなかすいたWiki！&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/tt_clown/20100202/1265096776"&gt;はてな的プログラミング言語人気ランキング - Life like a clown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/seikenn/20100203/programmingMaster"&gt;プログラミング基礎文法最速マスターまとめ - ネットサービス研究室&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;

&lt;iframe src="http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=FFFFFF&amp;IS2=1&amp;npa=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=footawwwservi-22&amp;o=9&amp;p=8&amp;l=as1&amp;m=amazon&amp;f=ifr&amp;asins=4274067149" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"&gt;&lt;/iframe&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-6286610868603525047?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/6286610868603525047/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=6286610868603525047' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/6286610868603525047'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/6286610868603525047'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/02/erlang.html' title='Erlang基礎文法最速マスター'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-1120310579823997893</id><published>2010-02-02T16:48:00.030+09:00</published><updated>2011-04-07T13:30:42.127+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Lua'/><title type='text'>Lua基礎文法最速マスター</title><content type='html'>最近、基礎文法最速マスターというプログラミング言語の解説が流行ってるようなので、便乗してみた。個人的にはC++やPythonの方が慣れ親しんでいるのだが、自分でも勉強できるように普段使っていない言語を書いてみることにした。以前にここのブログで言及した言語、Processing、Erlang、Lua、PowerShellなどの中でもErlangとLuaに興味があったので、比較的書きやすいLuaを選んでみた。&lt;br /&gt;&lt;br /&gt;

何故Luaなのか? &lt;a href="http://ja.wikipedia.org/wiki/Lua"&gt;Wikipedia&lt;/a&gt;によると、Luaはブラジル・リオデジャネイロのカトリカ大学で生まれた手続き型言語だ。高速な動作、高い移植性、組み込みの容易さが特徴だ。また、ホストプログラムへの組み込みが容易であることもあって、&lt;a href="http://en.wikipedia.org/wiki/Category:Lua-scripted_video_games"&gt;コンピュータゲームなどで利用&lt;/a&gt;されている。有名どころでは、&lt;a href="http://ja.wikipedia.org/wiki/PlayStation_Home"&gt;PlayStation Home&lt;/a&gt;や&lt;a href="http://ja.wikipedia.org/wiki/World_of_Warcraft"&gt;World of Warcraft&lt;/a&gt;などがある。また、小飼弾氏も&lt;a href="http://blog.livedoor.jp/dankogai/archives/51227421.html"&gt;なんてめんこい言語&lt;/a&gt;と述べているように評価が高い。&lt;br /&gt;&lt;br /&gt;

さて、早速Lua基礎文法最速マスターを書いてみる。上述のように自分にとっては勉強を兼ねて書いているので、間違いや記述漏れなどがあるかもしれない。コメントなどで指摘していただけるとありがたい。また、C言語の連携など、足りない部分については随時追加していく予定だ。&lt;br /&gt;&lt;br /&gt;

追記(2010/2/9): 「C言語とLuaの連携」を追加した。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;/span&gt;
&lt;div id="fullpost"&gt;&lt;br /&gt;
&lt;hr /&gt;

&lt;h3&gt;1. 基礎&lt;/h3&gt;

&lt;h4&gt;対話環境&lt;/h4&gt;
 
Luaは対話環境として実行できます。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;$ lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
&amp;gt; print("Hello world")
Hello, world
&amp;gt; print(1 + 2)
3
&amp;gt; print(type("Hello world"))
string&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

print関数の代わりに = を利用することもできます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&amp;gt; = "Hello world"
Hello world
&amp;gt; = 1 + 2
3
&amp;gt; = type("Hello world"))
string&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;print関数&lt;/h4&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;print("Hello world")
print(1 + 2)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h4&gt;コメント&lt;/h4&gt;
 
ハイフン2つ(--)から行末までがコメントになります。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;-- コメント.&lt;/span&gt;
&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

複数行コメントには--[[ ～ ]]を使います。また、最後の]]を--]]としておけば、先頭の--[[を---[[と変更することでコメントを外すことができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;--[[
これは,
コメントです.
--]]&lt;/span&gt;

&lt;span style="color:gray;"&gt;---[[&lt;/span&gt;
print("これは,")
print("コメントではありません.")
&lt;span style="color:gray;"&gt;--]]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h4&gt;変数の宣言と代入&lt;/h4&gt;
 
大域変数には宣言はありません。局所変数にはlocalを使います。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;a = 1
a = "hello"
local a = 3
local a = "world"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

Luaでは多重代入することができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;a, b, c = 1, 2, 3&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

多重代入を利用してスワップが可能です。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;a, b = b, a  &lt;span style="color:gray;"&gt;-- aとbの値を入れ替える.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

 
&lt;h4&gt;スクリプトの実行&lt;/h4&gt;

コマンドラインから以下のように実行することができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;lua test.lua&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

またコマンドライン引数はテーブルargに保持されます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;--[[
  arg[-1]: "lua"
  arg[0]: "test.lua"
  arg[1]: "arg1"
  arg[2]: "arg2"
--]]&lt;/span&gt;
lua test.lua arg1 arg2&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

 
&lt;h3&gt;2. 数値&lt;/h3&gt;
 
&lt;h4&gt;数値の表現&lt;/h4&gt;
 
実数と整数の区別はありません。以下の変数b, c, dは同じ数値になります。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;a = 1
b = 1.234
c = 0.1234e1
d = 12.34e-1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;各種演算&lt;/h4&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;num = 5 + 2  &lt;span style="color:gray;"&gt;-- 7&lt;/span&gt;
num = 5 - 2  &lt;span style="color:gray;"&gt;-- 3&lt;/span&gt;
num = 5 * 2  &lt;span style="color:gray;"&gt;-- 10&lt;/span&gt;
num = 5 / 2  &lt;span style="color:gray;"&gt;-- 2.5&lt;/span&gt;
num = 5 % 2  &lt;span style="color:gray;"&gt;-- 1 (剰余)&lt;/span&gt;
num = 5 ^ 2  &lt;span style="color:gray;"&gt;-- 25 (累乗)&lt;/span&gt;
&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h4&gt;インクリメントとデクリメント&lt;/h4&gt;
 
Luaには++演算子や+=演算子がないので以下のように記述します。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;i = i + 1
i = i - 1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h3&gt;3. 文字列&lt;/h3&gt;
 
&lt;h4&gt;文字列表現&lt;/h4&gt;
 
文字列はシングルクォート(')かダブルクォート(")で囲みます。2重の角括弧([[～]])でも文字列となり、エスケープシーケンスなどもそのまま表示できます。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;str = 'abc'
str = "abc"
str = [[abc]]
str = 'ab"cd"ef'   &lt;span style="color:gray;"&gt;-- ab"cd"ef&lt;/span&gt;
str = "ab'cd'ef"   &lt;span style="color:gray;"&gt;-- ab'cd'ef&lt;/span&gt;
str = "abc\ndef"   &lt;span style="color:gray;"&gt;-- abcに改行が入り、次の行にdefが出力される&lt;/span&gt;
str = [[abc\def]]  &lt;span style="color:gray;"&gt;-- abc\defが1行で出力される&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

string.format関数を利用してC言語のprintf関数のような書式設定ができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;string.format("%d, %6.3f", 10, 12.34567)  &lt;span style="color:gray;"&gt;-- 10,  12.346&lt;/span&gt;
("%d, %6.3f"):format(10, 12.34567)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

"%q"は、安全に読み出せる文字列に書式化します。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;--[[
"test\
\"test\"\
test"
--]]&lt;/span&gt;
string.format("%q", 'test\n"test"\ntest')
("%q"):format('test\n"test"\ntest')&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;h4&gt;文字列操作&lt;/h4&gt;

分割については標準ライブラリにないので自前でsplit関数を用意します。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;-- 結合.&lt;/span&gt;
a = "aaa" .. "bbb"  &lt;span style="color:gray;"&gt;-- "aaabbb"&lt;/span&gt;
a = table.concat({ "aaa", "bbb", "ccc" }, ",")  &lt;span style="color:gray;"&gt;-- "aaa,bbb,ccc"&lt;/span&gt;

&lt;span style="color:gray;"&gt;-- 分割.&lt;/span&gt;
function split(str, del)
  p, nrep = str:gsub("%s*"..del.."%s*", "")
  return { str:match((("%s*(.-)%s*"..del.."%s*"):rep(nrep).."(.*)")) }
end

split("aaa, bbb, ccc", ",")  &lt;span style="color:gray;"&gt;-- { "aaa", "bbb", "ccc" }&lt;/span&gt;

&lt;span style="color:gray;"&gt;-- 長さ.&lt;/span&gt;
length = #"abc"  &lt;span style="color:gray;"&gt;-- 3&lt;/span&gt;
 
&lt;span style="color:gray;"&gt;-- 切り出し.&lt;/span&gt;
substr = string.sub("abcd", 0, 2)  &lt;span style="color:gray;"&gt;-- ab&lt;/span&gt;
substr = ("abcd"):sub(0, 2)
 
&lt;span style="color:gray;"&gt;-- 検索.&lt;/span&gt;
result = string.find("abcd", "cd")  &lt;span style="color:gray;"&gt;-- 見つかった場合はその位置(この場合は3)、見つからなかった場合はnilが返る.&lt;/span&gt;
result = ("abcd"):find("cd")&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;4. 配列&lt;/h3&gt;
 
&lt;h4&gt;配列変数の宣言と代入&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;-- 配列の宣言&lt;/span&gt;
array = {}
 
&lt;span style="color:gray;"&gt;-- 配列への代入&lt;/span&gt;
array = { 1, 2, 3 }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;h4&gt;配列の要素の参照と代入&lt;/h4&gt;
 
Luaでは配列の添え字(インデックス)は通常1から始まります。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;-- 要素の参照.&lt;/span&gt;
array[1]  &lt;span style="color:gray;"&gt;-- 配列arrayの先頭.&lt;/span&gt;
array[2]
 
&lt;span style="color:gray;"&gt;-- 要素の代入.&lt;/span&gt;
array[1] = 1
array[2] = 2&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h4&gt;配列の個数&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;array_num = #array
array_num = table.getn(array)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

 
&lt;h4&gt;配列の操作&lt;/h4&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;array = { 1, 2, 3 }
 
&lt;span style="color:gray;"&gt;-- 先頭を取り出す.&lt;/span&gt;
first = table.remove(array, 1)  &lt;span style="color:gray;"&gt;-- firstは 1, arrayは { 2, 3 }&lt;/span&gt;
 
&lt;span style="color:gray;"&gt;-- 先頭に追加.&lt;/span&gt;
table.insert(array, 1, 5)       &lt;span style="color:gray;"&gt;-- arrayは { 5, 2, 3 }&lt;/span&gt;
 
&lt;span style="color:gray;"&gt;-- 末尾を取り出す.&lt;/span&gt;
last = table.remove(array)      &lt;span style="color:gray;"&gt;-- lastは 3, arrayは { 5, 2 }&lt;/span&gt;
 
&lt;span style="color:gray;"&gt;-- 末尾に追加.&lt;/span&gt;
table.insert(array, 9)          &lt;span style="color:gray;"&gt;-- arrayは { 5, 2, 9 }&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h3&gt;5. テーブル&lt;/h3&gt;
 
Luaのテーブルは連想配列になっていて、nilを除く数値や文字列を添え字とすることができます。上述の配列についても基本的にはテーブルとなります。また、テーブルを入れ子にしたり、関数を保持させることもできます。&lt;br /&gt;&lt;br /&gt;
 
&lt;h4&gt;テーブルの宣言と代入&lt;/h4&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;t = { 1, 2, 3 }
t = { name = "lua", length = 3, sense = "moon" }
t = { "students", name = { "taro", "hanako", "kenta" }, num = 3 }
t = { func1 = function() return "ok" end, func2 = function(a, b) local c = a * b return c end }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;h4&gt;テーブルの要素の参照と代入&lt;/h4&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;-- 要素の参照.&lt;/span&gt;
t[1]
t["name"]
t.name  &lt;span style="color:gray;"&gt;-- 文字列を添え字にする場合はピリオド(.)を使用できる.&lt;/span&gt;
t.func2(2, 3)  &lt;span style="color:gray;"&gt;-- テーブルに保持した関数を呼び出す.&lt;/span&gt;

&lt;span style="color:gray;"&gt;-- 要素の代入.&lt;/span&gt;
t[1] = 5
t["name"] = "lua"
t.name = "lua"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h3&gt;6. 制御文&lt;/h3&gt;
 
&lt;h4&gt;if 文&lt;/h4&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;if 条件 then
  &lt;span style="color:gray;"&gt;-- 条件が真.&lt;/span&gt;
end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h4&gt;if ～ else文&lt;/h4&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;if 条件 then
  &lt;span style="color:gray;"&gt;-- 条件が真.&lt;/span&gt;
else
  &lt;span style="color:gray;"&gt;-- 条件が偽.&lt;/span&gt;
end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h4&gt;if ～ elseif 文&lt;/h4&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;if 条件1 then
  &lt;span style="color:gray;"&gt;-- 条件1が真.&lt;/span&gt;
elseif 条件2 then
  &lt;span style="color:gray;"&gt;-- 条件1が偽、条件2が真.&lt;/span&gt;
end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h4&gt;while文&lt;/h4&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;while 条件 do
  &lt;span style="color:gray;"&gt;-- 条件が真の間、処理される.&lt;/span&gt;
end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h4&gt;repeat ～ until文&lt;/h4&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;repeat
  &lt;span style="color:gray;"&gt;-- 条件が偽の間、処理される.&lt;/span&gt;
until 条件&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 

&lt;h4&gt;for文&lt;/h4&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;for i = 1, 10 do
  print(i)  &lt;span style="color:gray;"&gt;-- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10を出力.&lt;/span&gt;
end
 
for i = 1, 10, 2 do
  print(i)  &lt;span style="color:gray;"&gt;-- 1, 3, 5, 7, 9を出力.&lt;/span&gt;
end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

汎用forループとipairs/pairs関数を使って配列/テーブルを出力できます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;for i, value in ipairs({ 10, 20, 30 }) do
  &lt;span style="color:gray;"&gt;--[[
    1       10
    2       20
    3       30
    と表示される.
  --]]&lt;/span&gt;
  print(i, value)
end

data = { a = 5, b = 6, c = 7 }
for key, value in pairs(data) do
  &lt;span style="color:gray;"&gt;--[[
    a       5
    c       7
    b       6
    と表示される.
  --]]&lt;/span&gt;
  print(key, value)
end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

 
&lt;h4&gt;比較演算子&lt;/h4&gt;

比較演算子では偽の時にfalse、真の時にtrueを返します。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;num1 == num2  &lt;span style="color:gray;"&gt;-- num1はnum2と等しい.&lt;/span&gt;
num1 ~= num2  &lt;span style="color:gray;"&gt;-- num1はnum2と等しくない.&lt;/span&gt;
num1 &amp;lt; num2   &lt;span style="color:gray;"&gt;-- num1はnum2より小さい.&lt;/span&gt;
num1 &amp;gt; num2   &lt;span style="color:gray;"&gt;-- num1はnum2より大きい.&lt;/span&gt;
num1 &amp;lt;= num2  &lt;span style="color:gray;"&gt;-- num1はnum2以下.&lt;/span&gt;
num1 &amp;gt;= num2  &lt;span style="color:gray;"&gt;-- num1はnum2以上.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h3&gt;7. 関数&lt;/h3&gt;
 
Luaでは関数を作るにはfunction(...) ～ endを使います。また、関数内に関数を作ることができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;function sum(num1, num2)
  local total = num1 + num2
  return total
end
 
sum = function(num1, num2)
  local total = num1 + num2
  return total
end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;h3&gt;8. ファイル入出力&lt;/h3&gt;

Lua独自のファイル入出力として、io.input, io.output関数があります。初期状態で標準入力、標準出力に割り当てられています。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;io.input("input.txt")    &lt;span style="color:gray;"&gt;-- 入力をinput.txtから読み込む.&lt;/span&gt;
io.output("output.txt")  &lt;span style="color:gray;"&gt;-- 出力をoutput.txtに書き出す.&lt;/span&gt;

while line = io.read() do
  print(line)  &lt;span style="color:gray;"&gt;-- io.write(line .."\n")と同じ.&lt;/span&gt;
end

io.input()   &lt;span style="color:gray;"&gt;-- 入力を標準入力にする.&lt;/span&gt;
io.output()  &lt;span style="color:gray;"&gt;-- 出力を標準出力にする.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


C言語のようなファイル入出力にはio.open関数を使います。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;&lt;span style="color:gray;"&gt;--[[
r: 読み込み, w:書き込み, a:追記, r+, w+, a+
fh: ファイルハンドル, msg: エラーメッセージ.
--]]&lt;/span&gt;
fh, msg = io.open("input.txt", "r")

if fh then
  data = fh:read("*a")  &lt;span style="color:gray;"&gt;-- ファイル全体を読み込む.&lt;/span&gt;
  print(data)
else
  print(msg)
end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;9. C言語とLuaの連携&lt;/h3&gt;

LuaではC言語と連携を取るためのAPIが充実しているので、C言語からLuaを利用することも、LuaからC言語を利用することも簡単です。基本的な流れとして、スタックに積んだデータをやりとりすることになります。&lt;br /&gt;&lt;br /&gt;
 
以下にサンプルコードを示しておきます。LuaからC言語を利用する場合も、C言語からLuaを利用する場合も、C言語のソースコードをコンパイルしたファイルから実行されます。&lt;br /&gt;&lt;br /&gt;
 
ヘッダファイルやライブラリのパスを通した上で、Visual Studioでは、&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;cl func.c lua51.lib&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
gccでは、&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;gcc func.c -llua -lm&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
でコンパイルできます。&lt;br /&gt;&lt;br /&gt;
 
&lt;strong&gt;func.lua&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;print(func1(1.5))   &lt;span style="color:gray;"&gt;-- C言語で作成した関数func1を呼び出す.&lt;/span&gt;
data = { test = "lua" }
print(func2(data))  &lt;span style="color:gray;"&gt;-- C言語で作成した関数func2を呼び出す.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
 
&lt;strong&gt;func.c&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#include &lt;stdio.h&gt;
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

&lt;span style="color:gray;"&gt;/* 引数で渡された数値を半分と倍にして返す */&lt;/span&gt;
int func1(lua_State *L)
{
    &lt;span style="color:gray;"&gt;/* 最初のスタックから数値を読み出す */&lt;/span&gt;
    double d = lua_tonumber(L, 1);

    lua_pushnumber(L, d / 2.0);  &lt;span style="color:gray;"&gt;/* 半分にしてスタックに載せる */&lt;/span&gt;
    lua_pushnumber(L, d * 2.0);  &lt;span style="color:gray;"&gt;/* 倍にしてスタックに載せる */&lt;/span&gt;

    return 2;  &lt;span style="color:gray;"&gt;/* 戻り値のスタック数 */&lt;/span&gt;
}

&lt;span style="color:gray;"&gt;/* 引数で渡されたテーブル内のtestフィールドの文字列を返す */&lt;/span&gt;
int func2(lua_State *L)
{
    &lt;span style="color:gray;"&gt;/* スタックをクリアする */&lt;/span&gt;
    lua_settop(L, 1);
    &lt;span style="color:gray;"&gt;/* テーブル内のフィールド"test"をスタックの最初に置く */&lt;/span&gt;
    lua_getfield(L, 1, "test");
    &lt;span style="color:gray;"&gt;/* スタックの2番目(つまりフィールド"test"のデータ)の */&lt;/span&gt;
    &lt;span style="color:gray;"&gt;/* 文字列を読み出してスタックに置く*/&lt;/span&gt;
    lua_pushstring(L, lua_tostring(L, 2));

    return 1;  &lt;span style="color:gray;"&gt;/* 戻り値のスタック数 */&lt;/span&gt;
}

int main()
{
    &lt;span style="color:gray;"&gt;/* Luaを使えるようにする */&lt;/span&gt;
    lua_State *L = luaL_newstate();
    &lt;span style="color:gray;"&gt;/* Luaの標準関数を利用可能にする */&lt;/span&gt;
    luaL_openlibs(L);

    &lt;span style="color:gray;"&gt;/* C言語で作成した関数をLua上で呼び出せるようにする */&lt;/span&gt;
    lua_register(L, "func1", func1);
    lua_register(L, "func2", func2);

    &lt;span style="color:gray;"&gt;/* Luaソースファイル"func.lua"を読み出して実行する */&lt;/span&gt;
    &lt;span style="color:gray;"&gt;/* func.lua内でfunc1, func2を利用している */&lt;/span&gt;
    if (luaL_loadfile(L, "func.lua") || lua_pcall(L, 0, 0, 0)) {
        fprintf(stderr, "error\n");
        return -1;
    }

    &lt;span style="color:gray;"&gt;/* luaL_dostringを利用してC言語からLuaを使用する */&lt;/span&gt;
    luaL_dostring(L, "print(3.5)");

    &lt;span style="color:gray;"&gt;/* スタックを利用してC言語からLuaを利用する */&lt;/span&gt;
    lua_getglobal(L, "print");    &lt;span style="color:gray;"&gt;/* print関数をスタックに置く */&lt;/span&gt;
    lua_pushstring(L, "string");  &lt;span style="color:gray;"&gt;/* 文字列"string"をスタックに置く*/&lt;/span&gt;
    lua_pcall(L, 1, 0, 0);  &lt;span style="color:gray;"&gt;/* 引数1個、戻り値なし、エラー関数なしで実行 */&lt;/span&gt;

    lua_close(L);

    return 0;
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
出力結果:&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;0.75    3
lua
3.5
string&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

上記の方法だと、LuaからC言語の関数を呼び出す場合でもC言語からLuaのコードを呼ばなくてはならず、コンパイルが必要になります。そこで、以下にC言語による関数をWindowsのDLLファイルおよびUnixの共有ライブラリファイルのモジュールとして呼び出す方法を書いておきます。関数の内容は上記のコードと同じで、func1とfunc2を定義しています。&lt;br /&gt;&lt;br /&gt;

Visual Studioでのコンパイル方法は以下の通りです。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;cl cmodule.c lua51.lib /LD&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

gccでのコンパイル方法は以下のようになります。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;gcc cmodule.c -llua -shared -o cmodule.so&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

実行は、通常のLuaスクリプトと同じです。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;lua func_mod.lua&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;strong&gt;func_mod.lua&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;require("cmodule")          &lt;span style="color:gray;"&gt;-- cmodule.dll/cmodule.soを読み込む.&lt;/span&gt;

print(cmodule.func1(1.5))   &lt;span style="color:gray;"&gt;-- C言語で作成した関数func1を呼び出す.&lt;/span&gt;
data = { test = "lua" }
print(cmodule.func2(data))  &lt;span style="color:gray;"&gt;-- C言語で作成した関数func2を呼び出す.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;strong&gt;cmodule.c&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#include &amp;lt;stdio.h&amp;gt;
#include "lua.h"
#include "lauxlib.h"

&lt;span style="color:gray;"&gt;/* 引数で渡された数値を半分と倍にして返す */&lt;/span&gt;
static int func1(lua_State *L)
{
    &lt;span style="color:gray;"&gt;/* 最初のスタックから数値を読み出す */&lt;/span&gt;
    double d = lua_tonumber(L, 1);

    lua_pushnumber(L, d / 2.0);  &lt;span style="color:gray;"&gt;/* 半分にしてスタックに載せる */&lt;/span&gt;
    lua_pushnumber(L, d * 2.0);  &lt;span style="color:gray;"&gt;/* 倍にしてスタックに載せる */&lt;/span&gt;

    return 2;  &lt;span style="color:gray;"&gt;/* 戻り値のスタック数 */&lt;/span&gt;
}

&lt;span style="color:gray;"&gt;/* 引数で渡されたテーブル内のtestフィールドの文字列を返す */&lt;/span&gt;
static int func2(lua_State *L)
{
    &lt;span style="color:gray;"&gt;/* スタックをクリアする */&lt;/span&gt;
    lua_settop(L, 1);
    &lt;span style="color:gray;"&gt;/* テーブル内のフィールド"test"をスタックの最初に置く */&lt;/span&gt;
    lua_getfield(L, 1, "test");
    &lt;span style="color:gray;"&gt;/* スタックの2番目(つまりフィールド"test"のデータ)の */&lt;/span&gt;
    &lt;span style="color:gray;"&gt;/* 文字列を読み出してスタックに置く*/&lt;/span&gt;
    lua_pushstring(L, lua_tostring(L, 2));

    return 1;  &lt;span style="color:gray;"&gt;/* 戻り値のスタック数 */&lt;/span&gt;
}

&lt;span style="color:gray;"&gt;/* モジュールに登録するために関数のテーブルを定義 */&lt;/span&gt;
static luaL_Reg cmodule[] = {
    { "func1", func1 },
    { "func2", func2 },
    { NULL, NULL }  &lt;span style="color:gray;"&gt;/* 最後は NULL, NULL とする */&lt;/span&gt;
};

&lt;span style="color:gray;"&gt;/* DLL/共有ライブラリにエクスポートする関数 luaopen_(モジュール名) */&lt;/span&gt;
&lt;span style="color:gray;"&gt;/* Unixでは__declspec(dllexport)は不要 */&lt;/span&gt;
__declspec(dllexport) int luaopen_cmodule(lua_State *L)
{
    luaL_register(L, "cmodule", cmodule); &lt;span style="color:gray;"&gt;/* cmoduleモジュール */&lt;/span&gt;
    return 1;
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

出力結果:&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;0.75    3
lua&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;知っておいた方がよい文法&lt;/h3&gt;
 
&lt;h4&gt;真偽値&lt;/h4&gt;
 
falseとnil以外はすべて真になります。よって、0や""や空のテーブルなどもすべて真です。&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;論理演算子&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;a = 10
b = 30
c = a &amp;lt; 20 or b &amp;lt; 20   &lt;span style="color:gray;"&gt;-- true&lt;/span&gt;
d = a &amp;lt; 20 and b &amp;lt; 20  &lt;span style="color:gray;"&gt;-- false&lt;/span&gt;

a = 10 or 20    &lt;span style="color:gray;"&gt;-- a = 10&lt;/span&gt;
a = 10 and 20   &lt;span style="color:gray;"&gt;-- a = 20&lt;/span&gt;
a = nil or 10   &lt;span style="color:gray;"&gt;-- a = 10&lt;/span&gt;
a = nil and 10  &lt;span style="color:gray;"&gt;-- a = nil&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

初期化されていない変数はすべてnilが入っていることを利用して、orを使ってデフォルト値を設定することができます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;a = a or 10  &lt;span style="color:gray;"&gt;-- aに何も代入されていなければ10が代入される.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;外部Luaファイルの読み込み&lt;/h4&gt;

外部Luaファイルの実行やテーブルなどのデータを読み込む目的で利用できます。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;dofile("file.lua")&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;文字列をプログラムとして実行&lt;/h4&gt;

Perlなどのeval機能として、loadstring関数があります。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;f = loadstring("i = i + 1")
i = 0
f()
f()
f()
print(i)  &lt;span style="color:gray;"&gt;-- 3と表示される.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;データの型を文字列で返す&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;type(1)            &lt;span style="color:gray;"&gt;-- "number"&lt;/span&gt;
type("abc")        &lt;span style="color:gray;"&gt;-- "string"&lt;/span&gt;
type({ 1, 2, 3 })  &lt;span style="color:gray;"&gt;-- "table"&lt;/span&gt;
type(true)         &lt;span style="color:gray;"&gt;-- "boolean"&lt;/span&gt;
type(print)        &lt;span style="color:gray;"&gt;-- "function"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h4&gt;数値と文字列の変換&lt;/h4&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;tonumber("123.5")  &lt;span style="color:gray;"&gt;-- 123.5&lt;/span&gt;
tostring(123.5)    &lt;span style="color:gray;"&gt;-- "123.5"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;


&lt;h3&gt;参考サイト&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.lua.org/"&gt;The Programming Language Lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://sugarpot.sakura.ne.jp/yuno/html/lua51_manual_ja.html"&gt;Lua 5.1 リファレンスマニュアル&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://staff.aist.go.jp/yutaka.ueno/lua/tebiki3jp.html"&gt;Lua言語の手引き&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;

&lt;h3&gt;他の基礎文法最速マスター&lt;/h3&gt;

&lt;ul&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/perlcodesample/20091226/1264257759"&gt;Perl基礎文法最速マスター - Perl入門～サンプルコードによるPerl入門～&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://route477.net/d/?date=20100125"&gt;Ruby基礎文法最速マスター - Route477&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/dplusplus/20100126/p1"&gt;Python基礎文法最速マスター - D++のはまり日誌&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://www.1x1.jp/blog/2010/01/php-basic-syntax.html"&gt;PHP基礎文法最速マスター - Shin x blog&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/nattou_curry_2/20100129/1264787849"&gt;VBA基礎文法最速マスター - 何かしらの言語による記述を解析する日記&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/nattou_curry_2/20100130/1264821094"&gt;Java基礎文法最速マスター - 何かしらの言語による記述を解析する日記&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/shunsuk/20100130/1264842323"&gt;Brainf*ck基礎文法最速マスター - 医者を志す妻を応援する夫の日記&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/ruicc/20100131/1264905896"&gt;Haskell基礎文法最速マスター - think and error&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://vbscript.g.hatena.ne.jp/cx20/20100131/1264906231"&gt;VBScript 基礎文法最速マスター - CX's VBScript Diary&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/nattou_curry_2/20100131/1264910483"&gt;Bash基礎文法最速マスター - 何かしらの言語による記述を解析する日記&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/gifnksm/20100131/1264934942"&gt;JavaScript基礎文法最速マスター - なんとなく日記&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/thinca/20100201/1265009821"&gt; Vimスクリプト基礎文法最速マスター - 永遠に未完成&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/faith_and_brave/20100201/1264997004"&gt; C++0x基礎文法最速マスター - Faith and Brave - C++で遊ぼう&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/repeatedly/20100201/1264972016"&gt;D言語基礎文法最速マスター   - はてなかよっ！&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://d.hatena.ne.jp/rubikitch/20100201/elispsyntax"&gt;Emacs Lisp基礎文法最速マスター - (rubikitch loves (Emacs Ruby CUI))&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://blog.livedoor.jp/takaaki_bb/archives/51374100.html"&gt;ActionScript 3.0 基礎文法最速マスター - 読書メモ＋tips＋日記&lt;/a&gt;&lt;/li&gt; 
&lt;li&gt;&lt;a href="http://gauc.no-ip.org/awk-users-jp/blis.cgi/awk_fastest"&gt;awk基礎文法最速マスター - AWK Users JP&lt;/a&gt;&lt;/li&gt; 
&lt;/ul&gt;&lt;br /&gt;

&lt;iframe src="http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=FFFFFF&amp;IS2=1&amp;npa=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=footawwwservi-22&amp;o=9&amp;p=8&amp;l=as1&amp;m=amazon&amp;f=ifr&amp;asins=4048677977" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"&gt;&lt;/iframe&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-1120310579823997893?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/1120310579823997893/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=1120310579823997893' title='4 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1120310579823997893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1120310579823997893'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/02/lua.html' title='Lua基礎文法最速マスター'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-5532210835605846581</id><published>2010-01-22T02:54:00.002+09:00</published><updated>2010-01-22T03:45:17.231+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='アルゴリズム'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>C++0x: 構造体を格納したSTLコンテナに対してソート・探索・削除などのアルゴリズムを適用する</title><content type='html'>先日、&lt;a href="http://handasse.blogspot.com/2009/12/c-stl.html"&gt;構造体を格納したコンテナに対してSTLのアルゴリズムを適用する方法&lt;/a&gt;について記したが、今回はそれを&lt;a href="http://ja.wikipedia.org/wiki/C%2B%2B0x"&gt;C++0x&lt;/a&gt;で書き直してみた。C++0xの規格はまだきちんと定まっていないが、草案ではかなりのところまでつめられてきており、また対応するC++コンパイラも出てきていることから今のうちにC++0xに慣れておいた方が良いと思う。今回のプログラムで行う内容については前回と全く同じだが、C++0xを使うことによりかなりすっきり書くことができた。因みに現時点の最新の規格(草案)は&lt;a href="http://www.open-std.org/JTC1/sc22/wg21/docs/papers/2009/n3000.pdf"&gt;N3000&lt;/a&gt; (PDF)になる。日本語での資料は&lt;a href="http://d.hatena.ne.jp/faith_and_brave/20071022/1193052163"&gt;C++0xの言語拡張まとめ (Faith and Brave - C++で遊ぼう)&lt;/a&gt;や&lt;a href="http://cpplover.blogspot.com/search/label/C%2B%2B"&gt;本の虫&lt;/a&gt;が詳しい。余談だが、2010年になっても0xから1xに変わることはないようだ。C++の生みの親であるBjarne Stroustrupによれば、&lt;a href="http://www2.research.att.com/~bs/C++0xFAQ.html"&gt;0xのxは16進数と考えて欲しい&lt;/a&gt;とのこと。&lt;br /&gt;&lt;br /&gt;
 
C++0xでは多くの便利な機能が追加されているが、今回はラムダ式、auto型指定子、初期化子リストを使用した。使用したコンパイラは&lt;a href="http://www.microsoft.com/japan/visualstudio/products/2010/default.mspx"&gt;Visual Studio 2010 beta2 (VS2010)&lt;/a&gt;、&lt;a href="http://gcc.gnu.org/"&gt;GCC 4.5 (snapshot 20100114)&lt;/a&gt;、&lt;a href="http://software.intel.com/en-us/intel-compilers/"&gt;Intel Compiler 11.1&lt;/a&gt;だが、残念ながらVS2010とIntel Compiler 11.1では初期化子リストの機能が実装されていないので、それらを使用する場合、今回示したコードのうち素数を格納する静的メンバコンテナの初期化部分は変更しなくてはならない。また、GCCについては最新リリース版である4.4.2で&lt;a href="http://gcc.gnu.org/projects/cxx0x.html"&gt;ラムダ式が使えない&lt;/a&gt;ので、ここでは開発版の4.5を使用している。その他のコンパイラも含めてC++0xへの対応は&lt;a href="http://wiki.apache.org/stdcxx/C++0xCompilerSupport"&gt;C++0xCompilerSupport&lt;/a&gt;で確認できる。&lt;br /&gt;&lt;br /&gt;
 
初期化子リストを使用しない:&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;static int init_primes[] = { 2, 3, 5, 7 };
vector&amp;lt;int&amp;gt; IsPrime::primes(init_primes, init_primes + 4);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
初期化子リストを使用する:&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;vector&amp;lt;int&amp;gt; IsPrime::primes = { 2, 3, 5, 7 };&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
今回のコードでは関数オブジェクトを多用していたのでラムダ式が非常に有効だった。ラムダ式を使えば関数オブジェクトを用意しなくてもその場ですぐに記述できるからだ。&lt;br /&gt;&lt;br /&gt;
 
以下のコードは関数オブジェクトを用意してsort関数を使用した例だ。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;class LessAn {
public:
    bool operator()(const A&amp;amp; a, const A&amp;amp; b)
    {
        return a.n &amp;lt; b.n;
    }
};
 
sort(A_list.begin(), A_list.end(), LessAn());&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
これに対しラムダ式を使えば、以下のように一行で済ませることができる。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;sort(A_list.begin(), A_list.end(), &lt;span style="color:red;"&gt;[](const A&amp;amp; a, const A&amp;amp; b) { return a.n &amp;lt; b.n; }&lt;/span&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
このように、C++0xは機能の一部を使うだけでもとても便利だ。規格が新しくなるたびに新たに覚えなくてはならないことが増えるので、それを面倒と見る向きもあるかもしれないが、そのコストに見合うだけの価値がC++0xにはあると思う。&lt;br /&gt;&lt;br /&gt;
 
最後にC++0xで書き換えたソースコードを示す。クラスによる関数オブジェクトがごっそり無くなっていることに気付くだろう。前回からの変更部分は赤で示している。変更前のコードや実行時の出力結果については&lt;a href="http://handasse.blogspot.com/2009/12/c-stl.html"&gt;前回の記事&lt;/a&gt;を参考にして欲しい。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;cstdlib&amp;gt;

using namespace std;

const int num_A = 10;  // A_listコンテナ内の構造体Aの数.
const int max_n = 30;  // 構造体A内の配列の最大数.

// 構造体A : 要素数n, 要素の配列pを持つ.
struct A {
    int n;
    int *p;

    A(int n_, int *p_) : n(n_), p(p_)
    { for (int i = 0; i &amp;lt; n; i++) p[i] = rand() % max_n; }
};

// 構造体Aの表示.
void print(const A&amp;amp; a)
{
    cout &amp;lt;&amp;lt; setw(3) &amp;lt;&amp;lt; a.n &amp;lt;&amp;lt; " :";
    for (int i = 0; i &amp;lt; a.n; i++) cout &amp;lt;&amp;lt; " " &amp;lt;&amp;lt; a.p[i];
    cout &amp;lt;&amp;lt; endl;
}

// A_listコンテナの表示.
void print(const vector&amp;lt;A&amp;gt;&amp;amp; a_list)
{
    cout &amp;lt;&amp;lt; "  n : p[]" &amp;lt;&amp;lt; endl;
    for (&lt;span style="color:red;"&gt;auto&lt;/span&gt; p = a_list.begin(); p != a_list.end(); ++p) print(*p);
    cout &amp;lt;&amp;lt; endl;
}

// 任意の数値を素数判定する関数オブジェクト.
class IsPrime {
private:
    static vector&amp;lt;int&amp;gt; primes;
    int i, j;

public:
    bool operator()(int n)
    {
        if (n &amp;lt; 2) return false;
        if (binary_search(primes.begin(), primes.end(), n)) return true;
        for (&lt;span style="color:red;"&gt;auto&lt;/span&gt; p = primes.begin(); p != primes.end(); ++p) {
            if (n &amp;lt; (*p) * (*p)) return true;
            if (n % *p == 0) return false;
        }
        for (i = *(primes.end() - 1) + 2; i * i &amp;lt;= n; i += 2) {
            for (j = 0; i &amp;gt; primes[j] * primes[j] &amp;amp;&amp;amp; i % primes[j] != 0; j++);
            if (i &amp;lt; primes[j] * primes[j]) {
                primes.push_back(i);
                if (n % i == 0) return false;
            }
        }
        return true;
    }
};

// 素数を格納する静的メンバコンテナ.
&lt;span style="color:red;"&gt;vector&amp;lt;int&amp;gt; IsPrime::primes = { 2, 3, 5, 7 };&lt;/span&gt;

int main()
{
    vector&amp;lt;A&amp;gt; A_list;  // 構造体Aのvectorコンテナ A_list.

    // A_listを乱数で初期化.
    for (int i = 0; i &amp;lt; num_A; i++) {
        int n = rand() % max_n + 1;
        A_list.push_back(A(n, new int[n]));
    }

    cout &amp;lt;&amp;lt; "初期化した構造体Aのコンテナ(A_list):" &amp;lt;&amp;lt; endl;
    print(A_list);

    cout &amp;lt;&amp;lt; "構造体Aの配列(A.p)をソートして重複データを削除:" &amp;lt;&amp;lt; endl;
    for_each(A_list.begin(), A_list.end(),
             &lt;span style="color:red;"&gt;[](A&amp;amp; a) { sort(a.p, a.p + a.n);  a.n = unique(a.p, a.p + a.n) - a.p; }&lt;/span&gt;);
    print(A_list);

    cout &amp;lt;&amp;lt; "構造体Aの配列(A.p)の要素数(A.n)でA_listをソート:" &amp;lt;&amp;lt; endl;
    sort(A_list.begin(), A_list.end(), &lt;span style="color:red;"&gt;[](const A&amp;amp; a, const A&amp;amp; b) { return a.n &amp;lt; b.n; }&lt;/span&gt;);
    print(A_list);

    cout &amp;lt;&amp;lt; "構造体Aの配列(A.p)を素数のみにする:" &amp;lt;&amp;lt; endl;
    for_each(A_list.begin(), A_list.end(),
             &lt;span style="color:red;"&gt;[](A&amp;amp; a) { a.n = stable_partition(a.p, a.p + a.n, IsPrime()) - a.p; }&lt;/span&gt;);
    print(A_list);

    cout &amp;lt;&amp;lt; "構造体Aの配列要素の和が偶数ならA_listから削除:" &amp;lt;&amp;lt; endl;
    A_list.erase(remove_if(A_list.begin(), A_list.end(),
                           &lt;span style="color:red;"&gt;[](const A&amp;amp; a) { return accumulate(a.p, a.p + a.n, 0) % 2 == 0; }&lt;/span&gt;),
                 A_list.end());
    print(A_list);

    cout &amp;lt;&amp;lt; "再び構造体Aの配列(A.p)の要素数(A.n)でA_listをソート:" &amp;lt;&amp;lt; endl;
    sort(A_list.begin(), A_list.end(), &lt;span style="color:red;"&gt;[](const A&amp;amp; a, const A&amp;amp; b) { return a.n &amp;lt; b.n; }&lt;/span&gt;);
    print(A_list);

    cout &amp;lt;&amp;lt; "構造体Aの配列(A.p)内で指定した数値と一致する要素を持つ構造体Aを探す:" &amp;lt;&amp;lt; endl;
    for (int i = 0; i &amp;lt; 10; i++) {
        &lt;span style="color:red;"&gt;auto&lt;/span&gt; p = find_if(A_list.begin(), A_list.end(),
                         &lt;span style="color:red;"&gt;[i](const A&amp;amp; a) { return find(a.p, a.p + a.n, i) != a.p + a.n; }&lt;/span&gt;);
        cout &amp;lt;&amp;lt; setw(3) &amp;lt;&amp;lt; i &amp;lt;&amp;lt; " : ";
        if (p != A_list.end()) print(*p);
        else cout &amp;lt;&amp;lt; "指定の数値を要素として持つ構造体Aは見つかりませんでした." &amp;lt;&amp;lt; endl;
    }

    return 0;
}&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-5532210835605846581?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/5532210835605846581/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=5532210835605846581' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/5532210835605846581'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/5532210835605846581'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/01/c0x-stl.html' title='C++0x: 構造体を格納したSTLコンテナに対してソート・探索・削除などのアルゴリズムを適用する'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-1722884296440505922</id><published>2010-01-19T00:47:00.008+09:00</published><updated>2010-01-19T15:28:22.971+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='アルゴリズム'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>整数の分割</title><content type='html'>&lt;a href="http://ja.wikipedia.org/wiki/整数分割"&gt;整数の分割(integer partitions)&lt;/a&gt;を列挙するプログラムを書いてみた。ここでは再帰を用いた比較的単純な実装を示すが、より効率的なアルゴリズムを考えるとなると奥が深い。&lt;br /&gt;&lt;br /&gt;

整数の分割とは、ある整数を自然数の和で表したものだ。例えば分割する整数を5とすると求める自然数は以下のようになる。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;5
4 1
3 2
3 1 1
2 2 1
2 1 1 1
1 1 1 1 1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

今回のC++で書いたコードは単純で理解しやすいと思うが、高速化などは考えていない。もしよりよいアルゴリズムに興味があるのなら&lt;a href="http://www.site.uottawa.ca/~ivan/F49-int-part.pdf"&gt;Antoine Zoghbiu and Ivan Stojmenovic. Fast Algorithms for Generating Integer Partitions. &lt;i&gt;Intern. J. Computer Math.&lt;/i&gt;, &lt;b&gt;70&lt;/b&gt;, 319-332 (1998)&lt;/a&gt; (PDF)あたりが参考になるかも。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
&lt;iframe src="http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=FFFFFF&amp;IS2=1&amp;npa=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=footawwwservi-22&amp;o=9&amp;p=8&amp;l=as1&amp;m=amazon&amp;f=ifr&amp;asins=4826931034" style="width:120px;height:240px;float:right;margin:0 10px 10px 0;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"&gt;&lt;/iframe&gt;
使い方は以下の通り。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;partitions &lt;i&gt;分割したい整数&lt;/i&gt; [&lt;i&gt;分割した要素の最大値&lt;/i&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

分割したい整数が5であれば、&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;partitions 5&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

とする。出力は次のようになる。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;5 (max 5): 7
5
4 1
3 2
3 1 1
2 2 1
2 1 1 1
1 1 1 1 1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

また、分割したい整数が5で、分割した要素の最大値を3とする場合は、&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;partitions 5 3&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

で、以下のような出力を得る。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;5 (max 3): 5
3 2
3 1 1
2 2 1
2 1 1 1
1 1 1 1 1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;partitions.cpp&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;cstdlib&amp;gt;

using namespace std;

void partitions(int n, int max_n, vector&amp;lt;int&amp;gt;&amp;amp; v, vector&amp;lt;vector&amp;lt;int&amp;gt; &amp;gt;&amp;amp; vv)
{
    if (n == 0) {
        vv.push_back(v);
        return;
    }
    for (int i = min(n, max_n); i &amp;gt; 0; i--) {
        v.push_back(i);
        partitions(n - i, i, v, vv);
        v.pop_back();
    }
}

int main(int argc, char* argv[])
{
    if (argc &amp;lt; 2) {
        cerr &amp;lt;&amp;lt; "Usage: " &amp;lt;&amp;lt; argv[0] &amp;lt;&amp;lt; " number [max_number_in_partitions]" &amp;lt;&amp;lt; endl;
        exit(1);
    }
    int n = atoi(argv[1]);
    int max_n = argc &amp;gt; 2 ? atoi(argv[2]) : n;
    vector&amp;lt;vector&amp;lt;int&amp;gt; &amp;gt; vv;

    partitions(n, max_n, vector&amp;lt;int&amp;gt;(), vv);

    cout &amp;lt;&amp;lt; n &amp;lt;&amp;lt; " (max " &amp;lt;&amp;lt; max_n &amp;lt;&amp;lt; "): " &amp;lt;&amp;lt; vv.size() &amp;lt;&amp;lt; endl;
    for (vector&amp;lt;vector&amp;lt;int&amp;gt; &amp;gt;::const_iterator p = vv.begin(); p != vv.end(); ++p) {
        for (vector&amp;lt;int&amp;gt;::const_iterator q = p-&amp;gt;begin(); q != p-&amp;gt;end(); ++q)
            cout &amp;lt;&amp;lt; " " &amp;lt;&amp;lt; *q;
        cout &amp;lt;&amp;lt; endl;
    }

    return 0;
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

余談だが、&lt;a href="http://code.activestate.com/recipes/218332/"&gt;ActiveState Code&lt;/a&gt;でPythonのジェネレータを使った以下のようなコードを見つけた。さらに同じ作者がその2年後に書いた&lt;a href="http://11011110.livejournal.com/4862.html"&gt;記事&lt;/a&gt;から、別アルゴリズムの&lt;a href="http://www.ics.uci.edu/~eppstein/PADS/PADS.tar.gz"&gt;ソースコード&lt;/a&gt;(tarball)を読むことができる。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;def partitions(n):
    # base case of recursion: zero is the sum of the empty list
    if n == 0:
        yield []
        return

    # modify partitions of n-1 to form partitions of n
    for p in partitions(n-1):
        yield [1] + p
        if p and (len(p) &amp;lt; 2 or p[1] &amp;gt; p[0]):
            yield [p[0] + 1] + p[1:]&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-1722884296440505922?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/1722884296440505922/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=1722884296440505922' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1722884296440505922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/1722884296440505922'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/01/blog-post.html' title='整数の分割'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-2228205039654622759</id><published>2010-01-12T09:11:00.009+09:00</published><updated>2010-01-18T08:15:19.326+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='アルゴリズム'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>C++: 騎士の巡歴と周遊</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_BwIshVLoLIU/S0vF7_tJcII/AAAAAAAAAPU/RhG_Z-WpELY/s1600-h/knight_200x200s.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://2.bp.blogspot.com/_BwIshVLoLIU/S0vF7_tJcII/AAAAAAAAAPU/RhG_Z-WpELY/s320/knight_200x200s.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5425647810601644162" /&gt;&lt;/a&gt;
「&lt;a href="http://ja.wikipedia.org/wiki/ナイト・ツアー"&gt;騎士の巡歴(Knight's Tour)&lt;/a&gt;」を解くプログラムをC++で書いてみた。騎士の巡歴とはチェスボード上の駒「ナイト」を移動させてすべてのマスを一回ずつ通過させる問題だ。特に最初の駒の位置に戻ってくる解を「騎士の周遊(Closed Knight's Tour)」と呼ぶ。&lt;br /&gt;&lt;br /&gt;

一応、C++らしいコードを心掛けてみたけど、思ったより長くなってしまった。騎士の巡歴では&lt;a href="http://en.wikipedia.org/wiki/Knight's_tour#Warnsdorff.27s_algorithm"&gt;Warnsdorffのアルゴリズム&lt;/a&gt;、騎士の周遊においてはそれに加えて&lt;a href="http://en.wikipedia.org/wiki/Knight's_tour#Schwenk.27s_Theorem"&gt;Schwenkの定理&lt;/a&gt;を用いている。また、&lt;a href="http://www.geocities.jp/m_hiroi/puzzle/knight2.html"&gt;Puzzle DE Programming&lt;/a&gt;で書かれている方法も参考にさせてもらった。&lt;br /&gt;&lt;br /&gt;

まず、ボード上のマスをNode構造体のポインタで表現し、&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;*node&lt;/span&gt;とする。その&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;node&lt;/span&gt;からナイトが動けるマスを&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;vector&amp;lt;Node*&amp;gt; next;&lt;/span&gt;として保持する。また、一度通った&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;node&lt;/span&gt;は&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;node-&gt;visited&lt;/span&gt;にそれを記録しておく。&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;next&lt;/span&gt;を順に巡っていくが、Warndorffのアルゴリズムにより、移動先となる&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;next&lt;/span&gt;内の&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;node&lt;/span&gt;のうち、動けるマスの最も少ない&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;node&lt;/span&gt;を移動先とする。すべてのマスを通れば探索終了だ。&lt;br /&gt;&lt;br /&gt;

また、騎士の周遊については、最初にSchwenkの定理から解があるかどうかを判断する。解があれば、騎士の巡歴と同様の手順でまず解を見つける。ここで、見つけた解の最初のマスからナイトが動けるマスをA、最後のマスからナイトが動けるマスをBとし、BからAの順となるマスがあるかどうか調べる。もしあれば、そのAのマスから最後のマスまでの順路を逆にすればそれが騎士の周遊の解となる。もし、BからAの順となるマスがなければ、動けるマスが見つかるまでバックトラックにより探索する。この手順については上記の&lt;a href="http://www.geocities.jp/m_hiroi/puzzle/knight2.html"&gt;Puzzle DE Programming&lt;/a&gt;が詳しい。&lt;br /&gt;
&lt;span id="fullpost"&gt;&lt;br /&gt;
プログラムの使い方は以下の通り。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;knight 行数 列数 [駒のスタート行=0 駒のスタート列=0 巡歴か周遊か?]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

スタート位置はデフォルトで(0, 0)で、何も指定がなければ騎士の巡歴を求める。騎士の周遊の場合は、コマンドラインの最後に c を付ける。&lt;br /&gt;&lt;br /&gt;

例えば、16x16マス上で騎士の周遊を解く場合は以下のようにする。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;knight 16 16 c&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

出力結果:&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;   1 254  31 232 205 196  29 194 203 106  27 104 187 108  25 102
  32 229 256 253  30 235 204 197  28 193 202 107  26 103 180 109
 255   2 231 206 233 252 195 238 201 198 105 186 181 188 101  24
 230  33 228 219 236 213 234 251 192 247 190 199 112 179 110 177
   3 224 209 214 207 220 237 246 239 200 185 182 189 176  23 100
  34 227 218 223 210 241 212 167 250 191 248 113 184 111 178  67
 225   4 215 208 217 166 221 240 245 160 183 172 175  68  99  22
 128  35 226 165 222 211 242 161 168 249 170 149 114 173  66  69
   5 164 129 216 143 162 157 244 159 148 115 174 171  98  21  64
  36 127 136 163 156 243 144 141 152 169 150  93 116  65  70  55
 133   6 155 130 137 142 153 158 147  92 117  80  97  56  63  20
 126  37 132 135 154 145 140  87 118 151  94  73  62  71  54  57
   7 134 125 138 131  86 119 146  91  74  81  96  79  58  19  50
  38 123  40  85 120 139  88  45  82  95  76  61  72  51  16  53
  41   8 121 124  43  10  83  90  75  12  47  78  59  14  49  18
 122  39  42   9  84  89  44  11  46  77  60  13  48  17  52  15&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_BwIshVLoLIU/S0vED9VF9-I/AAAAAAAAAO8/2auucAsCncQ/s1600-h/knight_16x16.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 400px;" src="http://2.bp.blogspot.com/_BwIshVLoLIU/S0vED9VF9-I/AAAAAAAAAO8/2auucAsCncQ/s400/knight_16x16.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5425645748379580386" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;

24x12マスで(10, 5)の位置から騎士の巡歴を解く場合は以下のようにする。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;knight 24 12 10 5&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

出力結果:&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;  24  27 106 225 272  29 108 231 250  31 110 113
 105 226  25  28 107 230 267  30 109 112 249  32
  26  23 224 271 268 273 232 251 266 247 114 111
 211 104 227 288 229 270 259 276 233 252  33 248
  22 223 210 269 280 285 274 265 258 263 246 115
 103 212 281 228 287 260 277 262 275 234 253  34
 214  21 222 209 284 279 286 257 264 245 116 235
 193 102 213 282 219 256 261 278 241 254  35 244
  20 215 194 221 208 283 240 255 204 243 236 117
 101 192 153 218 239 220 207 242 237 200 205  36
 152  19 216 195 190   1 238 203 206 175 118 201
 159 100 191 154 217 196 189 178 199 202  37 124
  18 151 158 187 172 179   2 197 174 125 176 119
  99 160 155 184 157 188 173 168 177 198 123  38
 150  17 148 161 186 171 180   3 126 169 120 129
  95  98 185 156 183 146 167 170 165 128  39 122
  16 149  96 147 162 181 164 127   4 121 130  69
  97  94 137 182 135 142 145 166 131  70   5  40
 138  15  92 141 144 163 134  73  66  57  68  59
  93  88 139 136  83  74 143 132  71  60  41   6
  14  81  84  91 140 133  72  65  56  67  58  49
  87  78  89  82  75  64  55  46  61  50   7  42
  80  13  76  85  90  11  62  53  44   9  48  51
  77  86  79  12  63  54  45  10  47  52  43   8&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_BwIshVLoLIU/S0vETw4y5wI/AAAAAAAAAPE/s8hWi2droGs/s1600-h/knight_24x12.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 400px;" src="http://3.bp.blogspot.com/_BwIshVLoLIU/S0vETw4y5wI/AAAAAAAAAPE/s8hWi2droGs/s400/knight_24x12.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5425646019917571842" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;

示した画像は出力した解をPython+PILで画像に変換したものだ。以下のように使用する。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;show_knight.py 解のデータファイル 出力画像ファイル [幅=400 高さ=400]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

上記の例では以下のようにする。&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;show_knight.py knight_16x16.txt knight_16x16.png&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;show_knight.py knight_24x12.txt knight_24x12.png  300 600&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

また、今回のプログラムでは200x200マスの騎士の周遊でもほぼ瞬時に解くことができた。先頭の画像はその解だ。多くの場合は問題なく解を見つけることができるが、場合によっては困難なこともある。そのようなときはスタート位置を変更することで解けることがある。&lt;br /&gt;&lt;br /&gt;

以下にプログラムのソースコードを示す。&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;knight.cpp&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;// Solver of Knight's Tour using Warnsdorff's algorithm and Schwenk's theorem

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;utility&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;cstdlib&amp;gt;

using namespace std;

struct Node {
    int row, col;
    bool visited;
    vector&amp;lt;Node*&amp;gt; next;

    Node(int r, int c) : row(r), col(c), visited(false) { }
};

class IsUnvisited {
public:
    bool operator()(const Node* a) { return !a-&amp;gt;visited; }
};

class IsVisited {
public:
    bool operator()(const Node* a) { return a-&amp;gt;visited; }
};

class NotEqualUnvisited {
private:
    const int cnt;

public:
    NotEqualUnvisited(const Node* a) : cnt(count_if(a-&amp;gt;next.begin(), a-&amp;gt;next.end(), IsUnvisited())) { }
    bool operator()(const Node* a)
    { return count_if(a-&amp;gt;next.begin(), a-&amp;gt;next.end(), IsUnvisited()) != cnt; }
};

class LessMovable {
public:
    bool operator()(const Node* a, const Node* b)
    {
        int cnt_a = count_if(a-&amp;gt;next.begin(), a-&amp;gt;next.end(), IsUnvisited());
        int cnt_b = count_if(b-&amp;gt;next.begin(), b-&amp;gt;next.end(), IsUnvisited());
        return cnt_a &amp;lt; cnt_b;
    }
};

class KnightTour {
private:
    int nrow, ncol;
    vector&amp;lt;pair&amp;lt;int, int&amp;gt; &amp;gt; moves;
    vector&amp;lt;Node*&amp;gt; nodes, tour, best_tour;
    bool is_closed;

public:
    KnightTour(int r, int c, bool closed) : nrow(r), ncol(c), is_closed(closed)
    {
        // Knight can move two horizontally and one vertically,
        // or one horizontally and two vertically.
        moves.push_back(make_pair( 2,  1));
        moves.push_back(make_pair( 1,  2));
        moves.push_back(make_pair( 2, -1));
        moves.push_back(make_pair( 1, -2));
        moves.push_back(make_pair(-2,  1));
        moves.push_back(make_pair(-1,  2));
        moves.push_back(make_pair(-2, -1));
        moves.push_back(make_pair(-1, -2));
    }
    void search(Node* node);
    void run(int start_pos);
    void print();
};

void KnightTour::search(Node* node)
{
    if (node-&amp;gt;visited) return;
    if (best_tour.size() == nrow * ncol) {
        // for Closed Knight's Tour
        if (is_closed) {
            if (find(best_tour.back()-&amp;gt;next.begin(), best_tour.back()-&amp;gt;next.end(), best_tour.front()) != best_tour.back()-&amp;gt;next.end()) return;
            for (vector&amp;lt;Node*&amp;gt;::iterator p = best_tour.back()-&amp;gt;next.begin(); p != best_tour.back()-&amp;gt;next.end(); ++p) {
                vector&amp;lt;Node*&amp;gt;::iterator q = find(best_tour.begin(), best_tour.end(), *p) + 1;
                vector&amp;lt;Node*&amp;gt;::iterator r = find(best_tour.front()-&amp;gt;next.begin(), best_tour.front()-&amp;gt;next.end(), *q);
                if (r != best_tour.front()-&amp;gt;next.end()) {
                    reverse(q, best_tour.end());
                    return;
                }
            }
            best_tour.clear();
        }
        return;
    }
    node-&amp;gt;visited = true;
    tour.push_back(node);
    if (best_tour.size() &amp;lt; tour.size()) best_tour = tour;
    // Warnsdorff's algorithm
    sort(node-&amp;gt;next.begin(), node-&amp;gt;next.end(), LessMovable());
    vector&amp;lt;Node*&amp;gt; next(node-&amp;gt;next);
    next.erase(remove_if(next.begin(), next.end(), IsVisited()), next.end());
    if (!next.empty())
        next.erase(remove_if(next.begin(), next.end(), NotEqualUnvisited(next.front())), next.end());
    for (vector&amp;lt;Node*&amp;gt;::iterator p = next.begin(); p != next.end(); ++p)
        search(*p);
    node-&amp;gt;visited = false;
    tour.pop_back();
}

void KnightTour::run(int start_pos)
{
    // initialization for nodes
    for (int i = 0; i &amp;lt; nrow; i++)
        for (int j = 0; j &amp;lt; ncol; j++)
            nodes.push_back(new Node(i, j));
    for (vector&amp;lt;Node*&amp;gt;::iterator p = nodes.begin(); p != nodes.end(); ++p) {
        for (vector&amp;lt;pair&amp;lt;int, int&amp;gt; &amp;gt;::iterator q = moves.begin(); q != moves.end(); ++q) {
            int r = (*p)-&amp;gt;row + q-&amp;gt;first;
            int c = (*p)-&amp;gt;col + q-&amp;gt;second;
            if (r &amp;gt;= 0 &amp;amp;&amp;amp; r &amp;lt; nrow &amp;amp;&amp;amp; c &amp;gt;= 0 &amp;amp;&amp;amp; c &amp;lt; ncol)
                (*p)-&amp;gt;next.push_back(nodes[r*ncol+c]);
        }
    }
    // Schwenk's theorem
    if (is_closed &amp;amp;&amp;amp; (nrow * ncol % 2 == 1 || (min(nrow, ncol) == 2 || min(nrow, ncol) == 4)
        || (min(nrow, ncol) == 3 &amp;amp;&amp;amp; (max(nrow, ncol) == 4 || max(nrow, ncol) == 6 || max(nrow, ncol) == 8))))
        return;
    if (!is_closed &amp;amp;&amp;amp; nrow * ncol % 2 == 1 &amp;amp;&amp;amp; start_pos % 2 == 1) return;

    search(nodes[start_pos]);
}

void KnightTour::print()
{
    if (best_tour.size() &amp;lt; nrow * ncol) {
        cout &amp;lt;&amp;lt; "No solution." &amp;lt;&amp;lt; endl;
        return;
    }
    vector&amp;lt;vector&amp;lt;int&amp;gt; &amp;gt; board(nrow, vector&amp;lt;int&amp;gt;(ncol));
    int cnt = 1;
    for (vector&amp;lt;Node*&amp;gt;::iterator p = best_tour.begin(); p != best_tour.end(); ++p)
        board[(*p)-&amp;gt;row][(*p)-&amp;gt;col] = cnt++;
    int width = static_cast&amp;lt;int&amp;gt;(log10(static_cast&amp;lt;double&amp;gt;(nrow * ncol)) + 2);
    for (int i = 0; i &amp;lt; nrow; i++) {
        for (int j = 0; j &amp;lt; ncol; j++) cout &amp;lt;&amp;lt; setw(width) &amp;lt;&amp;lt; board[i][j];
        cout &amp;lt;&amp;lt; endl;
    }
}

int main(int argc, char* argv[])
{
    if (argc &amp;lt;= 2) {
        cerr &amp;lt;&amp;lt; "Usage: " &amp;lt;&amp;lt; argv[0] &amp;lt;&amp;lt; " nrow ncol [start_row=0 start_col=0] [closed]" &amp;lt;&amp;lt; endl;
        exit(1);
    }

    int nrow = atoi(argv[1]);
    int ncol = atoi(argv[2]);
    int r = 0, c = 0;
    if (argc &amp;gt; 4) {
        r = atoi(argv[3]);
        c = atoi(argv[4]);
    }
    bool is_closed = false;
    if (argv[argc-1][0] == 'c') is_closed = true;

    KnightTour kt(nrow, ncol, is_closed);

    kt.run(r * ncol + c);
    kt.print();

    return 0;
}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;

&lt;strong&gt;show_knight.py&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;

&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;#!/usr/bin/env python

import sys, os, Image, ImageDraw

def draw_board(board, nrow, ncol, size, offset_x, offset_y, out_image):
    im = Image.new("RGB", size)
    im.paste((255, 255, 255))
    draw = ImageDraw.Draw(im)
    for i in range(0, size[0], offset_x):
        draw.line((i, 0, i, size[1]), fill=0)
    for i in range(0, size[1], offset_y):
        draw.line((0, i, size[0], i), fill=0)
    for i in range(0, size[0], offset_x):
        for j in range(0, size[1], offset_y):
            if (i // offset_x + j // offset_y) % 2 == 1:
                im.paste((190, 190, 190), (i + 1, j + 1, i + offset_x, j + offset_y))
    for i in range(nrow * ncol - 1):
        draw.line((board[i] % ncol * offset_x + offset_x // 2, board[i] // ncol * offset_y + offset_y // 2, board[i+1] % ncol * offset_x + offset_x // 2, board[i+1] // ncol * offset_y + offset_y // 2), fill=(255, 0, 0))
    if sorted([abs(board[nrow*ncol-1] % ncol - board[0] % ncol), abs(board[nrow*ncol-1] // ncol - board[0] // ncol)]) == [1, 2]:
        draw.line((board[nrow*ncol-1] % ncol * offset_x + offset_x // 2, board[nrow*ncol-1] // ncol * offset_y + offset_y // 2, board[0] % ncol * offset_x + offset_x // 2, board[0] // ncol * offset_y + offset_y // 2), fill=(255, 0, 0))
    im.save(out_image)

def main(args):
    if len(args) &amp;lt; 3:
        print &amp;gt;&amp;gt;sys.stderr, "Usage: %s in_datafile out_imagefile [size_x=400 size_y=400]" % os.path.basename(args[0])
        sys.exit(1)

    in_file, out_image = args[1:3]
    if len(args) &amp;lt; 5: size = (400, 400)
    else: size = map(int, args[3:5])
    data = [map(int, l.strip().split()) for l in file(in_file)]
    nrow, ncol = len(data), len(data[0])
    board = range(nrow * ncol)
    cnt = 0
    for r in data:
        for c in r:
            board[c-1] = cnt
            cnt += 1
    offset_x, offset_y = size[0] // ncol, size[1] // nrow
    size = (offset_x * ncol + 1, offset_y * nrow + 1)
    draw_board(board, nrow, ncol, size, offset_x, offset_y, out_image)

if __name__ == "__main__": main(sys.argv)&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-2228205039654622759?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/2228205039654622759/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=2228205039654622759' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/2228205039654622759'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/2228205039654622759'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/01/c.html' title='C++: 騎士の巡歴と周遊'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_BwIshVLoLIU/S0vF7_tJcII/AAAAAAAAAPU/RhG_Z-WpELY/s72-c/knight_200x200s.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-4623854980330349001</id><published>2010-01-01T17:49:00.004+09:00</published><updated>2010-01-01T18:07:00.815+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='雑記'/><category scheme='http://www.blogger.com/atom/ns#' term='Blog'/><title type='text'>2010年を迎えて</title><content type='html'>明けましておめでとうございます。&lt;br /&gt;&lt;br /&gt;

今年でこのブログも4年目になるなぁ。早いものだ。自分が初めてインターネットに触れたのが1994年で、それ以前はNIFTY-ServeやPC-VAN、草の根BBSなど利用していた。インターネットを利用し始めたころはメールアドレスを持っていても周りが誰も使っていなかったので全くの無用の長物だった。その頃、みんなが電子メールを持つようになれば素晴らしい世界になるのにと考えたものだが、それから数年でその「素晴らしい世界」になったわけだ。&lt;br /&gt;&lt;br /&gt;

自分のウェブサイトを作ったのが1996年から、ブログを書き始めたのは2000年2月14日からだけど、最初の頃はHTMLを直接編集していた。その後、Movable Typeに移行してしばらく使っていた。それからいくつかのサイトやブログなどに手を出し、科学情報ばかりを扱った「科学随想録」や以前ハマったMMOのEverQuestのギルド用フォーラムを作成したりしつつ、今の「良いもの。悪いもの。」に落ち着いた。&lt;br /&gt;&lt;br /&gt;

このブログはプログラミング関連情報をメインとして扱っているけど、昨年は、Google Wave、Android、Twitter、mixiアプリなどが興味深かったな。今年はどんな技術が出てくるんだろう。今からワクワクする。ブログ記事は自分が楽しめるものを書く、書きたい時が書き時というスタンスだけど、他の人にも楽しんでもらえるとしたら嬉しい。因みに自分のプログラミングスタイルの変遷は「&lt;a href="http://handasse.blogspot.com/2009/06/blog-post.html "&gt;プログラミングができるということは、人生を楽にできるということ&lt;/a&gt;」に書いてある。&lt;br /&gt;&lt;br /&gt;

これからもどうぞよろしくお願いします。&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6153301078335336992-4623854980330349001?l=handasse.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://handasse.blogspot.com/feeds/4623854980330349001/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6153301078335336992&amp;postID=4623854980330349001' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4623854980330349001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6153301078335336992/posts/default/4623854980330349001'/><link rel='alternate' type='text/html' href='http://handasse.blogspot.com/2010/01/2010.html' title='2010年を迎えて'/><author><name>nox</name><uri>http://www.blogger.com/profile/01142433117618538300</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6153301078335336992.post-3331380502074778880</id><published>2009-12-25T00:42:00.013+09:00</published><updated>2010-10-19T02:42:45.542+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><title type='text'>日本全国コンビニ店舗分布地図: 高解像度インタラクティブ版</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_BwIshVLoLIU/SzOPxZB4WXI/AAAAAAAAAO0/_mIGZ05a1Fk/s1600-h/conv01.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 191px; height: 200px;" src="http://3.bp.blogspot.com/_BwIshVLoLIU/SzOPxZB4WXI/AAAAAAAAAO0/_mIGZ05a1Fk/s200/conv01.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5418832855351974258" /&gt;&lt;/a&gt;
&lt;a href="http://handasse.blogspot.com/2009/12/processing.html"&gt;前回&lt;/a&gt;に引き続き今回も&lt;a href="http://processing.org/"&gt;Processing&lt;/a&gt;による日本全国コンビニ店舗分布地図について書いてみる。前回は大きさが固定された地図で拡大・縮小や移動ができなかったので、高解像度インタラクティブ版として、それをできるようにしてみた。&lt;br /&gt;&lt;br /&gt;
 
まず、日本地図がPNG画像なのでこれをベクタ画像に変更する。フォーマットはSVGだ。前回と同様に「&lt;a href="http://www.kabipan.com/geography/whitemap/index.html"&gt;カビパン男と私&lt;/a&gt;」で提供されている日本地図のSVGファイルを若干加工して利用させてもらった。&lt;br /&gt;&lt;br /&gt;
 
次にこのSVGデータをProcessingで利用する方法について述べる。「&lt;a href="http://www.amazon.co.jp/gp/product/4873113784?ie=UTF8&amp;tag=footawwwservi-22&amp;linkCode=as2&amp;camp=247&amp;creative=1211&amp;creativeASIN=4873113784"&gt;ビジュアライジング・データ&lt;/a&gt;&lt;img src="http://www.assoc-amazon.jp/e/ir?t=footawwwservi-22&amp;l=as2&amp;o=9&amp;a=4873113784" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;」にもSVGの利用方法(processing.candy.*)が書いてあるのだが、実はここに書いてある情報は古くて現在では使用できない。現在では、PShapeを使ってSVGを利用する。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;PShape mapShape = loadShape("japan.svg");&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
こんな感じだ。画面の(x, y)座標に表示するにはdraw関数内で以下のように記述すればいい。&lt;br /&gt;&lt;br /&gt;
 
&lt;span style="font-family:courier new,monospace;font-size:85%;white-space:pre;color:#0000ff;"&gt;shape(mapShape, x, y);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
 
詳しくは&lt;a href="http://processing.org/reference/PShape
