fc2ブログ

本を読む

読書やコンピュータなどに関するメモ

Re: シェルで複数のコマンドを1個のコマンドのように扱う

複数のコマンドを1個のコマンドのように扱いたくて、シェル関数を書くほどのことでもないなら eval が便利。

 evalで問題はないと思いますが、波括弧のほうがより意図に近いかと。

$ cat hoge.sh
#!/bin/sh
[ -z "$1" ] && { echo none; exit 1; }
echo processing $1
$ sh hoge.sh
none
$ sh hoge.sh something
processing something

 POSIX準拠。Linux、Mac OS X、Cygwinで動作確認。

追記2008-12-01:

 なぜ「evalで問題はないと思いますが」と入れたかというと、4.2BSDあたりでBourne Shellのシェルスクリプトを覚えたのかなと思ったので。その意味は単なる昔話なので略。

楽天でROMAとfairyの話を聞いてきた

 火曜日のHadoopの話に続き、土曜日には「楽天テクノロジーカンファレンス」でROMAとfairyの話を聞いてきました。fairyはようするに楽天版MapReduce、ROMAは楽天版memcached(よりストレージ寄り)という感じのソフトです。

 以下、メモ。

レコメンド&パーソナライゼーション(楽天技研 西岡氏)

(emasaka注:これはROMAとfairyのセッションの直前のセッションだけど、関連性が強いので、関連する部分のみ切り出し)

 楽天でレコメンデーションをやる場合、規模が膨大という特徴がある。何千万もの会員と2,500万の商品の組み合わせ。その対策として、Locality Sensitive Hashingなどアルゴリズムを改良すると同時に、多数のマシンを組みあわせている。そのために、現時点ではHadoopを使ってMapReduce処理をし、k-meansのクラスタリングや機械学習に導入している。台数に応じてスケールすることが確認できた。「現時点では」というのは、楽天技研ではROMAとfairyを開発しているので、今後使っていきたい。

fairy(楽天技研 増田氏)

 よくいわれるように、世の中のデータ量が爆発的に増えている。それに対応する技術を各社が開発している。GoogleはGFSやMapReduceにより、1日20petaバイトを処理しているという。Yahoo!はHadoopの開発に関わり、利用している。Amazonにも、Dynamoという分散key-valueのシステムがある。ベンダーだが、OracleにもCoherenceという製品がある。

 そういう楽天のデータや会員数も爆発している。そこで、まつもとゆきひろさんと共同研究してROMA/fairyを開発中。Rubyを使ったソリューション。まつもとさんの「Rubyを大規模に使いたい」という意欲と楽天技研の「大量のデータを効率的に処理したい」という要望が合わさってスタートした。オープンソース化に向けて開発している。

 fairyは分散並列処理フレームワークで、分散並列処理を複数のハードウェアで同時並行処理する。特徴(目標)は、3つ。1つめはスケーラブルなこと。マシンを追加すればどんどん計算力を増やせるようにする。将来的にはJRubyにも対応する。

 2つめはプログラマフレンドリー。Rubyの生産性を活かし、より自然で簡潔にプログラミングできるようにする。3つめは(リーズナブルな)パフォーマンス。Rubyは速いとはいわれていないが、データをメモリに置いたり、ネットワークの転送量を減らしたりといった工夫をする。

 fairyでは、inputからoutputの間を(MapReduceのmapやshuffle、reduceに相当する)多段の「フィルター」で処理する。MapReduceやHadoopで毎度おなじみのワードカウントの例でいうと、「単語: 1」に分解するfmap、単語ごとにグルーピングするgroup_by、グループごとにカウントするsmapで実現できる。group_byのあとは、単語ごとにマシンが分散される。割り当てはfairyがやる。

(ここでMapReduceでいうとSawzallに相当すると思うRubyソースを見せる。require 'fairy'とかある)

 フィルタのメソッドは、ブロックの第1引数が入力I/O、第2引数が出力I/O。

 fairyには3種類のマシンが関与する。リクエストをサーバーに投げる「クライアント」、分散された処理を実行する「ノード」、ノードの制御やリクエストの受付をする「マスターサーバー」。

 また、データは仮装分散ファイルVFile(GFS相当?)に置かれる。1つのデータを分割して配置する。アクセスするときには分散配置の情報を得てから。まだ仕様は固まっていない。

 楽天らしい使い方でいうと、併買データを処理する場合、実際のデータは数千万件×数千万件のかけあわせになる。そこで、購買アイテムの全組み合わせを作るfmapと、組み合わせごとに分解するgroup_by、集計するsmapで処理できる。

 ただし現状のfairyは、基本的な処理は実装して動作するものの、実データでの検証とパフォーマンスチューニングはこれから。

ROMA(楽天技研 西沢氏)

 ROMA開発の背景として、大量のデータを格納できる基盤システムの必要性がある。大量のトラフィックに耐え、耐障害性のあるもの。

 データベースはディスクI/Oがボトルネックになる。かといって、データをメモリ上に置くと、速いが量を置けないし、障害で消失する危険性がある。

 ROMAでは高負荷でも高速アクセスでき、データを消失しないシステムを狙った。

 特徴は、複数のマシンから構成されるオンメモリストレージで、インターフェイスはkey-valueのハッシュテーブル。クライアントAPIを利用してアクセスするようになっていて、現在JavaとRubyに対応。開発者に分散を意識させない。

 分散配置により、データアクセスが集中化してボトルネックになるようなことがないようにする。データは多重化して障害による消失を回避する一方、データの一貫性を重視する(memcachedとの違い)。また、動的にマシン(ノード)を追加できる。

 アーキテクチャはP2Pで、各ノードが自律的に状態(ノードの追加やダウン)を管理する。ノード間は環状のモデルで接続される。

 ノードはユニークなID(ハッシュ値)を持つ。クライアントが読み書きするときには、まずハッシュ値を計算して担当ノードを割り出し、担当ノードにアクセスするようになっている。パフォーマンスのために、データの探索はサーバー側ではなくクライアントがやる。クライアントは最初に環の情報を全部取得するようになっている。

 データが書き込まれるときは、担当ノードが自動的に左右(ハッシュ値の近さで)のノードに対してレプリケートする。一貫性を保つため、セカンダリーにデータが転送されるまでクライアントは待つ。

 ROMAへノードの追加するときは、まずノードのマシン上でROMAのプロセスを起動する。すると、自分のハッシュ値を計算し、ブロードキャストする。それを受けて、近いハッシュ値のノードや、メモリの少ないノードが、新ノードにアプローチする。アプローチが早い順に、その隣に参加する。

 各ノードには、p(自分の担当データ)、ls(左ノードのデータ)、rs(右ノードのデータ)の3つのデータ領域がある。新しいノードが追加されると、まずlsとrsをコピーしてきてリハッシュ、これで環に所属する。あとはじわじわ環全体に所属情報が伝わる。

 今後の課題。データを永続化をしてディスクに保存し、障害時のリカバリーに使う技術を考えている。

 また、パフォーマンスチューニングも重要。たとえば、RubyのGCにかかるオーバーヘッドが問題なので、解消したい。案としては、Rubyの拡張ライブラリとして実装して、そこにデータを格納し、メモリは自主管理するというの考えもある。パフォーマンス面では、パフォーマンスと一貫性はトレードオフなので、さまざまなモデルを追加し、アプリケーションによって、一貫性をゆるめてパフォーマンスをあげることもできるようにすることも考えている。

 オープンソース化は、来年の予定。ソースを公開するだけではなく、大規模処理や高速化についての知見(GCとパフォーマンスなど)をRubyコミュニティに還元したい。

GREE LabsでHadoopの話を聞いてきた

 GREEさんで不定期でやってる、GREE Labsオープンソーステクノロジー勉強会で、Hadoopの話を聞いてきました。Hadoopは、つまりはGoogleのGFSやMapReduceのクローンだそうで、「Googleを支える技術」にトキメいた人なら必見ですね。

 発表は、技術面を簡潔に押さえたうえでわかりやすく、そのうえ実際の利用事例の話も聞けたのが面白かったと思います。最近のWeb系では、サービス面でもマネタイズ面でも、データマイニングとか行動ターゲティングとかがアツいんだなぁと思いました。

 プレゼン資料もust録画も公開されていますが、以下、自分のメモという意味で記録しておきます。

Hadoopについて(太田一樹)

 Preferred InfrastructureのCTOで、Sedueの作者。大量のデータの処理がテーマで、半分は酒でできているw。そんなこんなで、はてブ検索でも使われている技術を作った。

 HadoopとはGoogleの基盤ソフトウェアのクローンで、GFSやMapReduceを実装。ていうか、作者はGoogleの論文を見て実装した。ぜんぶJava。実装を見て、Yahoo! Reserchが作者をスカウトしたんだとか。ちなみに、ぬいぐるみの名前が由来。

 参考としては、Codezineに記事を書いたのでそれとか、NTTレゾナントと共同で、実用性まで解析した資料を公開したとか。

 まず問題として、インターネットの爆発的普及により、超大規模なデータが蓄積されている。超すごい。読み込むだけで100日かかったりする

 なので、とにかく大量のマシンを用意したりするけど、起動、監視、通信、デバッグ、最適化など、プログラミングが非常に困難になる。

 スパコンの世界では、MPIという、並列プログラミングのためのライブラリが主流だったりする。だけど、耐障害性への考慮が少ないし、プリミティブな処理しかしてくれない。

 そこでMapReduce。めんどうなことをやってくれるフレームワーク。ジョブはよく死ぬが、処理は続く。

 MapReduceは、Map、Shuffle、Reduceの3つのフェーズで動く。入力は、keyとvalueのペア、つまりハッシュ。ワードカウントの例でいうと、入力で1単語ずつ入ってきたものが、MapperのMapフェーズで1単語ごとに「foo: 1」みたいな単位に分解される。ReducerのShuffleフェーズがキーごとに同じReducerにまとめて分散、それを受けてReducerのReduceフェーズが加算して、まとめるとワードカウントが完成する。

 MapReduceがうまくはまる処理には、grep、sort、ログ解析、Webのページ構造のグラフ化、転置インデックス作成、機械学習(つまりはてブの自動分類など)がある。機械学習については、ApacheのMahoutライブラリも登場してwktk。

 Hadoopは、GFS相当のHadoop Distibuted File System(HFS)とHadoop MapReduceからなる。HFSは、MasterとSlaveからなり、ブロック単位(デフォルト64MB)で情報を管理。MasterはNameNodeと呼ばれ、メタデータを管理する。SlaveはDataNodeと呼ばれ、データ本体を管理する。NameNodeがsingle point of failerや負荷になる危険性もある。

 Hadoop MapReduceは、これもMaster - Slave構成。ジョブを割りふるJobTrackerと、各ジョブを実行するTaskTrackerからなる。JobTrackerは、TaskTrackerを監視して、死んだノードの処理はほかに振り直すといったこともやる。

 Hadoop自体はJavaで作られてるけど、Hadoop Streamingというのもある。標準入出力をインターフェイスとしてMapReduceのノードを作れ、map.rbとかreduce.rbとか書ける。

 実例としては、まずもちろんYahoo!。2,000ノードぐらいのシステムを利用して、検索、広告、ログ処理、データ解析など、Information Retrievalに使っている。

 AmazonやFacebookも、400ノードぐらいのシステムで使っている。ほか、行動ターゲティングの解析をしている会社が使っている。

 国内では、Preferred Infrastructureが新はてブの解析システムに導入した。ほか、相談をもらうケースでは、Luceneを高速化したいとか、ログ処理が遅いとか、CellのクラスターでSPUにノードを割り当てている人とかがある。

  • Q: 分散ストレージとしてだけ使っている事例はあるか
    • A: 某キャリアの事例
  • Q: Hbase(tableのやつ)はどうか
    • A: 動きませんでした
  • Q: テキスト処理がメイン?
    • A: はい。ただ最近Facebookが、Hiveという、SQLの裏でMapReduceするのをやっている
  • Q: Hadoopにする処理の目安
    • A: 10Gや20Gなど、Luceneでは何十時間かかるデータ
  • Q: GFSはファイルのappendができるがHadoopはできないとか。ログ解析はappendだと思うが
    • A: たとえば1G単位とかで処理する。あと、0.19ではappendが追加された。
  • Q: MapReduceは可用性より分散?
    • A: 両方あるとは思うが、可用性はまだそれほど実例がでていないので(Yahoo!さんしだい?)
  • Q: DBで大きなデータがあって、Hadoopで解析するとき、同期は
    • A: 大きなデータでは同期はしない。たとえば数時間に1度ダンプしてきて処理するようにする
  • Q: 1台でできる処理をHadoopにしたとき、性能は
    • A: 単体で済む処理はそちらが速い。起動などでオーバーヘッドがある(10~20秒程度)。そのオーバーヘッドを乗り越えられる処理はHadoopで

HadoopとEC2による「安くて簡単」大規模データ処理(Tsutomu Okura:大倉努)

 日本中のブログを収集し、著者のプロフィールを推測し、各属性で話題になっているキーワードを分析する「blogeye」を開発した人。Haddoop+Amazon EC2+Amazon S3を使いまくっている。

 もともとデータマニングアルゴリズムを研究していて、適用対象としてブログを選んだ。日本のブログは1千万ちょっとで、blogeyeを1年やって2億記事ぐらい集まった。これを処理するのに、学生なのでお金も手間もかけられない。でもアルゴリズムは手軽に試したい。そこでHadoop+EC2+S3。導入コストは0。従量制だけど、EC2からS3の読み書きはタダ。

 Hadoopは動的にノードの追加や削除ができるのがいい。大きなジョブがあるときだけECのノードを増やせる。HadoopのS3ライブラリもあり、安定している。

 blogeyeでの利用。S3は読み書きが遅いので、キャッシュやクロールデータはMySQLに。属性推定ジョブにHadoopを使いまくり。普段は4ノードだけど、属性推定ジョブを動かすときには80ノード。

 クロールは、レスポンスがこなかったりとか、大量のレスポンスが来たりとか、危ない処理。なので、MasterではやらずSlaveにして、死ぬときにはそのスレッドだけにする。

 インデックスはMySQL+Senna。クエリーはフロントエンドにインデックスのレプリカを置いてそこで受ける。できるだけ多くの処理をインデクシングで並列に済ませておく。

 なんといっても一番がんばってるのが著者属性推定ジョブ。MapReduceを使って、ページ単位からサイト単位にデータ単位を変換、サイトをキーにしたページのハッシュを作り。それをReduceで処理する。80ノードで2日かかり、Amazon代が4万円。

 Hadoopは優先度を設定できるようになったので、たとえば、最新記事をリスティングするサイトは、時間をはずせないので優先度をあげるといったことができる。

 困ったところは、HadoopのデフォルトではMapと同時にReduceも開始されること。ジョブによってMapの処理時間が違うので、Reducerが終わっても次のReduceが始められないということにもなる。というわけで「スケジューラーをいじってみるといいかも」とか(やってるということかな?)。

  • Q: Shuffleフェーズは?
    • A: Mapperでは各データをコンテナに入れてReducerに送る。Reducerが定期的にMapperに問い合わせて、そろったデータがあれば転送されて処理する
  • Q: 学習アルゴリズムは、最初は人力? 文体とかで判断?
    • A: 両方とも、はい。Hadoopの中では「この単語があれば何パーセントで女性」のような、いちど学習させたデータを元にしている
  • Q: 80ぐらいのノードで、EC2とS3のバランスは
    • A: 実はバランスをとっていない。サイズを自由に変えられる。EC2にデータを持つと落ちたときに消えるので、念のためすべてS3に
  • Q: いままでいくらぐらいAmazonに貢いだかw
    • A: 1年で40万円ぐらい。いまはとある企業のサーバーに
  • Q: 著者属性推定処理のためだけにHadoop?
    • A: ふだんはクローラーや、各種アルゴリズムの研究。同じクラスター内で
  • Q: EC2のVMには種類がいろいろあるが、どれを?
    • A: 1年前は1種類しかありませんでしたw。たぶん速いマシンでも意味はない…そんなことはないかなぁ…場合に応じて(悩む)。あと、1台のマシンで複数のジョブを走らせたほうがいい。CPUをよく使うジョブと、I/Oをよく使うジョブとをまぜる。
  • Q: 台数を増やすのは簡単?
    • A: スクリプト一発でできた
  • Q: Hadoopの障害や欠点
    • A: 1年で2回、Hadoopを再起動した。マスターでクローラを動かしてハマったときと、もう1回は忘れたw。優先度を設定できなかったのが最初困ったけど、さっき言ったように、できるようになった。あとは、これもさっき言ったように、Reducerの起動を変えるオプションはあってもいい。そうそう、ログが巨大になりローカルディスクがいっぱいになるのは注意。
  • Q: これじゃなくちゃと思った理由
    • A: 計算力の必要なときとそうでないときの差が大きいこと。

Perlでカラム数単位のlengthとsubstrを作ってみる

 1バイト文字とキャラクター端末の時代には、文字数とバイト数、画面上の桁数(カラム数)の3つは同じでした。シフトJISや古いEUC-JPの時代にも、文字数はともかく、バイト数とカラム数は(偶然?)同じでした。

 現在では、これらは異なります。たとえばPerlで「あ」を内部コード表現にした場合、1文字/3バイト/2カラムとなります。

 「graph-easyでテキストのハコ図をらくらく生成」で紹介したPerlのGraph::Easyモジュールは、内部コードの1文字=1カラムを前提にして、lengthとsubstrでテキストのハコ図を描いています。lengthもsubstrも文字数単位で数える仕様です。そのため、日本語ではカラムがずれてしまいます。

 そこで、カラム単位で処理するlengthとsubstr相当のルーチンを作って置きかえてみました。Unicode::EastAsianWidthモジュールを使っています。方針は次のとおり。

  • lengthやsubstrとさしかえても、1バイト文字での挙動は変わらないこと(substrの左辺値には対応できませんでしたが)
  • 1バイト文字の場合にできるだけオーバーヘッドが少ないこと

 けっこう意外だったのが、「ü」がEast Asian WidthでAmbiguousになっていることでした。最初「■」などを考えてAmbiguousをデフォルトで全角扱いにしていたのですが、Graph::Easyのテストケースでは「ü」が1カラムとして扱われる想定になっていて、エラーになってしまいました。

 とりあえずGraph::Easy内のモジュールとして作ったものを下に貼ります。独立したモジュールにしてもいい気もしますが、気のせいかもしれません。

 Graph::Easy全体へのパッチは pastebin(テキスト共有サイト)に期限1か月で貼っておきます。人柱希望のかたはどうぞ。基本的に、lengthとsubstrを置き換えているだけですが、テストケースは通っています。本家に取り込んでもらったほうが楽ではあるものの、日本語の問題を説明するのは正直面倒だなぁ。

package Graph::Easy::UnicodeColumn;
use strict;
use warnings;
use Unicode::EastAsianWidth;

my @EXPORT = qw( columnlength columnsubstr );

use constant FULLWIDTH_WITH_AMBI_RE
    => qr/[\p{InFullwidth}\p{InEastAsianAmbiguous}]/;
use constant FULLWIDTH_WITHOUT_AMBI_RE => qr/\p{InFullwidth}/;

my $FULLWIDTH_RE = FULLWIDTH_WITHOUT_AMBI_RE;

sub import {
    no strict 'refs';
    my $module = shift;
    my %opt = @_;
    my $callpkg = caller;
    *{"${callpkg}::$_"} = \&$_ for (@EXPORT);

    if (exists $ENV{'UNICODECOLUMN_AMBIWIDTH'}) {
        if ($ENV{'UNICODECOLUMN_AMBIWIDTH'} eq 'double') {
            $FULLWIDTH_RE = FULLWIDTH_WITH_AMBI_RE;
        }
    } elsif (exists $opt{'ambiwidth'} && $opt{'ambiwidth'} eq 'double') {
        $FULLWIDTH_RE = FULLWIDTH_WITH_AMBI_RE;
    }
}

sub columnlength {
    my $str = shift;
    my $lenw = 0;
    while ($str =~ m/${FULLWIDTH_RE}+/g) {
        # strings without fullwidth character never step into this loop
        $lenw += length($&);
    }
    $lenw + length($str);
}

sub columnsubstr {
    if ($_[0] =~ /\P{InBasicLatin}/) {
        columnsubstr_mb(@_);
    } else {
        # if no mult-byte-characer, just call substr (for efficiency).
        if (exists $_[3]) {
            substr($_[0], $_[1], $_[2], $_[3]);
        } else {
            substr($_[0], $_[1], $_[2]);
        }
    }
}

sub columnsubstr_mb {
    my ($str, $pos, $len, $replace) = @_;
    die if ($len <= 0);
    my @ary = split //, $str;
    my $reversed;

    if ($pos < 0) {
        @ary = reverse(@ary);
        $pos =  -1 - $pos;
        $reversed = 1;
    }

    my $sum;
    my $start = 0;
    for ($sum = 0; $sum < $pos; $start++) {
        $sum += ($ary[$start] =~ /${FULLWIDTH_RE}/ ? 2 : 1 );
    }
    splice(@ary, $start++, 1, (' ', ' ')) if ($sum > $pos);

    my $end = $start;
    for ($sum = 0; $sum < $len; $end++) {
        $sum += (defined($ary[$end]) &&
                 $ary[$end] =~ /${FULLWIDTH_RE}/ ? 2 : 1 );
    }
    $end--;
    splice(@ary, $end, 1, (' ', ' ')) if ($sum > $len);

    my $ret;
    if (defined wantarray) {
        my @part = @ary[$start..$end];
        $ret = join('', $reversed ? reverse(@part) : @part);
    }
    if (defined $replace) {
        splice(@ary, $start, $end - $start + 1, ($replace));
        $_[0] = join('', $reversed ? reverse(@ary) : @ary);
    }
    $ret;
}

1;

__END__

=head1 NAME

Graph::Easy::UnicodeColumn - subroutines to handle colums of unicode string

=head1 SYNOPSIS

    use Graph::Easy::UnicodeColumn;
    # or
    use Graph::Easy::UnicodeColumn ambiwidth => 'double';

=head1 DESCRIPTION

=over 4

=item columnlength STRING

return how many columns contains in STRING.

=item columnsubstr STRING, OFFSET, LENGTH

=item columnsubstr STRING, OFFSET, LENGTH, REPLACEMENT

Similar to substr, but OFFSET and LENGTH is given as column number.
Also, columnsubstr can't be lvalue.

=back

=head1 "AMBIGUOUS" WIDTH

By default, characters that have "Ambiguous" East Asian Width is
treated as single column width.

When "ambiwidth => 'double'" is given to "use Graph::Easy::UnicodeColumn",
or environment variable UNICODECOLUMN_AMBIWIDTH is set to 'double',
characters that have "Ambiguous" East Asian Width is treated as
double column width.

=head1 ATENTION

Multibyte characters in strings must be utf-8 flagged.

=head1 COPYRIGHT

Copyright (c) 2008 Masakazu Takahasi L<http://emasaka.blog65.fc2.com/>.

=cut

「コペルニクスの仕掛人―中世を終わらせた男」

コペルニクスの仕掛人―中世を終わらせた男
デニス・ダニエルソン
東洋書林
売り上げランキング: 86394

 地動説のコペルニクスは、自説を世に伝えることにあまり積極的ではなかった。そのコペルニクスの唯一の弟子となり、コペルニクスの説を紹介する書籍「第一考察」を書き、コペルニクスが亡くなる直前に「天体の回転について」の出版にこぎつけさせた人物が、数学者のゲオルグ・ヨアヒム・レティクスである。レティクスは語られることも少なく、Wikipediaを見てもごく簡単に説明されているのみ。本書は、そのレティクスをおそらく初めて本格的に論じた評伝。

 本書によると、コペルニクスが亡くなったとき、レティクスは28才だった。それ以後のレティクスは、地動説を広める活動にいそしんだあと、イタリアでのカルダーノとの対立や精神疾患を経て、やがて同性愛スキャンダルで大学を追われてしまう。失意のレティクスだが、徐々に回復してゆくとともに三角法の研究に没頭し、三角法の金字塔である大著「三角形に関する宮殿の作品」が死後に発表される。

 そうした人間ドラマと同時に、放浪者で一匹狼気質のレティウスの行動を通じて、16世紀が学問でも社会でもパラダイムのシフトしていく時代だったことがわかる。本書でレティウスの活動に関係して語られたものを見ると:

  • 地動説
  • 近代三角法と、天体への適用
  • 宗教改革
  • チュートン騎士団の解体、プロイセン公国
  • 東ローマ帝国の終焉により、古代のラテン語文書がイタリアに移り、研究される
    • 翻訳と自然現象の解明の類似
  • アメリカ大陸
  • 確率論
  • パラケルススの毒性学(錬金術?)
  • 超新星の発見と観察、宇宙は変化のないものではない
  • 占星術と天文学、数学、医学が渾然一体

 ちなみに本書によると、グレシャムの法則として知られる「悪貨は良貨を駆逐する」を最初に書籍で論じたのはコペルニクスだそうな。

CPerlモードで正規表現のfont-lock処理が無限ループ

 Emacs 22でfont-lockを有効にし、CPerlモードで以下の内容を入力するか、ファイルを読み込んだら、無限ループで反応しなくなった。再現性は、毎回。

/[\p{Hiragana}](?{

 しかも、font-lock処理中でくらってるので、inhibit-quitのせいかC-gでも止まらない。プロセスをkillするか、ウィンドウを閉じて終了するはめになる。

 あとで調べる…かも。

Re: 0~1000に含まれる0をカウントする

 bashの(に限らないけど)コマンドラインで。

$ seq 0 1000|tr -d -c 0|wc -c
193

追記2008-11-21

 こっちのほうが簡潔だし直接的か。

$ seq 0 1000|grep -o 0|wc -l
193

「写楽百面相」

写楽百面相 (文春文庫)
泡坂 妻夫
文藝春秋
売り上げランキング: 404334

 亜愛一郎シリーズをまとめて読み返した勢いで読んだ。こちらは初読。

 町人文化と規制が拮抗する文化文政時代を舞台にした、ミステリー風味の時代小説。版元の若旦那を主人公に、女郎殺しの追求と東洲斎写楽の正体を追っていく。といっても、ストーリーは犯人探しが中心ではなく、各エピソードごとの謎や伏線、解決がそれぞれ泡のように浮んでは消えていき、互いに結びついたり結びつかなかったりという構成がニクい。ネタバレを避ける範囲でいうと、それらの要素を通じて、江戸城や朝廷の影を、庶民の視点から描く。

 ちなみに、趣向として数字尽しのうえ、各章には順に一~十の数字がついていたりもする。そうそう、本文の最後の2行はニヤリですな。

bashの内蔵コマンドを自作してみた

 bashでは、内蔵コマンドを個別に有効にしたり無効にしたりできます。さらに、共有ライブラリで内蔵コマンドを追加できる機能もあります。この機能には、あまり日本語のドキュメントがありませんが、ごく簡単な例で試してみました。以下にまとめてみますので、間違いや、よりよい方法などがありましたら、ご指摘ください。

内蔵コマンドにすると何がうれしい?

 普通の外部コマンドやシェル関数ではなく内蔵コマンドとして実装するメリットとしては、bashの内部の状態を操作できることがあります。そこで、ここではシェル変数に値を代入するコマンド「mysetvar」を追加してみます。「my」と付けたのは、「オレオレsetvar」とでもいうような意味です。

 mysetvarは、以下の形式で呼び出すことにします。

mysetvar 変数名 値

 「変数名=値」に比べると、変数名に変数展開などが適用されるという違いがあります。Lispでいえば、setqに対するsetのような。そこまで必要なシェルスクリプトは、あまり書かないとは思いますが。

前準備をする

 ここでは、Ubuntu Linux 8.10の環境で試してみます。UbuntuやDebianでは、bash-builtinsパッケージをインストールすると、必要なヘッダやサンプルをインストールしてくれます。

$ sudo apt-get install bash-builtins

 /usr/share/doc/bash/examples/loadables/の下にサンプルのソースが置かれます。ほぼこれがドキュメントがわりです。

 もちろん、Cコンパイラなどの開発環境も忘れずに用意しておきます。

Cのソースはこんな構造で書く

 さっそくコードを書きましょう。ここでは、mysetvar.cというファイル名で新規作成します。

 まず先頭で、bashの内蔵コマンドに必須のヘッダファイルを指定します。

#include <builtins.h>
#include <shell.h>

 ほかに、シェル変数を操作するためのヘッダファイルも指定します。

#include <variables.h>

 コマンドの本体は、以下のような関数になります。関数名は、「コマンド名_builtin」とつけるのが定番っぽいです。中身については後述します。

int mysetvar_builtin(WORD_LIST *list)
{
    /* 処理 */

    return EXECUTION_SUCCESS;
}

 続いて、ドキュメント文字列を定義します。bashで「help コマンド名」を実行すると、この次で出てくるusage文字列に続いて、ドキュメント文字列が表示されます。

char *mysetvar_doc[] = {
    "set VALUE to VARIABLE",
    (char *)NULL
};

 ドキュメント文字列は、1行ずつの文字列へのポインタの配列となっていて、最後はNULLポインタで終えます。ドキュメント文字列の変数名は、「コマンド名_doc」とつけるのが定番っぽいです。

 最後に、コマンドの情報をまとめた構造体を定義します。

struct builtin mysetvar_struct = {
    "mysetvar",		       	 /* コマンド名 */
    mysetvar_builtin,		 /* 本体の関数へのポインタ */
    BUILTIN_ENABLED,		 /* 初期状態 */
    mysetvar_doc,		 /* ドキュメント文字列へのポインタ */
    "mysetvar VARIABLE VALUE",	 /* usage文字列 */
    0	      	       		 /* 予約領域 */
};

引数と戻り値を見てみる

 では、改めて関数本体を見てみます。

int mysetvar_builtin(WORD_LIST *list)
{
    /* 処理 */

    return EXECUTION_SUCCESS;
}

 関数には、コマンドライン引数が展開されたデータがリスト構造で渡されます。そして、成功したらint型の値EXECUTION_SUCCESSを返します。

 ここで作るmysetvarは、2つの引数をとります。引数がない場合はusageを表示してエラー終了することにします。引数が0個の場合は、リスト構造が最初からNULLポインタになっているので、チェックします。

    if (list == NULL) {
        builtin_usage();
        return EX_USAGE;
    }

 そうでなければ、list->word->wordが1つ目の引数(の文字列へのポインタ)になります。さっそくCの変数varに入れてみます。

char *var = list->word->word;

 list->nextをたどると、2つ目の引数以降のリストになります。

    list = list->next;

 1つ目の引数と同じように、NULLポインタだったら2つ目の引数が指定されていないので、usageを表示してエラーにします。

    if (list == NULL) {
        builtin_usage();
        return EX_USAGE;
    }

 list->word->wordが2つ目の引数なので、Cの変数valに入れます。

    char *val = list->word->word;

シェル変数をいじる

 ようやくシェル変数を操作するコードを書きます。まず、1つ目の引数が変数名として問題ないかチェックします。

    if (! legal_identifier(var)) {
        sh_invalidid(var);
        return EXECUTION_FAILURE;
    }

 ではシェル変数に値をセットします。

    bind_variable(var, val, 0);

 最後に、シェルが特別扱いする変数であれば処理を実行するようにします。

    stupidly_hack_special_variables(var);

 これで完成です。

 ちなみに、メモリの確保や開放がこれでいいのかどうかは、ちょっと自身がありません。

改めてコード全体を見る

 以上で書いたmysetvar.cの内容を、改めて見てみましょう。

#include <builtins.h>
#include <shell.h>
#include <variables.h>

int mysetvar_builtin(WORD_LIST *list)
{
    if (list == NULL) {
        builtin_usage();
        return EX_USAGE;
    }
    char *var = list->word->word;

    list = list->next;
    if (list == NULL) {
        builtin_usage();
        return EX_USAGE;
    }
    char *val = list->word->word;

    if (! legal_identifier(var)) {
        sh_invalidid(var);
        return EXECUTION_FAILURE;
    }

    bind_variable(var, val, 0);
    stupidly_hack_special_variables(var);

    return EXECUTION_SUCCESS;
}

char *mysetvar_doc[] = {
    "set VALUE to VARIABLE",
    (char *)NULL
};

struct builtin mysetvar_struct = {
    "mysetvar",
    mysetvar_builtin,
    BUILTIN_ENABLED,
    mysetvar_doc,
    "mysetvar VARIABLE VALUE",
    0
};

コンパイルして共有ライブラリを作る

 できたmysetvar.cをコンパイルして、共有ライブラリmysetvar.soを作ります。

$ cc -I/usr/include/bash -fpic -shared -o mysetvar.so mysetvar.c

 ここで、/usr/include/bashディレクトリを指定しているのは、UbuntuやDebianでの指定だと思います。

実行してみる

 実行してみましょう。現在のbashで試すのは怖いので、サブシェルを起動します。

$ bash

 起動した新しいbashで、さきほどのmysetvar.soを読み込みます。それには、「enable -f ファイル名 コマンド名」を実行します。

$ enable -f ./mysetvar.so mysetvar

 ではmysetvarを実行します。

$ mysetvar a abc

 変数の値を表示してみましょう。

$ echo $a
abc

 うまくいったようです。

 念のため、readonlyした変数にmysetvarで値を設定してみます。

$ readonly b def
$ mysetvar b ghi
bash: b: readonly variable

 ちゃんとエラーになりました。

 最後にサブシェルを終了します。

$ exit

まとめ

  • bashの内蔵コマンドは、共有ライブラリから追加できる
  • 関数、ドキュメント文字列、struct builtinを定義する
  • enable -fで読み込む

「サブプライムを売った男の告白」

サブプライムを売った男の告白―欲とペテンと無知!
リチャード・ビトナー
ダイヤモンド社
売り上げランキング: 2383


 元サブプライム不動産ローン貸付業者(レンダー)が、当時の現場の様子を体験談で語る本。経済とか金融とかの大きな話じゃなくて(そういう話もあるけど)、顧客やブローカー(代理店)のデタラメな様子を、豊富なエピソードを元に語っていて、面白かった。

 たとえば、ブローカーの事務所が汚部屋状態で、ごみの山をひっくり返して書類を探し回らなくてはならないというエピソードとか。いちばん笑ったのが、クリーブランドにいるブローカーの事務所を訪問するエピソード。支社長がグリズリーのような大男で刑務所帰り、事務所のドアはバズーカーでもないと破れない装備、ナイフのコレクションが壁に飾られていて仕事中にナイフ投げ、逮捕された仲間を釈放するよう裁判官と電話、と。まるでマンガ。

 それらのエピソードと考察の部分がバランスよく書かれているのもうまいところ。データをごまかしてでも貸し付けて自分の成績を上げたいブローカーのデタラメや、借金をしているほうがより融資を受けられるクレジットスコア制度の矛盾、それらを見えなくする分業化や証券化によって、サブプライムローンは破綻すべくして破綻したのだというのが筆者の意見らしい。もっとも、筆者のいたレンダー業界は本書ではあまり悪者に書いていないが。

 もうひとつ本書で驚いた点は、監訳者が内容とは関係なく、本書を「これから不動産で儲けるためのヒントを与えてくれる本」として紹介していること。いわく、これだけ落ち込んだ米国なら、バブルを乗り越えた日本人が儲けるタネがあるはず、とか。その発想はなかった。

graph-easyでテキストのハコ図をらくらく生成

 以下、とあるメーリングリストに投稿した内容を、ほぼそのままブログのネタに再利用してみます。

Re: textで図を書くツール

 PerlにはGraph::Easyというモジュールがあり、フロントエンドのgraph-easyコマンドも付属します。graph-easyを使うと、比較的簡単な記述から、さまざまなデータ形式のハコ図を生成できます(デフォルトはテキスト形式)。

 たとえば、以下の1行のファイルを用意します。

[ WiFi ] == PC-Card ==> [ mini Note ] == PC-Card ==>  [ Air H" ]

 このファイルをgraph-easyの標準入力に与えます。

$ graph-easy < sample01.txt

 すると、標準出力に以下のようなテキストが出力されます。

+------+  PC-Card   +-----------+  PC-Card   +--------+
| WiFi | =========> | mini Note | =========> | Air H" |
+------+            +-----------+            +--------+

 今度は、以下の内容のファイルを用意します。

[ WiFi ] == PC-Card ==> [ mini Note ]
[ mini Note ] == PC-Card ==>  [ Air H" ] { origin: mini Note; offset: 2,0; }
[ mini Note ] -- DC-DC converter 16-\>10.5V --> [ DC\nBattery ]
                                         { origin: mini Note; offset: 0,2; }

 このファイルをgraph-easyの標準入力に与えます。

$ graph-easy < sample02.txt

 すると、標準出力に以下のようなテキストが出力されます。

+------+  PC-Card   +----------------------------+  PC-Card   +--------+
| WiFi | =========> |         mini Note          | =========> | Air H" |
+------+            +----------------------------+            +--------+
                      |
                      | DC-DC converter 16->10.5V
                      v
                    +----------------------------+
                    |             DC             |
                    |          Battery           |
                    +----------------------------+

 こういうのがいいかどうかは使いかたによると思いますが、とりあえずこういうツールもあるという例でした。

参考

UbuntuでIPAモナーフォントはどこからインストールされるか

 答:HPのプリンタドライバのパッケージに含まれている。

$ apt-get source opfc-modulehp-ipamonafont-source
$ ls opfc-modulehp-ipamonafont-source-1.1.1+1.0.8/opfc-ModuleHP-1.1.1_withIPAMonaFonts-1.0.8/fonts
COPYING.font.ja  README_ipamona.txt  ipagp-mona.ttf   ipamp-mona.ttf
Makefile.am      doc                 ipagui-mona.ttf
Makefile.in      ipag-mona.ttf       ipam-mona.ttf

「亜愛一郎の狼狽」「亜愛一郎の転倒」「亜愛一郎の逃亡」「亜智一郎の恐慌」

 本を片づけようとすると読書に没頭してしまうというのはお約束のパターンだけど、実際に「亜愛一郎」シリーズを読み返してしまった。

亜愛一郎の狼狽 (創元推理文庫)
泡坂 妻夫
東京創元社
売り上げランキング: 143252

亜愛一郎の転倒 (創元推理文庫)
泡坂 妻夫
東京創元社
売り上げランキング: 112298

亜愛一郎の逃亡 (創元推理文庫―現代日本推理小説叢書)
泡坂 妻夫
東京創元社
売り上げランキング: 175469


 美男子なのに与太郎キャラのカメラマン亜愛一郎(あ・あいいちろう)が、不思議な事件に出会うたびに白目を剥いて解決するという連作ミステリー小説。本格推理の傑作であり、トボけた感じのユーモアミステリーでもある。

 物語は毎回、違うゲストの視点からユーモアを込めて物語られる。奇妙な状況が起こり、とおりすがりの亜愛一郎がいきなり(場合によっては事件が始まったとも思えない時点で)結論を言い、その後で論理的に説明して周囲を驚かせる。もろに現代版ブラウン神父だ。現代といっても、登場したのが1975年だけど。

 犯人やトリックというのはもちろん、それ以上に、亜がどういう推理を経て結論にたどり着くかが焦点というところが、謎解きミステリーなのに倒叙ミステリーっぽくもあって面白い。

亜智一郎の恐慌 (創元推理文庫)
泡坂 妻夫
東京創元社
売り上げランキング: 229944


 亜愛一郎のご先祖様が幕末に閑職に見せかけて将軍(家定~)の右腕を務めていた、という番外編が本作。といっても、亜愛一郎シリーズほど本格推理というわけでもなく、亜愛一郎ほどすっとんきょうなキャラではなく、どちらかというと同じ作者の時代小説あたりのテイスト。亜愛一郎シリーズのさまざまな登場人物のご先祖らしき人物が登場し、ご先祖というよりスターシステムか。でも、これはこれで面白い。

「C.M.B.」9巻



 少年博物学者が活躍するミステリーマンガの最新刊。「太陽とフォークロア」「メタモルフォーゼ」「死滅回遊」の3編を収録。

 「Q.E.D.」ほど真正面からミステリというわけじゃないけど、この巻ではビジュアル伏線とか切ない結末とか、ちょっと初期「Q.E.D.」っぽいかも。

DNSサーバーのUnboundを使ってみたメモ

 自宅LANでローカルな名前解決をするのに、DNSキャッシュサーバーのUnboundをインストールしてみました。その話を小江戸らぐというグループの活動報告会で話してきたので、メモとして残してみます。

 といっても、とても簡単で、2~3台のクライアントにhostsを設定する程度の手間の手順ですが。

Unboundってなに?

DNSってなに?

  • ホスト名からIPアドレスを、あるいはIPアドレスからホスト名を調べるしくみ
  • ツリー状のインターネット分散データベース

DNSサーバーではBindが有名

  • Bindの役目は大きく2つ
    • キャッシュサーバー(フルリゾルバ):LAN内からの問いあわせを受けてインターネットのサーバーについて答える
    • コンテンツサーバー:インターネットに対して自ドメインのDNS情報を流す
  • Unboundは、このうちキャッシュサーバーに特化したもの
    • Bind(結びつけ、束縛)に対してUnbound(束縛されない)
    • 同じNLnetからは、コンテンツサーバーのNSD(DNSのアナグラム)、digクローンのdrillも

Unboundを使うと何がいいの?

  • キャッシュサーバーができる
    • LAN上からのクエリをキャッシュしてくれるので、各クライアントの名前解決が速くなる。といっても、今までも遅くなかったので、これから試す
  • ローカルな名前解決が設定できる
    • LAN内のファイルサーバーの名前を設定したり

Bindに比べてどう?

  • Bindみたいにコミコミじゃなくて、キャッシュサーバーに特化している
    • 設定がとても簡単(Bindは最小限の設定でも大変)
    • 小さい、速い(らしい)
  • セキュア(らしい)
    • DNSキャッシュ汚染に強いらしい
    • DNSSECにも対応

入手するには

  • Debian系
    • Debianではlenny以降またはsid、UbuntuではIntrepid以降で標準で用意されている
  • Red Hat系
  • あるいは、公式サイトからソースをダウンロード

キャッシュサーバーにしてみる

  • 以下、Debianでの例
  • インストール
    $ sudo apt-get install unbound
  • 設定ファイルを編集
    $ sudo vi /etc/unbound/unbound.conf
  • 設定するのはこれだけ
    interface: 0.0.0.0
    
    access-control: 192.168.1.0/24 allow
  • 再起動
    $ sudo /etc/init.d/unbound restart

ローカルの情報を登録してみる

  • 設定に1行追加するだけ
    local-data: "server01 A 192.168.1.250"
  • 再起動(上と同じ)
  • 試す
    $ dig server01 @localhost

そのマシンからUnboundを参照するようにする

  • resolv.confを設定
    $ sudo vi /etc/resolv.conf
    
    nameserver 127.0.0.1

クライアントからUnboundを参照するようにする

  • ブロードバンドルーターでDHCPサーバーしているので、配るDNSサーバー情報を設定
    • クライアントが自動的にUnboundを参照するようになる

Unboundの弱点

  • サーバー側でDNSラウンドロビンしている場合、Unboundが順序までキャッシュしてくれちゃう
    • クライアントは常に同じサーバーにアクセスすることになっちゃう

Ubuntu 8.10のXでCtrlとCapsLockを入れ替える

 常用するLinuxでは、たいていCtrlとCapsLockを入れ替えて使っている。一時期はGNOMEの設定で入れ替えていたこともあったが、たまにEmacsでおかしくなることがあったので、従来どおりXの設定で入れ替えている。

 Ubuntu 8.10がリリースされたので、いままでの環境を消して新規インストールしてみた。さて/etc/X11/xorg.confを編集しようとしたら、ほとんど設定がない。実質、これだけ。

Section "Device"
        Identifier      "Configured Video Device"
EndSection

Section "Monitor"
        Identifier      "Configured Monitor"
EndSection

Section "Screen"
        Identifier      "Default Screen"
        Monitor         "Configured Monitor"
        Device          "Configured Video Device"
EndSection

 Fedora 10といいUbuntu 8.10といい、最近はxorg.confなしが流行りなんだろうか。

 このxorg.confに設定を追加するのも癪なので、/etcの下を探してみた。どうやら、/etc/default/console-setupに、Xのキーボード設定もあるようだ。

XKBMODEL="jp106"
XKBLAYOUT="jp,jp"
XKBVARIANT="106,"
XKBOPTIONS="grp:alt_shift_toggle,grp_led:scroll"

 そこで、XKBOPTIONSの行を変更する。

XKBOPTIONS="ctrl:swapcaps"

 Xを再起動したら、無事CtrlとCapsLockが入れ替わった。

追記2009.1.19

 ちなみに、この設定はコンソールでのキーにも反映される。console-setupがckbcompでXの設定を変換して読み込んでいるため。

 | HOME | 

Categories

Recent Entries

Recent Comments

Recent Trackbacks

Appendix

emasaka

emasaka

フリーター。
連絡先はこのへん

Monthly