2009-11-26

また訓練

予備自補の訓練で、また五日間留守にする。帰ったら仕事を探さなければ。

lambdaに関する記事がやたらと人気だ。意外とlambdaの需要はあるのかもしれない。たしかに、rvalue referenceより、lambdaの方が、一般に使われると思う。そもそもlambdaは、既存の関数オブジェクトを、より気軽に使うことを目的としているのだから。

insertion sortをジェネリックに実装してみた

namespace hito {

namespace detail {

template < typename Iterator >
void rotate( Iterator first, Iterator last )
{
    using namespace std ;

    for ( Iterator i = first ; i != last ; ++i )
    {
        swap( *first, *i ) ;
    }
}

}

template < typename Iterator, typename Compare >
void insertion_sort(
    Iterator first,
    Iterator last,
    Compare comp  )
{
    typedef typename std::iterator_traits<Iterator>::value_type value_type ;

    Iterator i = first ;
    for ( ++i ; i != last ; ++i )
    {
        Iterator iter =
            std::find_if( first, i,
                [&](value_type const & value){ return comp( *i, value ) ; }
            ) ;

        if ( iter != i )
        {
            Iterator last = i ;
            ++last ;
            detail::rotate( iter, last ) ;
        }
    }
}

}

やりすぎたかもしれない。

調べたら、広く知られているinsertion sortのコードは駄目すぎる - やねうらお-よっちゃんイカを食べながら年収1億円稼げる(かも知れない)仕事術よりちょっぴり遅かった。

ただ、insertion sortは、ほとんどソートされた配列に提供するのが筋である。だから、逆から追っていくのが普通の実装だ。bidirectionalにしていいなら、もうすこし早くなるのかも。

追記:std::move()のイテレーターは、オーバーラップできないので、自前で書いた。

2009-11-25

rvalue reference 補足

前回、rvalue reference 完全解説なる記事を書いた所、ある程度の反響を得た。このような個人のブログでは、数人が思う所を書けば、まあ、ある程度の反響と言って差し支えないのである。

しかるに、皆rvalue referenceの理解に苦しんでいる様子だ。果たしてあの解説が至らなかったのであろうか。しかし、あの解説は、これ以上ないくらい簡単に書いたはずである。

実際、value referenceは、名前の通りのrvalueへのreferenceに過ぎぬのだし、std::move()とstd::forward()は、キャストに過ぎぬのだ。std::move()は、rvalueへのキャストであり。std::forward()は、lvalueをlvalueへ、rvalueをrvalueへキャストするのである。それだけのことなのである。

しかし、現実に理解に苦しんでいる者がいる。これは一体どういう事なのか。つくづくこの事を案ずるに、これは畢竟、rvalue referenceが理解できないのではなく、C++のその他の規格を理解していないのではないかと思う。

lvalueとrvalue

どうやら、lvalueとrvalueの違いを理解していないC++プログラマがいるらしい。C++98の頃から、lvalueとrvalueはある。これを理解せずには、C++98すら使いこなせないのではないかと思うのだが、不思議なものだ。

思うに、lvalueとrvalueというのは、名前がよろしくない。もはや、left, rightとは何の関係もないのだ。従って、もしC++の本で、左辺値、右辺値、などという訳語を使っている本があれば、例外なくクソであることが予想されるので買わない方が良い。

named valueとunnamed valueの方が分かりやすいという意見もある。確かに、rvalueに名前はない。また、rvalueは一時的なオブジェクトである。

templateのargument deduction

ある者は、std::forward()を、テンプレートではない普通の関数の中もで使うべきかどうか疑問に思っている。本来、こんな疑問が生ずること自体おかしいのだ。std::forward()はargument deductionに関係する問題なのだから。

そしてふと思ったのだが、もしや、世の中にはargument deductionを知らないC++プログラマがいるのではあるまいかということだ。一体どうやってテンプレート関数を書いているのか知らないが、どうやらいるらしい。不思議なことだ。

argument deductionとは、テンプレート引数を関数の引数として使った場合、テンプレート引数を指定しなくても、型を推定してくれる機能のことだ。

template < typename T > void f ( T ) {}
template < typename T > void g ( T * ) {}
template < typename T > void h ( T const * ) {}


int main()
{
    int const * ptr = nullptr ;

    f( ptr ) ; // T は int const *
    g( ptr ) ; // T は int const
    h( ptr ) ; // T は int
}

もしこの機能がないならば、プログラマは非常なる不便を被るであろう。

そのargument deductionとして、rvalue referenceがlvalueになるのである。

template < typename T > void f ( T && )
{
    std::cout<< std::is_lvalue_reference<T>::value << std::endl ;
}

int main()
{
    int x = 0 ;

    // T は int &
    f( x ) ;

    // T は int
    f( std::move(x) ) ;
}

つまり、テンプレート引数で、関数の引数をrvalue referenceにすると、引数がlvalueかrvalueか、インスタンス化されるまで分からないのである。

だから、std::moveは使えないのである。うっかり引数のlvalue referenceをrvalue referenceにしてしまおうものなら、呼び出し元が悲惨なことになるだろう。したがって、lvalueはlvalueのままにしておいてくれる、特別なキャストが必要なのである。

java 7が斜め上を行っている件について

Javaがクソなのは今に始まった話ではないのだが、Java 7でさらにクソミソになるようだ。

New language features in Java 7 « JoeJag :: Tech

Java 7にはDiamond Operatorなるものがある。

アイディアとしてはこうだ。いままでは、こんな風に書かなければならなかった。

Map<String, List<String>> anagrams = new HashMap<String, List<String>>();

これは、どう見ても冗長で無駄だ。モダンな言語をみるに、C++0xのautoといい、C#のvarといい、これらの冗長なコードを回避できる方法を提供している。時代遅れで池沼のJavaも、なんとかC++やC#のマネごとをしたかったものとみえる。連中の解決方法は以下の通り。

Map<String, List<String>> anagrams = new HashMap<>();

わー、ボク、よくできまちたねー。ナデナデ。

連中は何考えているんだろう。C++もC#も、型を右側に記述し、代入する変数に、わざわざ型を記述しなくてもすむようにしているのだ。なぜなら、式の結果の型を、コンパイラは決定できるからなのだ。しかし、Java 7は、依然として変数の型を指定しなければならないことに変わりない。何も変わっていないのである。

海外は進んでいるなぁ

Apple "refuses to repair smokers' Macs" | News | PC Pro

それに引き替え、このクソな日本は。亡命したいくらいだ。さっさとタバコを禁止にしろ。

2009-11-24

C++0xの新しい乱数ライブラリ、random

注意:最新ドラフトのN3000のrandomの規定は、コンセプトが却下される前の文面であり、今後、変更があると思われる。

C++は標準ライブラリが貧弱であるとは、よく言われることだ。ことに、乱数に関しては、貧弱の極みである。ご存じのように、C++は、Cから標準ライブラリを引き継いでいる。rand()だ。これは、0からRAND_MAXまでの値を返すと規定されている。RAND_MAXは実装によって異なるが、ほとんどの処理系では、32767である。現代の乱数需要を満たすには、あまりにも小さすぎる。

この状況を打破すべく、C++0xには新しい乱数のライブラリが盛り込まれた。randomである。これはBoostの実装を元にしているのだが、Boostとは少し違っている。今回はそのrandomを、浅く触りだけ紹介しようと思う。というのも、ライブラリの細かなメンバ関数の一つ一つまで説明するのは、甚だ冗長であるし、残念ながら私は、乱数のアルゴリズムを詳細に論ずるだけの数学的知識を持ち合わせていないからだ。そんな私が解説するのであるから、数式を見ただけでジンマシンがでるほどの数学アレルギーをお持ちの型も、安心して読み進めてもらいたい。

使い方

randomは、<random>をincludeすることによって使用できる。

#include <random>

randomを利用するに当たって、最低限知っておかなければならないことは二つある。engineとdistributionである。

engine

エンジンは、乱数生成のクラスである。乱数はこのクラスで生成されることになる。標準で、様々なアルゴリズムが用意されている。もちろん、標準のコンセプトに乗っ取って、自分で実装することも出来る。コンセプトはすでに廃止されたが、便宜上こう呼ぶことにする。あるいは、インターフェースとでもいうべきか。

標準ライブラリは、おもに三つのエンジンを提供し、そのエンジンをベースに、さらに三つのアダプタエンジンを提供している。

とはいっても、一般ユーザーが主に使うのは、そのエンジンをさらにtypedefしたものである。ここでは、主にメルセンヌ・ツイスタを使うことにする。その他のアルゴリズムや、具体的な実装方法に興味があれば、規格を読んでもらいたい。

さて、早くコードが読みたいせっかちな諸君のために、エンジンを使ったコード例を示そう。

int main()
{
    std::mt19937 engine ;

    std::cout << "min: " << engine.min() << std::endl ;
    std::cout << "max: " << engine.max() << std::endl ;

    for ( int i = 0 ; i != 10 ; ++i )
        std::cout << engine() << std::endl ;
}

これで乱数は生成できた。めでたしめでたし・・・・・・ならず。

残念ながら、話はハッピーエンドには終わらないのである。確かに乱数は生成できた。ただし、これではあまり使い勝手がよくない。無論、諸君は私より数学が得意であろうから、「値の範囲が便利じゃないって? 別にかまわんよ。値の範囲の調整ぐらい自前でやるさ」と思うかも知れない。しかし、私は先ほども言ったように、数学的知識が絶望的に足りないので、一体どうやって正しく値の範囲を変更すればよいのか分からない。私は先天的に数学を理解する脳の部分を持たずに生まれてきているので、数式というものは、まったく頭に入らないのである。

そもそも、仮に値の範囲を変更する方法を知っていたとしても、いちいちそんな面倒な事を自前で書きたくはない。真に優れたプログラマというのは、自分でコードを書かないものである。コードを書かなければ、バグを生み出す恐れはないからだ。幸い、標準ライブラリは、値を希望の範囲に変えてくれるクラスを提供してくれている。

distribution

engineクラスの生成する乱数を、ユーザーの欲しい値の範囲に変えてくれるのが、distributionクラスの役割だ。

ここで、六面のサイコロを作るものとする。六面のサイコロは、1から6までの数字を、一様に、ランダムで出すものである。型は、intでいいだろう。さっそく、distributionクラスを使って、サイコロを実装しよう。

int main()
{
    std::mt19937 engine ;

    std::uniform_int_distribution<int> distribution( 1, 6 ) ;

    for ( int i = 0 ; i != 10 ; ++i )
        std::cout << distribution(engine) << std::endl ;
}

このように、uniform_int_distributionを使うことによって、整数型の、範囲を指定した乱数を生成させることが出来る。ここではintを使ったが、shortでもlongでも、signedでもunsignedでも、整数型なら、自由に使える。

実数の乱数

整数だけではなく、実数の乱数も欲しい所である。実数の為には、uniform_real_distributionクラスが用意されている。

いま、0.0から1.0の範囲の実数を、一様かつランダムに生成したいとする。一体何故、このような乱数が必要になるのか、私にはいまいち分からないが、諸君の大部分は、私より数学が得意であろうから、なにがしかの理由を知っているのであろう。

int main()
{
    std::mt19937 engine ;

    std::uniform_real_distribution<double> distribution( 0.0, 1.0 ) ;

    for ( int i = 0 ; i != 10 ; ++i )
        std::cout << distribution(engine) << std::endl ;
}

ご覧の通りである。

distributionは他にもある。ここで紹介していないのは、uniformではない乱数を返すものである。つまり、範囲内の値が、同じ確率ででないのである。こう書くと、奇妙に聞こえるかも知れない。例えば、Normal Distributionだ。数学の分からない私には何がそんなに嬉しいのか理解できないが、範囲内の値を、正規分布な乱数で返すdistributionクラスである。その他にも、BernoulliだのPoissonだのSamplingだのと、色々あり、しかもその中d、さらに細かく別れているのだが、私にはさっぱり理解できない。多分、数学の出来る変態達には、垂涎もののクラスなのだろう。

seed

なるほど、randomの使い方はだいたい分かった。しかし、このままでは、実際に使うことは出来ぬ。凡そ乱数というものは、初期化を必要とする。メルセンヌなんとかいうアルゴリズムが、エラい数学のセンセーのお墨付きであったとしても、所詮は数式に過ぎぬ。何か外部から、最初の値を、真の乱数を与えてやらなければならないのだ。さもなくば、乱数の値は、プログラムを何度実行しても、同じものになってしまう。

標準ライブラリには、seed_seqというクラスがあり、これでもって、エンジンを初期化できる。

追記:engineに渡すseed sequenceを満たしたクラスのオブジェクトは、lvalueでなければならない。

    std::vector< std::uint_least32_t > v ;
    std::seed_seq seed( v.begin(), v.end() ) ;
    std::mt19937 engine( seed ) ;

seed_seqは、イテレーターをとる。各value_typeは、2の32乗に丸められて、seed_seqのprivateなメンバ変数であるvectorに格納される。エンジンは、seed_seqを使って初期化される。要素はいくつあってもかまわない。ただし、すべて使われるという保証はない。それは実装依存である。

問題なのは、一体どうやってこのvを乱数で埋めるかという話である。

真の乱数

乱数には、疑似乱数と真の乱数がある。今まで使っていたのは、疑似乱数である。ここでは、真の乱数が必要なのである。

本当の意味での真の乱数というのは、ラジウムとガイガーカウンターを組み合わせたデバイスであろう。なぜなら、ラジウムがいつアルファ崩壊するかは、観測するまで分からず、完全に確立の問題だからである。

余談だが、私は常々これに疑問を持っている。我々が観測しようがしまいが、アルファ崩壊する時は決まっているはずである。むしろ、アルファ崩壊を観測した我々と、アルファ崩壊を観測していない我々の、両方が存在するのではないかと思う。それはさておき。

残念ながら、そのようなデバイスは非常に高価であり、一般ユーザーのコンピューターには取り付けられていない。そこまで真の乱数とはいかなくても、ある程度のまともな乱数は、一般のコンピューターにも存在する。たとえば、現在の時刻であるとか、CPUの温度などだ。

幸いにして、C++0xの標準ライブラリには、そのような乱数を生成するクラスがある。random_deviceである。

int main()
{
    std::random_device rnd ;
    for ( int i = 0 ; i != 10 ; ++i )
        std::cout << rnd() << std::endl ;
}

「なんだ。最初からこれを使えばいいではないか」と思うかも知れない。ところが、普通の乱数の需要は、ここまで大がかりな乱数を使う必要はないのである。その理由については、私より簡潔かつ詳しく解説している本が山ほどあるので、ここでは説明しない。もし、分かりやすい本を知らないのと言うのであれば、結城浩の『新版暗号技術入門――秘密の国のアリス』がおすすめである。今手元にないので確認できないが、確か乱数について解説していたと思う。

さて、では早速、エンジンを初期化しよう。

int main()
{
    // ランダムデバイス
    std::random_device rnd ;

    // 初期化用ベクタ
    std::vector< std::uint_least32_t> v(10) ;

    // ベクタの初期化
    std::generate( v.begin(), v.end(), std::ref(rnd) ) ;
    
    // 乱数エンジン
    std::mt19937 engine( std::seed_seq( v.begin(), v.end() ) ) ;

    // distribution
    std::uniform_real_distribution<double> distribution(0.0, 1.0) ;
    

    for ( int i = 0 ; i != 10 ; ++i )
        std::cout << distribution(engine) << std::endl ;
}

美しい。

random_deviceをstd::ref()で渡しているのには、理由がある。というのも、このクラス、コピーもmoveもできないのである。したがって、関数オブジェクトとして渡そうと思ったら、参照で渡さなければならない。これも、C++0xには、便利な関数が<functional>にあるので、問題ない。

Boost.Randomとの違い

BoostにはRandomというライブラリがあり、TR1はこれを参考にして設計された。とはいえ、実際のC++0xに採用されたrandomは、Boostのものとは、多少異なっている。特にBoostユーザーは、variate_generatorがないことに驚くであろう。しかし、これには理由がある。

variate_generatorは、engineとdistributionをラップする便利なクラスである。なぜ、C++0xにはないのだろうか。

いくつかの乱数のアルゴリズムでは、variate_generatorを使えば、ある種の最適化ができるはずだった。これは、variate_generatorの本来の目的だ。ところが、実際に実装してみた所、別にvariate_generatorがなくても、問題がないことが分かった。さらに、variate_generatorが、乱数のアルゴリズムの実装を制限する可能性も指摘された。そんなわけで、variate_generatorの本来の目的は、消えてしまったのだ。そこで、variate_generatorは、規格から消されることになった。

しかし、engineとdistributionのラッパーとしての役割はどうすればいいのか。二つの変数を管理するのは面倒だ。一つにまとめたい。わざわざ自前でクラスを書かなければならないのだろうか。

実は、C++0xの力を以てすれば、そんなことはわけないのである。

int main()
{
    // bindを使う方法
    auto rnd1 = std::bind( std::uniform_real_distribution<double>(0.0, 1.0), std::mt19937() ) ;
 

    std::cout << rnd1() << std::endl ;

    // lambdaを使う方法
    std::mt19937 engine ;
    std::uniform_real_distribution<double> distribution(0.0, 1.0) ;

    auto rnd2 = [&]{ return distribution(engine) ; } ;

    std::cout << rnd2() << std::endl ; 
}

もちろん、autoの代わりに、std::functionを使ってもよい。

最後に

このように、C++0xのrandomは、数学がまったく理解できないものでも使いこなせる、実に便利なライブラリなのである。

oldnewthing: ウィリアムって人と替わってもらえる? さっき分かりやすかったからさ

The Old New Thing : Can I talk to that William fellow? He was so helpful

えー、本日はタイムマシンを使って、少し昔へ行ってみようと思います。ガイドはこの人、セス・マンヘイム君。当時その現場に居合わせておりました。

タイムマシンの日付を1989年、11月22日に合わせます。今からちょうど、20年と一日前の出来事でございます。あちらに見えますように、今、ビル・ゲイツが、新しいオフィスビルの製品サポート部門を視察しております。視察中、彼が今、一人の社員が電話と取るのを遮って、言いますことには、「私が替わりにやってもかまわないかね?」と。

ビルはヘッドセットを装着し、席に着き、電話を取りました。「はい、こちらはマイクロソフトの製品サポート、担当はウィリアムです。どういたしましたか」

ビルはお客様と話を致しまして、問題を把握しまして、製品サポートKBを検索しまして、検索結果からさらに探しまして、解決方法を見つけまして、お客様が問題を解決できるよう、辛抱強く説明しております。

お客様は、ウィリアムが問題を素早く解決し、また対応態度も真摯であったことに、大変満足されたようでございます。ビルは、「マイクロソフトの製品をご利用いただきありがとうございました」と言って、電話を終えたのでございます。

ビルは終始、自分のことをウィリアムと称しておりました。お客様は、外ならぬ、あのビル・ゲイツが対応致したとは、夢にも思わなかったことでございましょう。

さて、このお話は、まだまだ続くのでございます。

この時の時刻はちょうどお昼時にあたり、サポート社員のほとんどは昼食に席を外しておりましたが、この話はすぐに広まりました。そして、ビルが製品サポートの電話を取ったということは、すぐにその部署全体の知る所となったのでございます。

その後しばらくして、同じお客様が、さらに質問の電話をかけてこられました。

「もしもし、さっきかくかくしかじかの事について電話したんだけど、ウィリアムって人がすごい、いい人で、親切に教えてくれたんだよね。別の質問があるんだけど、ウィリアムと替わってもらえる?」

「かしこまりました。ただ今ウィリアムが空いているかどうか調べますので少々お待ち下さい」 製品サポートの社員は、サポート記録から、先ほどの電話を取った人の名前を調べました。billg。

「あ、いや、うーむ。すまん、ウィリアムってのはちょっと今出られないみたいなんだな。みんな奴のことはビルって呼んでるんだよ。いやなに、あれでね。さっきおたくと話したのは誰かって? ビル・ゲイツさ」

「何ィッ?」

lambda 完全解説

目的

この記事は、C++0xのlambdaを完全に解説せんとする目的を以て書かれた。サンプルコードは最小に留め、エラー処理等は省いた。この記事さえ読めば、今日からlambdaを恐れることなく使う本物のC++0xプログラマになれるだろう。

lambdaとは何ぞや

lambdaである。あるものは、lambda関数、あるいは無名関数という名称を使っている。いったいlambdaとは何なのか。

lambdaは関数である。また、特に名前はない。したがって、lambda関数、無名関数と呼ぶのは、間違ってはいない。しかしここでは、単にlambdaと呼ぶことにする。

lambdaを定義しよう

lambdaは関数オブジェクトである。lambdaは関数オブジェクトである。これはとても大事なので二度書いた。lambdaは関数オブジェクト以外の何物でもない。ところが不思議なことに、皆lambdaが単なる関数オブジェクトであることを承知してくれない。そこで今回は、コードで多くを語りたいと思う。

int main()
{
    [](){} ;
}

このコードは、well-formedなlambdaの最小のコードである。例しにコンパイルしてみるとよい。問題なくコンパイル出来るはずである。[](){} とあるのが、lambdaである。しかし、これはただ、lambdaという関数を定義しているだけなのだ。

lambdaを呼び出そう

lambdaは関数オブジェクトであるので、当然、関数呼び出しができる。関数呼び出しの文法は、通常の関数と同じく、()である。

int main()
{
    [](){}() ;
}

lambdaの文法解説

では、詳しい解説をして行きたいと思う。

int main()
{
    []  //  [ lambda-capture ]
    ()  //  ( parameter-declaration-clause )
    {}  //  compound-statement
    ()  //  Function call expression
    ;
}

まず、一番始めの[]は、lambda-introducerという。[]のなかには、lambda-captureを記述できる。これについては、後に解説する。
二番目は、関数の引数の定義である。通常の関数で、void f(int a, int b) などと書く引数と、まったく同じである。
三番目は、関数の本体である。通常の関数と同じく、実際のコードはこの中に書く。
四番目は、関数呼び出しである。これも、通常の関数とまったく変わらない。

実は、lambdaの引数は省略できるので、本当のlambdaの最小のコードは、以下の通りである。

int main()
{
    []{} ;
}

Hello,World

さて、関数オブジェクトを呼び出すからには、何か意味のあることをさせたい。そこで、lambdaに、Hello,Worldと表示させることにしよう。

int main()
{
  []{ std::cout << "Hello,World" << std::endl ; }() ;
}

そろそろ、lambdaも単なる関数オブジェクトであることが、分かってきたかと思う。

変数に代入

lambdaは関数オブジェクトである。これを承知できないのは、文法が少し変わっているからに過ぎないのだ。lambdaは関数オブジェクトであるが故に、変数に代入できる。

int main()
{
  // 変数へ代入
  auto func = []{ std::cout << "My hovercraft is full of eels." << std::endl ; } ;

  // 呼び出し
  func() ;
}

関数の引数に渡す

また、別の関数に渡せる。

template < typename Func >
void f( Func func )
{
  func() ;
}

int main()
{
  f( []{std::cout << "All your lambda is belong to us." << std::endl ; } ) ;
}

これで諸君も、否応なくlambdaが関数オブジェクトであることを認識できたであろう。

引数を取る

lambdaは関数オブジェクトであるので、引数を取れる。

int main()
{
  [](std::string const & str)          // 引数
  { std::cout << str << std::endl ; }   // 関数本体
  ("Have you read your SICP today?") ;  // 関数呼び出し
}

戻り値を返す

lambdaは関数オブジェクトであるので、戻り値を返せる。

int main()
{
  // 戻り値は、明示的に書かなくても推測してくれる
  auto a = []{ return 0 ; }() ;

  // 戻り値を明示的に書いた場合。
    // doubleからfloatへの型変換をしている。
  auto b = []() -> float { return 3.14 ; }() ; 
}

この、->、という記述は、少し戸惑うかもしれないが、こういう文法になっているので、仕方がないのである。

lambda関数では、戻り値は明示的に書かなくても、推測してくれる。ただし、{ return expression ; }の形でなければならない。この形でない場合は、void型を返すと見なされる。戻り値を明示する場合は、たとえ引数を取らないlambdaでも、引数リストを省略することは出来ない。

変数のキャプチャ

さて、ここまで読み進めれば、lambdaが関数オブジェクトであることは、疑いようもなく理解できたと思う。しかし、ここで一つ疑問がある。「なぜlambdaなのだ。なぜもっと他の、分かりやすい名称ではないのだ」と。もっともな疑問である。lambdaをlambdaたらしめる、最も重要な機能を解説しよう。

lambdaは、その定義されている関数のスコープ内の変数を、キャプチャできる。これも、文章で説明するのは分かりにくい。例を示す。

int main()
{
  std::string x = "I'm a lumberjack and I'm OK." ;

  // 参照によるキャプチャ
  [&]{ std::cout << x << std::endl ; }() ;
  
  // コピーによるキャプチャ
  [=]{ std::cout << x << std::endl ; }() ;
}

キャプチャには二種類ある。参照によるキャプチャと、コピーによるキャプチャである。参照というのは、lambdaのオブジェクト内で、変数の参照を保持するものである。コピーとは、lambdaのオブジェクト内で、変数そのものをコピーして保持するものである。その結果、lambdaが定義されている関数内のスコープにある変数を、lambdaの中で使うことが出来る。

例えば、以下のように使える。

template < typename Func >
void f( Func func )
{
  func(" and I'm OK.") ;
}


int main()
{
  std::string x = "I'm a lumberjack" ;

  f( [&](std::string const & str){ x += str ;} ) ;

    // 変数が書き換わっている。
    // "I'm a lumberjack and I'm OK."
  std::cout << x << std::endl ;
}

コピーの場合はどうだろうか。

template < typename Func >
void f( Func func )
{
  func(" and I'm OK.") ;
}


int main()
{
  std::string x = "I'm a lumberjack" ;

    // Error
    f( [=](std::string const & str){ x += str ;} ) ;

  std::cout << x << std::endl ;
}

コピーの場合は、エラーになってしまう。何故ならば、lambdaの関数呼び出し演算子は、const修飾されているからだ。もし、どうしてもコピーのキャプチャで、変数を書き換えたいのならば、mutableが使える。

template < typename Func >
void f( Func func )
{
  func(" and I'm OK.") ;
}


int main()
{
  std::string x = "I'm a lumberjack" ;

  f( [=](std::string const & str) mutable { x += str ;} ) ;

    // コピーなので、変数は書き換わっていない。
  // "I'm a lumberjack"
  std::cout << x << std::endl ;
}

mutableは、引数リストの後、戻り値指定の前に記述する。

[]() mutable -> {} ;

キャプチャを、コピーか参照のどちらでするかは、個々に指定できる。

int main()
{
  int a = 0, b = 0 ;

  [a, &b]() mutable { a = 1 ; b = 1 ; }() ;

  // 0
  std::cout << a << std::endl ;
  // 1
  std::cout << b << std::endl ;
}

このように、変数を列挙すればいい。また、一部の変数だけを指定して、残りはひとまとめに指定したい場合、capture-defaultを指定すればよい。

int main()
{
  int a, b, c, d, e, f, g ;

  // a, bのみコピー、その他は参照
  [&, a, b] { } ;

  // a, bのみ参照、その他はコピー
  [=, &a, &b] { } ;

}

ただし、デフォルトのキャプチャと同じものを指定することは出来ない。

int main()
{
  int a, b ;

  // Error. デフォルトと同じ
  [&, &a] { } ;

  // Error. デフォルトと同じ
  [=, a] { } ;

}

同じ変数を複数書くことは出来ない

int main()
{
  int a;

  // Error. aを二度書いている。
  [a, a] { } ;
}

this

クラスの非静的なメンバ関数内のlambdaで、thisを使った場合、そのクラスのオブジェクトのthisになる。

struct X
{
  int a ;
  void f()
  {
    [=]{ this->a = 1 ;}() ;
  }
} ;

thisはポインタであるので、この場合、キャプチャがコピーでも参照でも、aは書き換わる。

lambdaを返す関数

lambdaは関数の戻り値として返すことも出来る。

std::function< void () > f()
{
  std::string str("hello") ;
  return [=]{ std::cout << str << std::endl ; } ;
}

int main()
{

  // 一度変数に代入してから呼び出す。
  auto func = f() ;
  func() ;

  // lambdaを変数に代入せずそのまま呼び出す。
  f()() ;

}

このように、C++0xで追加された、すばらしい標準ライブラリ、std::functionを使えば、lambdaを返すことが出来る。

キャプチャが参照ではなくコピーであることに注意されたい。f()が戻る時点で、strは破棄されるので、ここはコピーでなくてはならない。

lambdaの型

規格では、lambdaは、ユニークな型を持つと定義されている。ただし、以下のコードはエラーである。

// Error
decltype([]{}) ;

これを出来るようにすると、コンパイラの実装が難しくなるからだ。同じ理由で、sizeofも使えない。

lambda実践

コードは文章よりも分かりやすい。

struct X
{
  int value ;
} ;

int main()
{
  std::vector< X > v(20) ;

  std::mt19937 rng ;
  std::uniform_int_distribution<int> dist(1, 99) ;

  // 乱数で初期化
  std::generate( v.begin(), v.end(),
    [&]() -> X {
      X x ;
      x.value = dist( rng ) ;
      return x ;
    }
  ) ;

  // 表示
  std::for_each( v.begin(), v.end(),
    [](X const & x)
            { std::cout << x.value << " " ; }
  ) ;
  std::cout << std::endl ;

  // ソート
  std::sort( v.begin(), v.end(),
    [](X const & a, X const & b)
            { return a.value < b.value ; }
  ) ;

  // 表示
  std::for_each( v.begin(), v.end(),
    [](X const & x){ std::cout << x.value << " " ; }
  ) ;
  std::cout << std::endl ;
}

最後に

結局の所、lambdaとは、ちょっと便利な関数オブジェクトに過ぎないのである。C++で関数オブジェクト使うのは常識であるから、C++0xでlambdaを使うのも常識になるだろう。

2009-11-23

rvalue reference 完全解説

目的

この記事は、C++0xのrvalue referenceを完全に解説せんとする目的を以て書かれた。サンプルコードは最小に留め、エラー処理等は省いた。この記事さえ読めば、今日からrvalue referenceを恐れることなく使う本物のC++0xプログラマになれるだろう。

lvalueとrvalueについて

Cの時代では、lvalueとrvalueの違いは、代入演算子の左側か右側かという違いだけであった。つまり、left hand value, right hand valueの略である。従って、訳語も、左辺値、右辺値であった。C++においては、これはもはや正しくはない。従って、右辺値、左辺値というのも、誤訳である。それ故に、ここでは、これ以上、左辺値、右辺値という名称を使用しない。

誤解を恐れずにいえば、lvalueとは、明示的に実体のある、名前付きのオブジェクトであり、rvalueとは、一時的に生成される無名のオブジェクトである。

struct X{} ;
int f() { return 0 ; }

int main()
{
  int i = 0 ;

  i ;   // lvalue
  0 ;   // rvalue

  X x ;

  x ;   // lvalue
  X() ;  // rvalue

  f() ;  // rvalue
}

上記のコードを読めば、lvalueとrvalueの違いが、なんとなく分かってくれる事と思う。lvalueはrvalueに変換できるが、その逆、rvalueをlvalueに変換することは出来ない。

referenceについて

C++98のreferenceは、C++0xにおいては、lvalue referenceと呼ばれるものである。

struct X{ } ;

void f( X & ) { }
void g( X const & ) { }

int main()
{
  X x ;

  f( x ) ;  // 1. OK
  f( X() ) ; // 2. Error

  g( X() ) ; // 3. OK
}

1.は問題がない。lvalueだからだ。
2.はコンパイルエラーになる。rvalueを渡しているからだ。
3.は問題がない。constなreferenceは、rvalueを参照できるからだ。

ちなみに、VC++の独自拡張(/Zaで無効にできる)では、2.のコンパイルが通ってしまうので注意されたい。真のC++プログラマは、コンパイラを信用しないものである。

ここで、3.は、言語的には、汚い仕様である。本来、rvalueなものを、lvalue referenceで参照しているのである。そこで、rvalue referenceの出番となる。

Rvalue Reference

rvalue referenceとは、その名の通り、rvalueに対する参照である。文章で説明するより、コードを示した方が分かりやすい。

struct X{ } ;

int main()
{
  X x ;

  // lvalue reference
  X & lr1 = x ;    // 1. OK
  X & lr2 = X() ;   // 2. Error

  // rvalue reference
  X && rr1 = x ;   // 3. Error
  X && rr2 = X() ;  // 4. OK
}

ごらんのように、rvalue referenceは、アンパサンドを二つ使う文法になっている。

1. は問題ない。xはlvalueだからだ。
2. はコンパイルエラーである。X()はrvalueであり、lvalue referenceでは参照できないからだ。
3. はコンパイルエラーである。xはlvalue referenceであり、rvalue referenceでは参照できないからだ。
4. は問題ない。X()はrvalueだからだ。

Rvalue Referenceの存在意義

実は、rvalue referenceとは、これだけの事なのである。その名前通り、rvalueに対する参照なのだ。とはいっても、これだけでは、存在意義が分からないであろうと思う。「一体何の役に立つのだ? const lvalue referenceでなくてもrvalueを参照できるようになっただけではないか?」と思うことだろう。実際、その通りで、「const lvalue referenceでなくてもrvalueを参照できるようになる」だけなのである。

そもそも、rvalueのオブジェクトには名前がなく、参照されなくなった時点で、自動的に破棄されるものである。勝手に破棄されるなら、書き換えても無駄である。constではなくなったからといって、何がそんなに嬉しいのか。

Move Semantics

以下のようなクラスを考える。

class X
{
private :
  char * ptr ;

public :
  X()
  {
    ptr = new char[1000] ;
    // バッファに対して、時間のかかる書き込みを実行
  }

  // コピーコンストラクタ
  X( X const & r )
  {
    ptr = new char[1000] ;
    std::copy( &ptr[0], &ptr[1000], &r.ptr[0] ) ;
  }

  // デストラクタ
  ~X()
  {
    delete[] ptr ;
  }

} ;

このクラスは、明らかにコンストラクタとコピーコンストラクタの実行が遅い。もし、コピーコンストラクタを、ポインタのすげ替えだけにすれば、パフォーマンスが大いに向上するだろう。ところが、そんなことをしてしまっては、コピー元のオブジェクトが使えなくなってしまうので、それは出来ない相談である。

しかし、よく考えると、安全に、コピーをポインタのすげ替えだけで済ませられる場合が存在するのである。

struct X {} ;

X f(){ return X() ; }

int main()
{
    // 関数の戻り値はrvalueである。
    X a( f() ) ; // 1.
    
    X tmp ; 
    X b( tmp ) ; // 2.

    // これ以降、tmpはもう使わない。
}

ここで、関数の戻り値はrvalueなので、安全にポインタをすげ替えられる。また、tmpは、もうこれ以上使わないので、ポインタをすげ替えても差し支えない。問題は、一体どうやって、その意図を表現すればいいのだろうか。

そこで、rvalue referenceの出番である。rvalueであれば、そのオブジェクトは、ポインタを横取りしても問題ないのである。

Move コンストラクタ

1. のコピーを、ポインタのすげ替えにするために、クラスXに、rvalue referenceを引数に取るコンストラクタを追加する。

class X
{
public :

    // Move コンストラクタ
  X( X && r )
  {
    ptr = r.ptr ;
    r.ptr = nullptr ;
  }
} ;

これをmoveコンストラクタと呼ぶ。1. は、このmoveコンストラクタが呼ばれ、ポインタのすげ替えになる。コピー元のオブジェクトのポインタを、nullptrにするのを忘れないこと。さもなくば、デストラクタが走る際に、ランタイムエラーになるだろう。

lvalueをmoveせよ

さて、2. はどうしたらいいだろう。moveコンストラクタを実装したものの、コンパイラは2. の場合には、moveコンストラクタを呼び出してくれない。なぜなら、コンパイラは、プログラマの脳内仕様を読んではくれないからだ。tmpが、その後に使われていないかどうかは、コンパイラは静的に決定できないのである。

そこで、プログラマが意図を伝えてやらなければならない。

X b( static_cast<X &&>(tmp) ) ;

この様に、rvalueにキャストしてやれば、moveコンストラクタを呼び出すことが出来る。

std::move()

とはいえ、これは甚だしく面倒である。タイプミスもしやすい。そこで、標準ライブラリには、便利な関数が用意されている。std::move()だ。

X b( std::move(tmp) ) ;

何のことはない、std::move()とは、本質的にはキャストなのである。実装例を以下に示す。

namespace std {

template <class T>
inline
typename std::remove_reference<T>::type&&
move(T&& t)
{
  return static_cast< std::remove_reference<T>::type&& >(t) ;
}

}

これだけの事なのである。単なるキャストである。自前でキャストを書くのは、エラーの元なので、std::move()を使うべきである。

ひとたび、変数に対してstd::move()を呼び出すと、それ以降、その変数を使える保証はなくなる。なぜなら、すでにmoveされているかもしれないからだ。

賢いコンパイラの場合

ちなみに、コンパイラによっては、上記のコードは、そもそもコピーコンストラクタもmoveコンストラクタも呼び出されない可能性がある。というのも、ある種の状況においては、コンパイラは安全且つ静的に、オブジェクトをコピーせずに、使い回せることを決定できるのである。たとえコンストラクタにサイドエフェクトがあったとしても、コンストラクタの呼び出しを省略できるのである。この種の最適化は、規格で保証されている。
(N3000 § 12.8 Copying class objects p19)

手持ちのコンパイラが優秀で、上記のコードでは、コンストラクタが呼び出されない場合、rvalue referenceの勉強のためには、以下のように書くとよい。

class X
{
public ;

    // moveな代入演算子
  X & operator = (X && r)
  {
    if ( this == &r )
      return *this ;

    delete[] ptr ;

    ptr = r.ptr ;
    r.ptr = nullptr ;

    return *this ;
  }
} ;

int main()
{
    X tmp ;
    X x ;
    
    x = std::move(tmp) ;
   
}

これは、最適化できないはずである。また、実際のコードではこのように、movableにしたければ、move コンストラクタの他に、move 代入演算子も定義するべきである。

オーバーロード

lvalue referenceとrvalue referenceは、もちろん、関数のoverload resolutionの際に、考慮される。

struct X {} ;

void f( X & x )
{ std::cout << "lvalue reference" << std::endl ; }

void f( X && x )
{ std::cout << "rvalue reference" << std::endl ; }


int main()
{
  X x ;

  f( x ) ;  // lvalue reference
  f( X() ) ; // rvalue reference
}

これは、さほど驚くに当たらないだろう。なぜなら、lvalueかrvalueかは、コンパイル時に静的に決定できるのだから。

テンプレート関数の引数におけるrvalue referenceのargument deduction

テンプレート関数の場合はどうなるだろうか。以下のコードを考えてもらいたい。

struct X {} ;

template < typename T >
void f( T && t ) {}

int main()
{
  X x ;

  f( x ) ;  // lvalue reference
  f( X() ) ; // rvalue reference
}

果たして、これはコンパイルが通るのだろうか。

実は、このコードはコンパイルが通る。規格には特別なルールがあり、テンプレート引数を、rvalue referenceとして関数の引数に使った場合のargument deductionで、lvalueを渡すと、lvalue referenceとなるのである。
(§ 14.9.2.1 Deducing template arguments from a function call p3)

つまり、上記のコードの場合、f()に、lvalue referenceを渡すと、TがX &になり、続く&&は無視され、lvalue referenceとして取り扱われる。

実に不思議なルールである。しかし、これも理由あってのことなのだ。もし、これが出来ないとなると、プログラマは、わざわざ、lvalue referenceとrvalue referenceとで、似たようなコードを複数書かなければならなくなる。すべての組み合わせを網羅するには、膨大なオーバーロード関数が必要になる。引数が1個の場合は、オーバーロード関数は2個、引数が2個の場合は、4個、引数が3個の場合は、8個、引数が4個の場合は、16個もの、オーバーロード関数を書かなければならない。これでは、一体何のためのテンプレートなのだろうか。

幸いなことに、テンプレート関数の場合は、rvalue referenceでlvalue referenceも参照できるので、そのようなオーバーロード関数の指数関数的な増加は起こらない。しかし、ここでひとつ問題がある。

Perfect Forwarding

template < typename T >
void f( T && t )
{
  X x(t) ;
}

f()の中で、Xをコピーしたい。ここまで読み進めた者ならば、当然、rvalueの際には、moveしたいところであろう。ところが残念なことに、std::move()は使えないのである。

なぜだろうか。

struct X {} ;

template < typename T >
void f( T && t )
{
  X x( std::move(t) ) ;
    // これ以降、tは使用不可
}

int main()
{
  X x ;

  f( x ) ;  // lvalue reference

  //これ以降、xは使用不可
}

なぜなら、引数はlvalue referenceである可能性もあるからだ。main()側で、std::move()していないのに、xが勝手にmoveされて使用不可になったのでは、たまったものではない。main()側でstd::move()したときのみ、moveしてもらいたい。

ところが、f()側からみれば、引数はlvalueかrvalueか、テンプレートがインスタンス化されるまで分からないのである。lvalue referenceならコピーし、rvalue referenceの時のみmoveしたい。さて困った。一体どうしよう。

メタプログラミングを試す

メタプログラミングである。メタプログラミングの理解出来ないプログラマは、もはやC++プログラマとして認められないのである。ポインタの理解できないCプログラマと同じぐらい、役立たずのゴミ虫のすかしっ屁である。メタプログラミングこそ正義ィィッ! メタプログラミングに不可能はないィィッ!

では、さっそくメタプログラミングで問題を解決しようッ!

template < typename T >
void f( T && t )
{
  if ( std::is_lvalue_reference<T>::value )
    X x( t ) ;
  else
    X x( std::move(t) ) ;
}

残念ながら、これは問題を多数のオーバーロード関数から、多数のメタプログラムに移しただけである。引数をひとつひとつ、このような方法で調べていくのは、面倒だし、引数が増えれば、オーバーロード関数と同じく、if文も爆発的に増えていく。

必要なのは、lvalueの場合はlvalue、rvalueの場合はrvalueを渡す方法である。

キャストを使う

以下のようなキャストを使えば、それが実現できる。

template < typename T >
void f( T && t )
{
  X x( static_cast<T &&>(t) ) ;
}

なぜこのキャストが、lvalueの時はlvalueを返し、rvalueの時はrvalueを返すのか。それは、argument deductionのおかげである。

もし、引数にlvalueが渡された場合、TはX &となり、&&は無視される。それ故、このキャストは、lvalueをlvalueにキャストするのである。rvalueが渡された場合は、当然、rvalueとなる。

このようにすれば、テンプレート関数に渡された引数を、そのまま別の関数に渡すことが出来る。

std::forward()

とはいえ、キャストを使うのは面倒であるし、エラーの元である。そのために、標準ライブラリには、便利な関数が用意されている。std::forward()だ。

template < typename T >
void f( T && t )
{
  X x( std::forward<T>(t) ) ;
}

何のことはない。std::forward()とは、本質的にはキャストなのである。実装例を以下に示す。

namespace std {

template <class T, class U,
  class = typename enable_if<
     (is_lvalue_reference<T>::value ?
       is_lvalue_reference<U>::value :
       true) &&
     is_convertible<typename remove_reference<U>::type*,
            typename remove_reference<T>::type*>::value
  >::type>
inline
T&&
forward(U&& u)
{
  return static_cast<T&&>(u);
}

}

恐ろしげなメタプログラムに面食らうかも知れないが、本質的には単なるキャストである。メタプログラムの意味は、
Tがlvalue referenceならば、Uもlvalue referenceでなければならない。
参照を取り除いた状態で、UからTに変換できなければならない。
という意味である。この二つの条件を満たさない場合、std::forward()はoverload resolutionのcandidateから外される。則ち、コンパイルエラーとなる。これにより、典型的なタイプミスなどによるエラーを防ぐことが出来るのである。

std::forward()は、テンプレート関数の引数を、田の関数にそのまま渡す際に使うものである。これをPerfect Forwardingという。

最後に

rvalue referenceは、実に単純なのである。名前の通り、rvalueへの参照に過ぎないのである。std::move()もstd::forward()も、単なるキャストに過ぎないのである。

std::move()は、lvalueをmoveしたいときに使い、std::forward()は、テンプレート関数の引数を、そのまま別の関数に渡したい時に使う。

2009-11-22

rvalue referenceは難しい

N2951: forwardを理解するために、rvalue referenceを、本腰入れて学んだが、これは難しい。

とにかく、近日中に、rvalue referenceを完全に解説していこうと思う。

2009-11-21

theoldnewthing: メモ帳の作者、発見さる

The Old New Thing : We found the author of Notepad, sorry you didn't go to the award ceremony

メモ帳の作者に関する確証を得た。どうやら本人らしい。ごめんね、授賞式に参加させられなくて。

メモ帳の最初の作者は、Windows 95の開発マネージャであった。その仕事は、Windows 95のプログラマを管理する事であった。「簡単ではない」と分類される種類の仕事である。

Windows 95以降、彼はソフトウェア業界から引退し、高校の理科の教師になった。数年後のとある機会に、私は彼と再会したので、ソフトウェア業界から高校の理科の教師に転身した感想を訊ねたところ、

曰く、「もう能力がまるっきり変わってしまったよ」と。

五日間家を空けていたので、リアルタイムで気がつかなかった。尚、この文章は、メモ帳で書いた。

ところで、この原文では、興味深いイディオムが使われている。"to herd cats"という言葉だ。これは文字通りに解釈すれば、「猫の群れの番をする」という意味で、転じて、「とても困難な事」という意味を持つ。

参考:本の虫: theoldnewthing: おい、誰が受賞するよ

風邪をひいたらしい

予備自補の訓練で疲労したのか、はた、訓練が終わって気が抜けていたのか、風邪をひいてしまったようだ。今日は、どうにも眠いので、ついうとうとと昼寝をした。目覚めた後、やけに体が熱っぽく感じたので、体温を計測した所、熱が38.7度もあった。明らかに体調を崩している。

今のところ、やや体が熱っぽい以外の自覚症状はないが、これだけ熱が高いと、明日以降、自覚症状も出てくることだろう。早めに直さねばならぬ。

今週のサウスパーク

予備自補の訓練にいっていたため、サウスパークを観るのが遅れてしまった。今回は、プールに小便が混じっているという話だ。

どうも今回の話は、あまり面白くない。プールに小便、カートマンが有色人種を嫌う、アホ科学者のマヌケな実験、個々には面白いのだが、一連の話に繋がりがなく、伏線を回収して終了。ただ、昔のサウスパークを思い出すような話だった。昔のサウスパークは、こんな風に訳の分からない話が多かったのだ。

South Park: Pee

2009-11-20

日記

予備自補の訓練より帰宅。もう、だいぶ慣れてきたので、何か特別に書くということもなくなってしまった。

今回は、25km行軍を実施した。それほど疲れなかったが、歩き終わって、足を調べた所、マメが出来ていた。

帰宅して、ふとモチベーションがあったので、ブログの各記事に、はてなブックマークへのリンクをつけてみた。標準の画像が小さいので、二倍に拡大した。拡大は汚い。はてなは、画像を用いるならば、SVGで提供すべきである。

訓練の空き時間に、韓愈の争臣論を読んだ。あまり名文とは言い難い、荒削りな文章であった。どうやら、韓愈のまだ若い時の作であるらしい。

思うに、韓愈の文章のすばらしさは、その明瞭簡潔なことにあるのだと思う。

2009-11-15

外人の剣道に対する見方

数時間暇なので、いつもの如くIRCで外人とチャットをしていた所、剣道の話題が出た。曰く、「ケンドーはマーシャルアートだ。如何にしてカタナを使ったリアルファイトで生き残るかという技術である」と。それは違う。剣道は単なるスポーツである。フェンシングと同じなのである。ところが、外人は皆、そう主張する私を、「ああ、戦後のアメリカ様の占領政策に騙された、かわいそうなイエローのジャップなんだな」と諭すのである。

剣道か剣術かという、名義上の議論はどうでもいい。重要なのは、果たして剣というものが、それほど重要であったかどうかだ。

そもそも、つらつらと日本の歴史をみるに、戦争で剣が重要視されたという話は見あたらない。古代では、剣は単なる儀礼的な道具であった。奈良、平安時代でも、戦争で剣が重要だったという話は聞かない。戦国時代でも、剣は使われていなかった。本当の戦争では、主に弓や槍、長刀の類が使われていたである。

これは当然である。だれもわざわざ、たかだか60cm前後の、リーチの短い武器で接近して戦いたいとは思わなかったのである。そんなに接近しては、自分の身を全うして、相手を殺すことが出来ぬ。

さて、戦国時代も終わり、世の中が平和になると、やれ何々流といった、剣術が盛んに行われるようになる。これは、西洋のフェンシングと同等の理由である。個人間の決闘や、儀礼としての剣である。決闘といえば聞こえはいいが、まあ大抵は、どちらかが少しでも血を流せば、それで勝負が付くようなお遊びである。マーシャルアートなどと重々しく呼ぶに足りぬ。

結局、刀というのは儀礼上の道具でしかなく。つばぜり合いなどしようものなら、たちまち刃こぼれして使えなくなる、まことに脆い武器なのである。

ということを主張したが、連中は依然として、「剣道はマーシャルアートであり、お前はアメリカ様に洗脳された哀れな土人だ」という見方を改めぬのである。つまり、彼らの頭の中では、アメリカの占領政策の影響はそれほど大きく、日本を完全に変えてしまったと考えているのである。日本の近代化は戦後からで、戦前は他のアジアと変わらぬ、土人の住む未開の地であったという頭があるのだろう。

無論、私は剣道を馬鹿にするつもりは毛頭ない。あくまで、剣道がスポーツに属することを主張したいだけである。適度な運動は健康によいことはもちろんである。

最も日本の歴史を変えた人物は徳川慶喜である

“日本の歴史を変えた偉人”に1位に坂本龍馬、先見の明と決断力が日本を変える!?

坂本龍馬など、単なる一壮士に過ぎぬ。もし司馬遼太郎という作家がいなければ、歴史の中に埋没していただろう。

徳川家康は、むしろ逆である。彼は日本を変えないことに成功した人物である。彼は当時の日本の文化を維持するために、鎖国を敷いた。

今日の日本を日本たらしめた、歴史を変えた偉人の第一位は、徳川慶喜である。もし彼が江戸幕府の維持に積極的であれば、日本は内戦が続き、富国強兵どころではなかっただろう。おそらくは混乱に乗ぜられ、列強の植民地化する所となり、今日の先進国ではなく、単なる第三国の一と成り下がたのである。

豊臣秀吉は、第二位である。もし彼がキリスト教を禁止しなかったら、日本は侵略され、植民地となって、第三国に成り下がっていたであろう。

第三位は、源頼朝である。彼は人望が厚く、政治的手腕もあった。すでに実質的な力を失い、単なる権威と成り下がっていた天皇家から、武家に政権を移す、その第一歩を進めたのが、頼朝である。

第四位は、藤原兼家である。彼は花山院を出家させることに成功し、政治の実権を、天皇家から、摂政家に移すことに成功した人物である。無論、摂政が力を持っていたのは、それ以前からであったが、それを決定的なものにしたのは、兼家の力である。

第五位は、天皇である。國津神、すなわち土着民を征服して、一天四海を統一した最初の人物である。

そういえば、私の親父は、「蘇我入鹿あたりで、どう考えてもおかしい部分がある。あの辺で一回、革命が起きて、つじつまを合わせるために、嘘の史書を書いていたんじゃないか」と言っていた。その辺の歴史は、不勉強でよく分からない。

【DTP】心に残るむちゃくちゃな指示

【DTP】心に残るむちゃくちゃな指示 - にくろぐ。

大変なんだな。

2009-11-14

ヲコゼ

柳田國男の書いたヲコゼを笑う祭りの話が面白い。

それにしても、柳田國男は、ヲコゼがどのような魚かというのを知らず、友人に手紙を出して訪ねている。友人は、手持ちの図鑑を参照して返答している。また、友人の子供に、図鑑の絵を描き写させて、送っている。

それに引き替え私は、なんともズボラである。まったく、いい時代になったものだ。

それにしても、崇拝したというヲコゼは、ハナオコゼなのだろうか。確かに、崇拝するに足る外見ではある。

柳田國男は偉大なる妄想家であった。

多分、タイムマシンで現代に連れて帰ったら、ラノベでも書き始めるのではなかろうか。

とにかく、空想が激しい。優れた民族学者であることの条件には、文才の他に、妄想力が問われるのではなかろうか。しかしそれは約めていえば、自ら荒唐無稽な昔話を作り出してるということである。

ブラウザは須く一部のCSS3 rubyを実装すべし

webkitがrubyを実装して、喜んでいたのだが、依然私は、以下のようなユーザースクリプトを用いている。

(function(){
    if ( document.getElementById( "hito_hide_ruby" ) )
        return undefined ;

    var s = document.createElement( "style" ) ;
    s.id = "hito_hide_ruby" ;
    s.type = "text/css" ;
    s.appendChild( document.createTextNode( "rp{display:none;} rt{display:none;}" ) ) ;
    document.getElementsByTagName( "head" )[0].appendChild( s ) ;

    return undefined;
})() ;

理由は、rubyの表示が汚いのだ。たとえば、以下の例を、rubyをサポートしたブラウザで見てもらいたい。

俺は邪気眼(ダーク・セイクレッド・サード・アイ)を持っている。

この例では、「邪気眼」という単語に、「ダーク・セイクレッド・サード・アイ」というルビがかかっている。

さて、まず問題なのは、ルビのオーバーハングである。

   ダーク・セイクレッド・サード・アイ
俺は       邪気眼        を持っている。

と表示されたことと思う。しかし、これは読みにくいと感じる人もいるかもしれない。例えば、

ダーク・セイクレッド・サード・アイ
俺は邪気眼を持っている。

となっているほうが読みやすくないだろうか。

あるいは、前者を好む人もいるかもしれぬ。出来ることなら、この表示を変更できるようにしたい。

そこで、CSS3のruby-overhangプロパティである。これを使えば、ルビがオーバーハングするかどうかを指定できる。

次に、アラインの問題がある。ルビのオーバーハングをしない指定にしたとしよう。その場合、二つの表示方法がある。

   ダーク・セイクレッド・サード・アイ
俺は       邪気眼        を持っている。

   ダーク・セイクレッド・サード・アイ
俺は    邪    気    眼   を持っている。

どちらがいいかは、人それぞれだろうと思う。ただし、rubyの規格では、デフォルトの表示がどちらであるか、決めていないのだ。

そこで、ruby-alignの出番である。これを使えば、アラインを指定できる。

いうまでもなく、ルビというのは日本語の表示に欠くべからざるものである。もし日本人が最初にHTMLやCSSを考案、実装していたのならば、ワードワラップなどうち捨てて、ルビを最優先で実装したことであろう。哀しい哉、日本はWebに出遅れ、かかる憂き目を見るとは。

2009-11-13

Windows 7におけるプリコンパイルドヘッダーのエラーについて

Visual C++ Team Blog : Visual C++ Precompiled Header Errors on Windows 7

Visual C++コンパイラをWindows 7で使うと、以下のようなエラーが出ると、顧客が報告してきた。

fatal error C1859: 'stdafx.pch' unexpected precompiled header error, simply rerunning the compiler might fix this problem

このエラーは、以下のような条件を満たすと起きる。

  • Visual C++コンパイラがWindows 7上で実行される。
  • プリコンパイルドヘッダ(PCH)が有効になっている。
  • /analyzeが有効になっている(これは必ずしも必要条件ではないが、発生率を上げてしまう)

エラーメッセージの提案である、「単にもういちどコンパイルしてみる」は、おそらく問題を解決しないであろう。実際、この問題は、単なる問題ではなく、プリコンパイルドヘッダーの設計上の都合と、Windows 7における、新しいセキュリティ拡張の影響なのだ。

Visual C++ プリコンパイルドヘッダーとASLR

プリコンパイルドヘッダーファイルは、コンパイルされた「状態(state)」を保存しており、そのstate情報は、後のコンパイルに再利用され、ビルドのスループットを大幅に向上させるものである。過去15年に渡って、我が社のコンパイラは、プリコンパイルドヘッダーをディスクに書き込み、仮想メモリに直接リロードするという方法を用いていた。その信頼性は99.999%であり、パフォーマンスも十分に得られた。この方法の欠点は、設計がやや、難しいということだ。

というのも、PCHファイルには、ポインタが含まれている。つまり、仮想メモリ上で、作成された時とまったく同じアドレスにロードしなければならない。もしPCHが、後々のコンパイルで、別のアドレスに読み込まれてしまったならば、ポインタは不正なものになってしまう。さらにややこしい問題には、PCHはポリモーフィック・オブジェクトを含むのだ。ポリモーフィック・オブジェクトは、それぞれ仮想関数テーブルポインター(VFTP)を持っている。このVFTPは、モジュール内の仮想関数テーブルを指し示しているのである。そのため、もしPCH内のポリモーフィック・オブジェクトが、あるモジュールの仮想関数テーブルに依存している場合、そのモジュールは、PCHが作成された時と、まったく同じ仮想アドレスにロードしなければならない。もし、モジュールが、後々のコンパイルで、別のアドレスにロードされてしまったならば、PCH内のVFTPのは不正なものになってしまう。

以上が、「依存しあうPCHファイルとモジュールは、コンパイルごとにアドレスが移動してはならない」ということに関する、長々しい説明である。Visual C++コンパイラは、この状態が正しいかどうかを、ビルド前に確認し、上記のエラーを出して、失敗するのである。後者(訳注:PCHファイルないのVFTPが参照するモジュールのアドレス)が、Windows 7では問題になる。Windows 7は、より強力なアルゴリズムを用いたAddress Space Layout Randomization (ASLR)を提供しているのだ。ASLRは、プロセス内のモジュールをランダムにリロケートしてexploitを狙うマルウェアを防いでくれる。VistaのASLR randomizationに対応するために、Visudal Studio 2008のコンパイラのモジュールは、 /dynamicbase:noオプションをつけてビルドされていた。これは、ランダム化がより強力となったWin7では十分ではないのだ。

この問題への修正するために、我々はまず、コンパイラのモジュールのベースアドレスを、どこか「安全(モジュールの衝突の起こる確率が十分に低い)」な場所に固定しようと試みた。残念ながら、我々の努力にもかかわらず、モジュールは、安全なアドレススペースの別の場所に移動してしまい、それが積み重なって、結局はPCHで使用されるモジュールはリベースされてしまうのだ。このような失敗の原因は、特定するのが難しく、大抵は複数の要因が積み重なっている。プロセスの生成順序であるとか(つまり、devenv.exeはcl.exeの使うモジュールを読み込む等)、ネイティブDLLローダーの為だ。我々はついに、バタフライ効果との戦いに負けてしまった。

解決方法

代価案は、かなりの大きい作業量が伴うか、さもなければ、パフォーマンスがひどく落ち込むものであった。我々はついに、PCHデータ構造に、自前のディスパッチ機構を実装することによって、仮想関数テーブルを取り除いた。このPCHデータ構造の「非仮想化」によって、我々は二つ目の障碍も取り除くことに成功した。コンパイラのモジュールは、プリコンパイルドヘッダーファイルを壊さずに、プロセス内を自由に移動できるようになったのだ。

この修正はVisual Studio 2010の最終リリースに提供され、また、Visual Studio 2008への修正パッチも、追ってリリースされる見込みである。もし、この問題に遭遇してしまったならば、以下の方法を試して欲しい。

  • /analyzeが有効であれば、無効にする。
  • クリーンビルドする。
  • コンピューターをリブートする。
  • PCHファイルを無効にする。

ワーナーの見づらいにも程があるデザイン

YouTube Succumbs To Branding As Warner Music Begins Its Return
YouTube、Warner Musicの復帰でブランディングに屈する

しかし、誰がこんな馬鹿げたデザインのページを見たがるというのか。ワーナーはもう少しまともなデザイナーを雇うべきでは無かろうか。

今週のサウスパーク

South Park: Dances With Smurfs

今回は、よく理解できなかった。スマーフは名前ぐらいしか知らないので実感がわかない。また、アメリカの馬鹿なテレビのニュース番組を皮肉っているようだが、それも見たことがないので、よく分からない。

どうも、これがネタじゃないらしいが。

ㄘんㄘん

常の如くGoogleReaderを見ていた所、不思議な文字列が目に入った。曰く、「ㄘんㄘん」と。

ちんちんがどうした。下品なことの好まるるは、世の常ではないか。いまさら目くじらを立てるには及ばぬ、という人もいるだろう。しかし、問題はそこではない。よく見て欲しい。

ㄘんㄘん
ちんちん

何と見た目が違うではないか。これは一体どういうことだろう。フォントを変えているのか? しかし、こんなフォントをインストールした覚えはない。WOFF(Web Open Font Format)という、web上でフォントを提供する規格が制定されつつあると聞く、しかし、私はFirefoxを使っていないし、第一、その様な最先端の規格を使っているようにも見えない。あるいは、CSSでウエイトや傾きを変えているのか? そうではない。これは、実は、違う文字なのだ。

'BOPOMOFO LETTER C' (U+3118)
'HIRAGANA LETTER TI' (U+3061)

これは、注音符号と呼ばれる、発音を表す文字である。1918年に、当時の北京政府によって考案されたが、後に、アルファベットを使うようになった。台湾では、今でも使われているらしい。

ㄅ ㄆ ㄇ ㄈ ㄉ ㄊ ㄋ ㄌ ㄍ ㄎ ㄏ ㄐ ㄑ ㄒ ㄓ ㄔ ㄕ ㄖ ㄗ ㄘ ㄙ ㄚ ㄜ 丨 ㄨ ㄛ ㄩ ㄝ ㄞ ㄟ ㄠ ㄡ ㄢ ㄣ ㄤ ㄥ ㄦ

ほかにもいくつか、日本のひらがなに似ているものがある。諸君も、今後はこのような幼稚な手に引っかからぬよう、注意されたがよい。ただ、キリル文字と似ていて、わざと使うのは、面白いかもしれぬ。

ㄙ-、ㄜてㄜて、ㄝっかㄑだからもっとつかおう。

2009-11-12

C++0x: Post Santa Cruz mailingの簡易レビュー

ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee

Post Santa Cruz mailingが公開された。今回は、面白そうなペーパーが多い。最新のドラフトは、N3000である。

n2979: Moving Swap Forward (revision 1)

decltype、戻り値の型の後置記法、関数テンプレートへのデフォルト引数の追加により、我々には、強力なSFINAEがもたらされた。このSFINAEのテクニックをサポートするため、誰もが必要とするであろうヘルパー関数を定義しようというもの。以下のようなコードだ。

template<class T>
 T&& value(); // not used

これは、decltypeの中で用いられることを想定している。

template<class To, class From>
 decltype(static_cast<To>(value<From>())) convert(From&&);

もし、このvalue()を使わない場合、上記のコードは、以下のようになる。

template<class To, class From>
  decltype(
    static_cast<To>(
      static_cast<From &&> (
        *reinterpret_cast<From *>(null_ptr)
      )
    )
  ) convert(From&&);

これは甚だ面倒であるし、また読みにくいので、当然、我々はvalue()関数と同等のものを、自前で書く事になるだろう。それほど一般的なテクニックなら、標準にいれてしまおうというものである。

このvalue()は、実行時に用いることを想定していない。どうやって、間違った使い方をコンパイル時にエラーにして、未然に防ぐかということも、提案されている。

また、swap(), forward(), move()といった関数と、このvalue()だけを定義した、別のヘッダを付け加えようとも提案している。なぜならば、この四つの関数は、言語側に近いのだが、これを使うために、<type_traits>を必要とするのは、やや大げさすぎるからだ。

N2980: SCARY Iterator Assignment and Initialization Revision 1

これは、コンテナのイテレータの実装に起因する問題で、コンテナのアロケータの型が違う場合、イテレータの実装方法によっては、要素の型さえ同じなら、同じように扱われてしまうという問題を取り扱っている。今まで、標準規格では、これを実装依存としてきたが、これを認めるように規定するものである。not dependantな実装のライブラリは、修正を迫られるだろう。

template < typename T >
class _List_Iterator {} ;

template < tyepname T, class Allocator = allocator >
class list{
    typedef _ListIterator iterator ;
}

この実装を、independantな実装という。逆に、コンテナにネストして、直接iteratorを実装しているようなコードを、not independantな実装という。このようなindependant実装をしている場合、イテレータは、コンテナのアロケータの型に依存しないので、アロケータの型だけ違うイテレータ同士の処理を、コンパイルエラーにできないというものである。

set<int, C1, A1>::iterator i1;
set<int, C2, A1>::iterator i2 = i1; // different comparator
set<int, C1, A2>::iterator i3 = i1; // different allocator

ここで、i3を、怖い初期化(SCARY initialization)という。アロケータの実装次第で、動くかどうかが変わるからだ。この提案では、この怖い初期化を、合法にしようというものである。そのためには、independantな実装が必須となる。

この初期化は、確かに「怖い」が、必ずしも「危険」とは限らない。それに、もしこの初期化がコンパイルエラーになったとしても、防げるバグはほんの一部である。この問題を未然に防ぎたければ、rangeなどのテクニックの方が、よほど役に立つ。そういうわけで、この怖い初期化を、規格で合法にする。

N2981: Proposal to Simplify pair (rev 3)

pairは、本来シンプルな型だったはずだ。ところが、やれmapのサポートだの、move semanticsだの、emplaceだの、scoped allocatorだの、様々な言語仕様の対応に追われて、恐ろしくふくれあがっている。いまや、pairクラスは、14個ものコンストラクタを持っている(暗黙に生成されるコンストラクタを除く)! これはどう考えても、ピザデブにも程がある。少し減らそうぜ、という提案。

N2982: Allocators post Removal of C++ Concepts (Rev 1)

コンセプト廃止に伴うアロケータの変更。また、アロケータがかなりのデブになってきているので、少し減量させようという提案もはいっている。

N2983: Allowing Move Constructors to Throw

N2855で、我々はmoveコンストラクタからのthrowを禁止した。だがちょっと待って欲しい。禁止したから何だというのだ。すでにthrowを禁止している部分は多く、いまさらmoveコンストラクタを禁止したからって、一体何になるというのだ。だいたい、throwが禁止されている状態で、テンプレートクラスでmoveコンストラクタを書こうとしたら、エキスパートレベルのテンプレートメタプログラミングのスキルが必要になってしまうではないか。throwぐらい認めてやろうではないか。

ただし、どうしてもthrowして欲しくない場合がある。それは我々も理解する所だ。従って、絶対throwしない場合のみ、moveをする、std::move_if_noexcept(x)を追加してやろう。また、最適化を容易にするために、新たなexception-specificationを追加してやろう。

なんか机上の空論っぽい提案なのだが、大丈夫なのだろうか。

N2984: Additional Type Traits for C++0x (Revision 1)

前回からの変更点。

  • Added is_constructible
  • Added is_nothrow_constructible
  • Made is_convertible a synonym for a two argument is_constructible
  • Renamed enum_base as underlying_type
  • Added Acknowledgements

以上。

N2985: C and C++ Thread Compatibility

名前の通り。提案ではなく、意見。

N2986: Equality Comparison for Unordered Containers

unorderd系のコンテナが、等しいかどうかを比較することについて論じている。つまり、operator ==()を実装するかどうかという話だ。このペーパーでは、ある種の問題では、二つのコンテナが等しいかどうかという情報は有益であることを述べている。さらに、等しいということを、どうやって定義するかという問題についても論じている。この提案では、二つのコンテナのsize()が等しく、要素の順序を問わず、すべての要素が、互いに等しければ、コンテナは等しいとしている。

unorderdコンテナ同士の比較は、どのように実装しても、かなり遅くなると思われる。もちろん、size()が異なるならば別だが。

N2987: Defining Move Special Member Functions

copyableとmovableなメンバが同居するクラスの、デフォルトのコピーコンストラクタ、moveコンストラクタの生成をどうするかというお話。

N2988: LWG Issue 897 and other small changes to forward_list

forward_listのerase_afterは、戻り値を返しても意味がないので、voidにしよう。また、規格にtypoがあるので、その修正。

N2989: Unified Function Syntax

まだまだ規格入りしない。頼むぞ、お前には期待しているのだ。私を失望させるな。

N2990: Core issue 789: Fixing Raw Strings wrt. Trigraphs

トライグラフを廃止したいが、IBMが自社コードに互換性の問題があると報告してきた。トライグラフを廃止しつつも、IBMに配慮するような提案。

N2991: Core issue 743: decltype(...) name qualifiers

decltypeにnested-name-specifierが使えない問題の修正。今回の改訂は、文面の変更だけ。

N2992: More Collected Issues with Atomics

Atomicに関する、文面上の細かい修正。

N2994: constexpr in the library: take 2

constexprに関する諸問題を取り扱っている。constexprが、引数に値しか認めないことを、テンプレートコードとの絡みで問題になるとしている。ただし、conceptexpr自身の問題は、単に紹介だけにとどめている。このペーパーでは、ある種の標準ライブラリでは、constexprなコンストラクタが有益であり、当然提供するべきであると提案している。

N2996: A Simple Asynchronous Call

改訂版。意見を取り入れて、少し規格が変わっている。単なる文面程度の変更ではなく、注意すべき変更点がある。

N2997: Issues on Futures (Rev. 1)

これは、N2996とは、別々のペーパーになっているが、N2996の改訂をするにあたっての、補助的な提案である。N2996では、is_ready()が廃止されているが、is_ready()を規格から取り除くように出来るように、その他の部分の整合性をとる変更である。

N2998: Reaching Scope of Lambda Expressions

N2977で、lambdaの参照できる範囲を、 immediately-enclosing function or lambda expressionに制限したのだが、これはきつすぎる制限で、現実のコードが悲惨なことになってしまう。

そこで、immediately-enclosing function or lambda expressionから、innermost enclosing function and its parametersに変更されるようだ。これで、冗長なコードを書かずにすむ。おかしいとは思っていたのだ。

N3010: Rvalue References as "Funny" Lvalues

rvalue referenceの戻り値を、lvalueとして扱いたいが、少々問題がある。そこで、"Rref lvalue"というファニーなlvalueを付け加えようというお話。

Post Santa Cruz mailingではないが、参考の為に簡易レビュー。

N2951: forward

forward()は、最終的には、単にstatic_cast<T&&>(u)を返すに過ぎぬのだが、スマートな実装をすべきかという議論。六つの実装例と、六つのテストケースを示して、この問題について論じている。N3000には、採用されたらしい。

しゃっくり

昨日今日と、しゃっくりに悩まされている。私は、いまだかつて二日に渡ってしゃっくりに悩まされたことはない。実に困った。

しゃっくりに対する民間療法は様々伝えられている。息を止めるのがいいと主張する者もいれば、逆に慎重に行うべきだと主張する者もいる。水を飲む。あるいはお湯を飲む。誰かに驚かしてもらう。一番消極的なのは、何か他のことに集中していれば、忘れた頃に止まるというものである。

ところで、この機会にしゃっくりについて調べた所、多くの言語で、しゃっくりの際に発せられる音から、単語ができているらしい。もっとも、あまりに似すぎている言語もあるので、借用語なのかもしれない。

2009-11-11

名を残すには文才が必要

それにしても、柳田國男の文章はすばらしい。なるほど、後世に名を残すには、文才が必要なのだな。例えば、宮本常一や網野善彦も、劣らず優れていると思うのだが、彼らの名は、柳田ほどには知られていない。

遠野物語では、丈高き人(たけたかきひと)とかいう表現が、いとも自然に使われていたりする。今読んでいる所では、徒然草のむく犬など、思わずニヤリとする表現が随所に見られる。

とにかく、恐ろしくたくさん物を読んでいるのだなとは思う。

思えば、古くは孔子もそうだった。仲尼と愉快な仲間達は、今風にいえば、就職に失敗して、自分探しの旅にでた者どもであった。ただし、その弟子の弟子(柳宗元の論語辯によると、曾子の弟子らしい)が書いた論語が大当たりした為、人気になった。孔子自身が書いたという春秋は、どうもそれ自体の価値は、それほどではないのではないかと思う。というのは、後々に、左傳などの注釈付きの春秋云々がいろいろと作られ、世に行われているのは、もっぱら注釈書の方である。

落ちた

どうも、番号がないので、曹候補生の試験に落ちたようだ。次は二士か。厳しそうだな。

そもそも、今回何人ぐらい受けたのだろう。倍率はいくつなのだろう。

W3CとHTML5のruby要素の違い。

Ruby Annotation
HTML5 ruby

昔々、あるところにruby要素なるものがあった。IEと呼ばれている古代のブラウザによって実装せられていた。

ここに、w3cといういう奴らがやってきて、標準化しようとした。これが、まことに複雑怪奇なものであって、誰も実装しようとしなかった。いまだに、誰もw3cのrubyは実装していない。以下に、その複雑怪奇な例を示す。

<ruby>
    <rbc>
        <rb>複</rb>
        <rb>雑</rb>
        <rb>怪</rb>
        <rb>奇</rb>
    </rbc>
    <rtc>
        <rt>ふく</rt>
        <rt>ざつ</rt>
        <rt>かい</rt>
        <rt>き</rt>
    </rtc>
    <rtc>
        <rt>ruby要素の例</rt>
    </rtc>
</ruby>

これは、例えば、以下のように表示される。

ふくざつかいき
複雑怪奇
ruby要素の例

実に複雑なのだ。まず、rubyを表示したいテキストは、ruby要素で囲む。まず、通常のテキストを、rb要素で囲むのだが、rb要素は複数用いることができる。複数用いる場合には、rbc要素で囲む。次に、上下のルビを振っていくのだが、ルビはrt要素で囲む。複数のrt要素を使いたい場合には、rtc要素で囲む。rtc要素は、ruby要素中に、一つか二つ、用いることができる。二つ用いた場合は、横書きの場合は上下、縦書きの場合は、左右のルビになる。

さっぱり理解できなかったとしても、それは恥ではないので安心してもらいたい。第一、先ほどもいったように、w3cのルビを実装しているブラウザは、この世に存在しない。おそらくは、ブラウザ開発者の誰も、これを理解できなかったからに相違ない。よって、覚える価値もない。さて、ではHTML5はどうか。

<ruby>
    簡<rt>かん</rt>
    単<rt>たん</rt>
    明<rt>めい</rt>
    白<rt>はく</rt>
</ruby>

実に簡単である。残念ながら、HTML5では、下、もしくは左にルビを配置する方法はない。とはいえ、そのようなルビの必要性は低いし、この記述の簡単さには代え難い。

HTML5は、現実の実装から、規格化している。w3cは理想に走りすぎていた。その結果、誰からも実装されなかったのだろう。

ところで、完全を期すために、一応、通常のルビの使い方の説明もしておく。これは一文字ずつ丁寧にルビを当てた場合であって、たいていの場合は、これで足りる。

<ruby>簡単明白<rt>かんたんめいはく</rt></ruby>なルビの<ruby>使<rt>つか</rt></ruby>い<ruby>方<rt>かた</rt></ruby>の<ruby>例<rt>れい</rt></ruby>。

がしかし、これを、rubyをサポートしていない時代遅れで多言語サポートへの配慮に欠けるクソなブラウザで閲覧すると、以下のように表示されてしまう。

簡単明白かんたんめいはくなルビの使つかい方かたの例れい。

これでは困るので、rp要素というものがある。これは、正しくルビをサポートしているブラウザでは、無視される要素である。

<ruby>簡単明白<rp>(</rp><rt>かんたんめいはく</rt><rp>)</rp></ruby>なルビの<ruby>使<rp>(</rp><rt>つか</rt><rp>)</rp></ruby>い<ruby>方<rp>(</rp><rt>かた</rt><rp>)</rp></ruby>の<ruby>例<rp>(</rp><rt>れい</rt><rp>)</rp></ruby>。

使用例。

(馬鹿にしか見えないテキスト)おいおい、お前のブラウザ、rubyもサポートしてないかよw 遅れてるなぁw 使ってて恥ずかしくないのか?(pgr
簡単明白(かんたんめいはく)なルビの使(つか)(かた)(れい)

ルビをサポートしていないブラウザにとっては、以下のように見えるはずだ。

簡単明白(かんたんめいはく)なルビの使(つか)い方(かた)の例(れい)。

まだいくらかマシなハズである。

2009-11-10

Chromeがいつのまにかruby要素をサポートしている件について

なんとなく、Dev版のChromeで、芥川龍之介(あくたがわりゅうのすけ)澄江堂雑記を、青空文庫(あおぞらぶんこ)で見やうとしたところ、なんと、ruby要素が表示されているではないか。一体いつの間にこうなったのだろう。

確か、webkitに、ruby関連らしいコミットがあったハズだが、最新のwebkitでは、すでに実装しているということだろうか。

ともかく、これで、

山吹き色の波紋疾走(サンライトイエローオーバードライブ)

青緑波紋疾走(ターコイズブルーオーバードライブ)

銀色の波紋疾走( メタルシルバーオーバードライブ)

緋色の波紋疾走(スカーレットオーバードライブ)

生命磁気への波紋疾走(せいめいじきへのオーバードライブ)

事故(ジコ)(ヤツ)は・・・ 不運(ハードラック)(ダンス)っちまったんだよ・・・

とかいう文章を、Chromeで正しく表示させることができるわけだ。

ところで、芥川龍之介の澄江堂雑記は大変面白い文章であるから、諸君も一度は読まれたがよい。

追記:Bloggerは、記事中のあらゆるTHMLタグをエスケープして、フィードで出力する。Google Readerは、p要素や、div、object、embed、div、等を、アンエスケープして表示してくれるようだが、ruby要素にまでは行き届いていない。ruby要素にも対応して欲しいものだ。

Firefoxのリリースから、まだ五年しかたってないらしい

Five Years Of Firefox

驚いた。まだ五年しかたっていなかったのか。しかし、相変わらずThoeraの画質はクソすぎる。

驚くべし。これで、動画ストリーム部分に1600kbpsも浪費しているのだ。

2009-11-09

ウラジーミル・ナボコフの遺稿が出版されるらしい

『ロリータ』作者の未発表小説、英米で17日発売 国際ニュース : AFPBB News

ロリータは、いつか原書で読みたいと思っているのだが、何しろ、あれはいかにもロシア人が書いた文章臭くて、読むのに辟易とする。やたらと無駄に長いわりに、話は遅々として進まないのだ。ただし、「あとがき」が面白かった覚えがある。あとがきだけ、原文で読みたい。

so...

ソフトウェア技術者のための英語(9: So):An Agile Way:ITmedia オルタナティブ・ブログ

OK, here's the video, but it is in Japanese so you won't understand.
ではここでそのビデオをお見せします。このビデオはあなた方には分からないように日本語になっています!

なんと、「ぼくは東洋の秘密を持ってきたが、あなた方アメリカ人に分かられては困るので、分からないように日本にしました。」と聞こえて、これが(ぼくのキャラクターと相まって?)とても面白いジョークに聞こえたらしい。

そういえば、そんな感じというような気もする。これは気をつけなければ、笑われてしまう。ことによると、自分もこの手の間違いをしていたかもしれない。

開明墨汁

つけペンのインクに悩んでいた所、墨汁を使っている人が、少なからずいるらしい。墨汁でも特に、開明墨汁がいいという。さっそく開明墨汁を買ってきた。開明墨汁は、アニメイトに売っていた。

なるほど、確かに書き味がいい。

吾笑ヒヲ止ムコト能ハズ

「伸太、部屋ニ駆ケ込ミテ、号泣シテ曰ク」 ドラえもんを漢文訓読してみた:アルファルファモザイク - 2ちゃんねるスレッドまとめブログ

之、哆啦A夢ヲ漢文ノ如ク読ミ下シタルスレ也。面白キコト限リ無シ。

2009-11-08

外国人参政権の是非に関する外国人の意見

聞説、民主党が、外国人に対して、地方参政権を与えようとしている云々。

IRCで外人と論じた所、少なからぬ外人が、そこに長年住んでいる外国人が地方参政権を得ることは、当然の権利であると考えている。そして、地方参政権たりとも認めるべからずという意見の私を、閉鎖的で差別主義の極右と見なすのである。議論の結果、私は、彼らが如何にしてさういふ意見を持つかという理由が、なんとなく分かったような気がする。

まず、特にヨーロッパと旧ソ連あたりに多いが、彼らは多重国籍が普通という環境で暮らしている。これも、何も好きこのんで複数の国籍を得たわけではなく、戦争や独立などといった、いろんな要素が絡んでいると思われる。

また、イタリアのように、本来都市国家だった所もあり、国という概念があまり強くないのかも知れぬ。これは、四方を海に囲まれ、基本的に一国だった日本とは、大きく異なる。もちろん、アイヌや琉球や佐渡あたりは、また微妙な問題もあるとはいえ、やはり日本は一国である。歴史を省みれば、日本が分裂していてもおかしくなかったのではないかという事件は複数あるのに、結局、一国だったというこの史実は、未だによく理解できないが、とにかく一国であった。そういうわけで、日本は多重国籍を禁止している。

そういう国の概念が薄い人間にしてみれば、そこに住んでいて、ちゃんと税金を納めているのだから、地方参政権ぐらい与えられるべきだという考えが生まれるのかも知れない。事実、欧州の多くの国が、外国人の地方参政権を認めている。

何にせよ、私は政治や歴史に詳しくない。この辺の事情を簡潔にまとめた本があれば、読んでみたい次第である。

神隠しについて

柳田國男の山の人生を読んでいる。

古来より今に至るまで、子供がいなくなるということは、よくある話だ。もちろん、その理由は、道に迷っただとか、家出や自殺、あるいは誘拐にあったなど、何らかの事件事故に巻き込まれたことによる。

ところで、当時の人達は、これは神隠しにあったに違いないと考えた。古くは鬼、後に天狗や山姥によって連れさらわれると考えた。

その対処も面白い。近所の人達で、鉦太鼓を鳴らし、「太郎かやせ、次郎かやせ」と叫びながら、練り歩くらしい。明らかに、何か超自然的な存在から、子供を取り替えそうという意図がみてとれる。また、子供の使っていた茶碗を叩くというのもあるらしい。茶碗と叩くというのは、大抵の農家では忌み嫌われているのだが、それというのも、飯茶碗を叩くというのは、「今から飯をくれてやるぞ」という意思表示であり、そういう神を呼び寄せる効果があるのだとか。だから禁忌をあえてして、そういう神を呼び出しているのだとか。

あるいは、「鯖くった誰それやーい」と呼ぶこともあるのだとか。これは、天狗というものは鯖を嫌うらしく、誘拐した者が、鯖を食っていると分かったならば、直ちに返すであろうという意図があるらしい。

次に、「赤豆飯を食えよ」と呼ばわる場合もあるのだとか。神は赤飯が好きらしい。これは、柳田國男にはその理由が分からないとしてあるが、神が赤飯を好むということ、また「食えよ」というのは、意味的に、「今から食わしてやるぜ」という感じがするので、赤飯をエサに神を釣ろうという考えではあるまいか。

古来より、何百年も生きたと自称する人間が複数いた。そういう人間は、なぜか常陸坊海尊を名乗っていて、源平合戦を、まるで見てきたかのように語ったという。どうも、常陸坊は、行方不明になっていたため、自称するのに都合がよかったらしい。

【そんなヤツ】ネタ求人?を晒すスレ【いねーよ】

【そんなヤツ】ネタ求人?を晒すスレ【いねーよ】 働くモノニュース : 人生VIP職人ブログwww

私も以前、一度だけハロワに行ったことがあるが、この手の求人ばかりでげんなりした記憶がある。明らかにおかしいだろう、矛盾しているだろう、誰がこれを満たすんだよ、と叫びたくなる条件が、多々見受けられるのだ。

こういう意味不明な求人が多いという背景には、ブラックもさることながら、ハロワ側にも問題があるのではないかという人もいる。すなわち、見かけ上の有効求人倍率を上げるために、求人していない会社にしつこくせまり、その結果、会社側としても、ふざけた求人で答えるらしい。

はてさて、日本の実質の有効求人倍率はいくつなのやら。

2009-11-07

奈良先端大のIRCサーバー、停止さる

ともちゃ日記 - 元大学生のぐだぐだ日記、ジジイと呼ばれたOL生活-

奈良先端科学技術大学院大学に設置されているIRCサーバー、 irc.nara.wide.ad.jpが、サービス停止になるようだ。

私は今でも、IRCがもっとも優れたチャットプロトコルだと信じている。考えようによっては、IRCとTwitterは似ているのではあるまいか。IRCは、一つのチャンネルに百人以上いることもあるが、積極的に発言するのは、大抵数人である。IRCの空気は、リアルタイムな会話というより、掲示板に近い。適当に何かを発言すると、数分から数十分後に、誰かが反応する。興味がない者は無視する。

LiveだのYahooだのと、チャットサービスは様々あるが、未だにIRCに勝るチャットはない。IRCプロトコルはオープンであり、その実装であるIRCクライアントは多数あり、好きなものを選べる。

ただ一つ気になるのは、日本のIRCサーバが、いまだにことごとくshift-jisを使用していることである。もちろん、IRCにおいては、文字コードというのは、サーバー側の設定ではなく、クライアント側の設定なのだが、日本のIRCサーバーは、慣習的に、shift-jisになっている。今の時代は、utf8にするべきだ。そんなわけで、私は主に海外のIRCサーバーにたむろしているわけである。utf8を解さない馬鹿外人も多いのだが。

2009-11-05

マヨヒガ

柳田國男の遠野物語を読んだ所、マヨヒガなるものが出てきた。あの有名なマヨヒガか、と読んでみた所、実に短い記述であった。どうも調べた所によると、マヨヒガが全国的に知られるようになったのは、外ならぬ遠野物語のためであるらしい。

六三 小國の三浦某と云ふは村一の金持なり。今より二三代前の主人、まだ家は貧しくして、妻は少しく魯鈍なりき。この妻ある日門の前を流るゝ小さき川に沿ひて蕗を採りに入りしに、よき物少なければ次第に谷奥深く登りたり。さてふと見れば立派なる黑き門の家あり。訝しけれど門の中に入りて見るに、大なる庭にて紅白の花一面に咲き雞多く遊べり。その庭を裏の方へ廻れば、牛小屋ありて牛多く居り、馬舎ありて馬多く居れども、一考に人は居らず。終に玄關より上りたるに、その次の間には朱と黑との膳椀をあまた取出したり。奥の座敷には火鉢ありて鐵瓶の湯のたぎれるを見たり。されども終に人影は無ければ、もしや山男の家では無いかと急に恐ろしくなり、駆け出して家に歸りたり。此事を人に語れども實と思ふ者も無かりしが、又或日我家のカドに出でて物を洗ひてありしに、川上より赤き椀一つ流れて來たり。あまり美しければ拾ひ上げたれど、之を食器に用ゐたらば汚しと人に叱られんかと思ひ、ケセネギツの中に置きてケセネを量る器と爲したり。然るに此器にて量り始めてより、いつ迄經ちてもケセネ盡きず。家の者も之を怪しみて女に問ひたるとき、始めて川より拾い上げし由をば語りぬ。此家はこれより幸運に向かひ、終に今の三浦家と成れり。遠野にては山中の不思議なる家をマヨヒガと云ふ。マヨヒガに行き當たりたる者は、必ず其家の内の什器家畜何にてもあれ持ち出でて來べきものなり。其人に授けんが爲にかゝる家をば見する也。女が無慾にて何物をも盗み來ざりしが故に、この椀自ら流れて來たりし成るべしと云へり。

○此カドは門には非ず。川戸にて門前を流るゝ川の岸に水を汲み物を洗ふ爲家ごとに儲けたる所なり。
○ケセネは米稗其他の穀物を云ふ。キツは其穀物を容るゝ箱なり。大小種々のキツあり。

六四 金澤村は白望の麗、上閉伊郡の内にても殊に山奥にて、人の往来する者少なし。六七年前此村より栃内村の山崎なる某かゝが家に娘の壻を取りたり。此壻實家にいかんとして山路に迷ひ、又このマヨヒガに行き當たりぬ。家の有様、牛馬鶏の多きこと、花の紅白に咲きたりしことなど、すべて前の話の通りなり。同じく玄關に入りしに、膳椀を取出したる室あり。座敷に鐵瓶の湯たぎりて、今まさに茶を煮んとする所のやうに見え、どこか便所などのあたりに人が立ちて在るやうにも思はれたり。呆然として後には段々恐ろしくなり、引き返して終に小國の村里に出でたり。小國にては此話を聞きて實とする者も無かりしが、山崎の方にてはそはマヨヒガなるべし、行きて膳椀の類を持ち來たり長者にならんとて、壻殿を先に立てゝ人あまた之を求めに山の奥に入り、こゝに門ありきと云ふ處に來たれども、目にかゝるものも無く空しく歸り来りぬ。その壻も終に金持になりたりと云ふことを聞かず。

なんと、この程度の量の文章から、知られるようになったらしい。

ところで、柳田國男は、まだ著作権が切れていないらしい。まあ、上は引用の範囲であろうから、問題はないだろう。

今週のサウスパーク:Fワード

South Park: The F Word

今週のサウスパークは、珍走団について。

今回の話は、あまり面白くない。なんであのFagどもをブッ殺さないのか。

faggot - Wolfram|Alpha
fag - Wolfram|Alpha

思えば、先週は最高のエピソードだった。

2009-11-04

min()とmax()が見直されそうな雰囲気

C++0xでは、min()とmax()は可変引数であるが、以下のように書かなければならない。

int a = max({1,2,3,4,5,6,7,8,9,10}) ;

何故ならば、min()/max()は、initializer_listを使うからである。

こうなった理由というのも、ベンチマークをとったら、initializer_list版の方が、早かったかららしい。

私は、そのベンチマーク結果に疑問を持っていた。Variadic Templateの遅くなる理由が見あたらない。実装の問題では無かろうかと。

どうも、そんな動きがあるようだ。詳しくは、またペーパーがでるものと思われる。

韓愈

寒い晩、男が一人、油明かりの元で、上書を認めている。

嗚呼、思えば己は不遇だった。生まれて三歳にして、厳父にそむかれて狐となった。少時より書を好み、読むこと日々に数千百言、能く六経百家に通じ、略古今を知る。進士に及第したのは、貞元八年、二十五歳の時だ。己はもっと早く名を挙げているべきだったのだ。貞元十九年にして、ようやく監察御史となるも、宮市の甚だしきを上疏して、流された。

思えば、己は体も弱かった。まだ三十だというのに、歯が一本抜け落ちた時は、さすがの己も慌てた。己はこのまま死ぬのではなかろうかと、真剣に悩んだ。歯が一本ないだけで、物食らうことを妨げるし、周りの歯もぐらぐらして、うがいも怖い。後を追って、二本、三本と抜けていくごとに、己は言いようのない恐怖を感じた。

クソッ、何故だ。何故、己はいつもこうなのだ。己が一体何をしたというのだ。己はそこらにいる馬鹿どもよりは、よほど気が利いている。己は書を読んでいる。己は老仏に惑わされぬ。己は常に正しいことしか言っておらぬ。その報いがこれでは、あんまりではないか。

就中、今回のことだ。鳳翔にあるとかいう、仏の指の骨を宮中に迎え入れ、士をして地に這いつくばって拝ましめ、甚だしきに至っては、額を焼き指を焼き、衣を脱ぎ銭を散じ、朝より暮に至り・・・・・・。嗚呼、もはやこれは、中国の禮ではない。夷狄に属するものである。とすれば、彼はこれ夷狄か。

ああ、何故こんなにも、老仏思想がもてはやされるのか。己はさすがに堪えられず、直接、上表して訴えた。仏の汚らわしい骨など、水火に投ずべしと。それがどうだ。己は罪、死にあたるだと。クソッ、何だこれは。

幸い、同僚の諫めもあり、死罪一等を減じて、潮州刺史となり、この寒い地に来た。今から、到着の報告を書かねばならん。さて。

臣、狂妄戇愚、禮度を識らざるを以て、上表して佛骨の事を陳べ、言不敬に渉る。

狂妄戇愚だと、禮度を識らざるだと、言不敬だと。我ながらどうしてこんな言葉が出せるのか信じられぬ。一体己の何が悪かったというのだ。

聖恩宏大、地量る莫し。腦を破り心を刳くとも、豈謝を為すに足らんや。臣某誠惶誠恐、頓首頓首。

何だこれは。己の長年の読書は、長年の学は、詩作は、このような空虚な文を書く為のものだったのだろうか。

臣、正月十四日を以て,恩を蒙りて潮州刺史に除せられ、即日奔馳して道に上り、嶺海を經渉す。

嗚呼、拏よ。思えばお前は不幸な子であった。旅立ちに臨んでお前の顔を見るに、今度の旅に堪えられぬ事を恐れた。まさか、その恐れの通りになってしまうとはな。仮に道ばたに葬った。棺も間に合わせのものに過ぎん。埋めてすぐに行かねばならなかった。嗚呼、お前を誰か守り、誰かみるというのか。今もお前の顔が、脳裏に浮かんで離れない。己のせいなのか。だが、己のどこが悪かったというのだ。

参考:論仏骨表、潮州刺史謝上表、落歯、祭女拏女文

祭女拏女文は、日本ではマイナーだと思われるので、ここに引用しておく。

維年月日、阿爹阿八、使汝妳以清酒時果庶羞之奠、祭於第四小娘子挐子之靈。嗚呼、昔汝疾極、值吾南逐。蒼黃分散、使女驚憂。我視汝顏、心知死隔。汝視我面、悲不能啼。我既南行、家亦隨譴。扶汝上輿、走朝至暮。天雪冰寒、傷汝羸肌。撼頓險阻、不得少息。不能食飲、又使渴饑。死於窮山、實非其命。不免水火、父母之罪。使汝至此。豈不緣我。草葬路隅、棺非其棺。既瘞遂行、誰守誰瞻。魂單骨寒、無所托依。人誰不死。於汝即冤。我歸自南、乃臨哭汝。汝目汝面、在吾眼傍。汝心汝意、宛宛可忘。逢歲之吉、致汝先墓。無驚無恐、安以即路。飲食芳甘、棺輿華好。歸於其丘、萬古是保。尚饗。

韓愈の文章の中でも、だいぶ変わっている文章だと思う。この文は韻を踏んでいない。以清酒時果庶羞之奠という所だけは、決まり文句なので、かろうじて韻が入っているが、それだけである。そこにも、普通、以清酒庶羞之奠であるのに、時果が入っていることから、親の愛情を感じる。

2009-11-03

theoldnewthing: おい、誰が受賞するよ

The Old New Thing : Hey, is there somebody around to accept this award?

1990年代の後半、とある大手のインターネット団体が、カテゴリ別の、ベスト賞を決めようとしていた。ベストWebサーバー賞だとか、ベストWebブラウザー賞だとか、そういった物だ。そのカテゴリの中に、ベストWeb編集ツール賞なるものがあった。

我々はこのような受賞が行われていることをまったく知らなかった。その団体はWindows チームに連絡してきて訊ねた、「やあ、マイクロソフトにベストWeb編集ツール賞を差し上げたいんだけど、メモ帳の作者って誰かな。授賞式に招きたいんだけど」と

何と、メモ帳がベストWeb編集ツール賞を受賞したのだった。

チーム内にメールが飛びかった。「おい、誰がメモ帳を書いたか覚えてるか?」

その時代ですら、メモ帳の作者は、時の霧の中に隠れてしまっていたのだった。確か、結局、授賞式に出席したのは、マルチラインエディトコントロールの作者だったはずだ。だいたい、メモ帳の価値は、そこにあるのだから。

まあ、すべてのWindowsユーザーのWeb開発者は、少なくとも一度は、メモ帳のお世話になっているはずだ。最強と自称するエディタはたくさんあるが、人それぞれ好みが違うので、最も多く使われているエディタとなると、そのOSに標準で入っているエディタということになるのだろう。

最近興味を持ったリンク集

告白されたときの正しいステータスコードの返しかた - As Sloth As Possible
エイプリルフールRFCにまで言及していて可なり。

斬新すぎるマルチ iPhoneレースゲーム Endless Racing Game
リアルで遊べそうだ。

Visual C++ Team Blog : Visual C++ Code Generation in Visual Studio 2010
VS2010の改良点について。そろそろ時代は64bitかな。

Tarzan Fail « FAIL Blog: Epic Fail Pictures and Videos of Owned, Pwnd and Fail Moments
ターザン fail! これはひどい。"Doesn't look like Tarzan needs Jane anymore."

もみあげチャ~シュ~:彼氏が博多出身だった・・・
言葉遊び。

最近の自分のGoogle Readerアカウントのスター付きの奴を持ってきてみた。そのほかのスター付き記事は、たいてい、猫か犬の画像ないしは動画である。

甘酒のうまい季節になった

何か食べるものを買いに行こうと外に出たら、恐ろしく寒い。ほうほうの体で、近所のスーパーにたどり着いた。見ると、酒粕が売っている。思わず買って帰った。

さっそく、甘酒をつくって飲む。実に体が温まる。これはいい。今年の冬は、甘酒でしのごう。

2009-11-02

米のうまい炊き方

米である。やはり日本人としては、何としても米を食はねばならない。かくいふ自分も、例外なく毎日、米を食ふているのだが、毎日格闘せざるべからざる問題がある。如何にして米を炊くか、すなはちこれなり。

米を如何にして炊くかといふのは、難しい問題である。今日では、電気釜の普及によつて、我々は、この問題をあまり意識してをらぬ。ところが、未だに電気釜を所有せざる余としては、実に重大なる問題となつてゐる。

余の家は、少時より、電気釜を使つてゐなかつた。これは何故かと云ふに、どうも、我が父、電気釜を嫌ふこと甚だしきによってらしい。さういふわけで、余としても、電気釜には、どうもなじめない。そこで、毎日毎日、鍋で米を炊いてゐる。今回は、米のうまい炊き方について述べてみやうと思ふ。

まず、米ごとに特性がまるで違ふ。それは、たんに米の品種にとどまらず、売つている米袋ごとに、特性が違つてゐる。主に問題になるのは、水の量である。よく、米の1.5倍の水と云ふけれど、これは、流通の発達せる現代においては、当てはまらぬ。新米ほど、水は少なめに使はなければならぬ。

米を洗つて、適当な分量の水につけたら、そのまましばらく放つておく。少なくとも三十分はみなければならぬ。

次に火加減である。まず、最初は強火を以てす。吹きこぼれそうになつたら、弱火にする。では、この後どのくらい弱火にしておけばよいのかということであるが、これがなかなか難しい。余は、火を強めても吹きこぼれないやうになるまでは、弱火にしておくべきだと思ふている。時間にして、五分から八分程度であろう。

次に、中火にする。どのくらい中火にすればいいかということであるが、これもまた難しい。余の目安は、水が蒸発するようなチリチリという音が、ほとんどしなくなるまでである。

然る後に、火を止めて、十分から十五分ぐらい、放置する。

余はいつも、あたらしい米を買つてくるたびに、焦がしてしまふ。どうにかならぬものだろうか。

2009-11-01

柳田國男がすごい

高校時代、にわかに柳田國男にハマり、彼のまとめた、地域ごとの昔話の差異のおもしろさを喧伝している友人がいた。当時の私は、柳田國男という名前は、一般常識として知っていたが、彼の著作を、ひとつだに読んだことがなかったので、その実否を質すことができなかった。

さて、近日、たまたま定本柳田國男集を手に入れ、昨日、その第一巻の初めを読んだ。なるほど、確かに柳田國男は、すごい男であった。

ただ、ひとつ気になる点がある。というのも、彼の著作は、多分に空想を含んでいるのではあるまいかということだ。とはいえ、空想を否定して、確実な証拠のみを元にした歴史というのも、当てにならないのである。というのも、当時の文章に残っている世界は、かなり偏りがあったからである。その辺は、網野善彦の著作にも詳しい。

さて、手に入れた所の定本柳田國男集は、35冊。別巻五を欠く。ただし、別巻五は索引と年表らしく、無くてもそれほど差し支えあるまい。第一、この35冊の分量を読むには、数年を要すると思われる。