2009/12/04

時代の変化を拒絶する人々

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

どうやら、Google日本語入力の予測変換を気に入らぬ人がいるようだ。曰く、「予測変換は偏っている」、「予測変換に我々の日本語が支配せられてしまう」、云々。

一昔前の、「ワープロでは正しい日本語が書けない。宜しくペンを使うべし」という議論を彷彿とさせる。この議論は、今や、「どうでもいいじゃないか」の一言で、片付けられてしまう。

実際、どうでもいいのである。時代というのは常に、「甲なのか乙なのか」と争っているうちに、「実はイロハなんだ、ABCDなんだ」といわれて、甲乙などどうでも良くなってしまうのである。

ただし、あえてこの議論に乗っかるとすれば、予測変換の利点と、彼ら時代遅れの老害の非を、上げることはできる。

彼らはいう。「予測変換は我々の日本語を制限する。例えば、だ・である調で書きたいとしても、変換候補がです・ます調であれば、面倒なのでそちらを使ってしまうだろう」と。またいう、「予測変換は、時として不思議な変換を行うので、到底仕事で使うことはできぬ」と。

ひとつめの非難に対しては、ソフトウェアが学習機能というものを設けて、ユーザーの癖を学習すればいいだけの話である。ふたつめは、そもそも従来のかな漢字変換ですら、誤変換が多いのである。してみれば、彼らは従来のIMEをも、仕事で使うことはできぬであろう。

プライバシーの問題も叫ばれている。がこれは、別にGoogleに限った話ではないだろう。

ベジタリアンも喰える肉、開発さる

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Meat grown in laboratory in world first - Telegraph

とうとう人工肉が開発された。

やっていることは、生きている豚の筋組織を採取し、培養するだけである。

つまり、ベジタリアン(笑)も安心して食べられる肉だということだ。なぜなら、彼らの肉を喰わない理由というのは、主に、「動物様を屠殺するなんて許せん」、ということなのだから。その割には、彼らは植物様を殺して食べたり、植物様の命を弄ぶ品種改良された野菜を食べることには、躊躇しないようである。この矛盾を、彼らはどのように解決しているのか、それは私の知るところではない。

2009/12/03

Google日本語入力の面白予測変換集

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Google日本語入力の、面白予測変換の例をいくつか見つけたので、ここにメモしておく。

ただしま
ただし魔法は尻から出る
ただしい
ただしイケメンに限る
ひとがご
人がゴミのようだ
なにをする
何をするだァーッ
たいほ
逮捕しちゃうぞ
しんせいき
新世紀GPXサイバーフォーミュラ
こちらか
こちら葛飾区亀有公園前派出所
こいするお
恋する乙女と守護の楯
おさななじみ
幼なじみはベッドヤクザ
れれれ
レレレのおじさん
まほうじ
魔法陣グルグル
じょじょの
ジョジョの奇妙な冒険
やまだお
山田オルタナティブ
くそみ
くそみそテクニック
みこみこ
巫女みこナース
ぎおんし
祇園精舎の鐘の声
きみが
君が主で執事が俺で
まぶら
マブラヴオルタネイティブ
きみがの
君が望む永遠
きしんほ
機神咆吼デモンベイン
みらいへ
未来への遺産、未来への咆哮
とばくも
賭博黙示録カイジ
とばくは
賭博破戒録カイジ
らきすた
らき☆すた
きどう
機動戦士ガンダム
きどうぶ
機動武闘伝Gガンダム
はたらい
働いたら負けかなと思ってる
まほうしょ
魔法少女リリカルなのは
ちょうじく
超時空要塞マクロス、超時空世紀オーガス、超時空騎団サザンクロス
こうかくき
攻殻機動隊
よーし
よーしパパ特盛頼んじゃうぞー
あたらな
当たらなければどうということはない
ぼうや
坊やだからさ
おやじに
親父にもぶたれたことないのに
せいきま
聖飢魔、世紀末リーダー伝たけし、世紀末の魔術師、世紀末の詩、世紀末覇者
わたしが
私がクマにキレた理由
ざんぎゅ
ザンギュラのスーパーウリアッ上
いんどじ
インド人を右に
じゅげむ
じゅげむじゅげむナンタラカンタラ
ふとんが
布団が吹っ飛んだ
あつは
アツはナツいね
あるみかん
アルミ缶の上にあるミカン
それはわたしの
それは私のおいなりさんだ

どうも、主にエロゲのタイトルが、面白い予測変換を引き起こす原因となっているようだ。

ところで、たまに変換がおかしい場合がある。例えば、「お茶を淹れる」が変換できない。そもそも、「淹れる」という単語が存在しない。そういう場合は、以下から報告することができる。
誤変換報告フォーム - Google 日本語入力 ヘルプ

Google日本語入力がすごすぎる

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Google 日本語入力 - ダウンロード

Google日本語入力は、Yet Another Japanese IMEである。これはすごい。何がすごいかと入って、辞書だ。漫画やアニメの単語は、ほぼ一発で変換してくれるし、ネットスラングの類も、一発変換できる。さらに、サジェスト機能も優秀だ。

空条承太郎
空条徐倫
荒木飛呂彦
覇王翔吼拳を使わざるを得ない
滅殺豪波動
瞬獄殺、春獄殺
狼牙風風拳
界王拳
魔貫光殺砲
波紋疾走
二重の極み
北斗百裂拳
二指真空把
岩山両斬波
華山群狼拳
邪気眼
エターナルフォースブリザード
パッド長
幻想郷、紅魔郷、妖々夢、萃夢想、永夜抄、花映塚、風神録、緋想天、地霊殿、星蓮船
博麗霊夢、霧雨魔理沙、射命丸文、河城にとり

驚くべし。これらは皆、一発で変換できる。

2009/12/02

うげ、これはまずい

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

template<class InputIterator, class OutputIterator> OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result);

Requires: result shall not be in the range [first,last).

template<class InputIterator, class OutputIterator> OutputIterator move(InputIterator first, InputIterator last, OutputIterator result);

Requires: result shall not be in the range [first,last).

うわ、やっべ。俺の書いた既存のコード間違えまくりんぐ。

というのも、VC++のmoveやcopyは、メタプログラミングによって、使える場合はmemmove()を使っているので、コンパイルが通り、普通に動くのだ。

シンタックスシュガーとしてのlambdaの解説

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

本の虫: lambda 完全解説で、lambdaの全機能は、ほぼ網羅したと思う。lambdaの文法は、それほど難しくはないと思うのだが、難しいと感じる人がいるらしい。とくに、キャプチャが理解できない人がいるそうだ。そこで今回は、lambdaの根本を解説してみようと思う。

lambdaというのは、そもそも関数オブジェクトのシンタックスシュガーなのである。例えば、

namespace hito {

template< class InputIterator, class Function >
Function for_each(
    InputIterator first,
    InputIterator last,
    Function f )
{
    for ( ; first != last ; ++first )
        f( *first ) ;

    return f ;
}

}


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

    for ( int i = 0 ; i != 10 ; ++i )
        v.push_back(i) ;

    hito::for_each(v.begin(), v.end(),
        [](int x){ std::cout << x << std::endl ; }
    ) ;
}

このコードは、これ以上ないくらいに分かりやすいC++のコードである。今、for_eachのしていることを分かりやすくするため、自前で実装してみた。for_eachは、イテレーターに対して、引数として與えられた関数オブジェクトを呼び出すのである。関数オブジェクトは、この場合、lambdaである。

とはいえ、lambdaを理解できない人間は、このコードもまた、理解できないのである。では、以下のように書き換えてはどうか。

struct lambda
{
    void operator ()( int value ) const
    {
        std::cout << value << std::endl ;
    }
} ;


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

    for ( int i = 0 ; i != 10 ; ++i )
        v.push_back(i) ;

    std::for_each(v.begin(), v.end(),
        lambda()
    ) ;
}

このように関数オブジェクトを渡しているに過ぎないのである。lambdaは所詮、関数オブジェクトのシンタックスシュガーであることが、実感できたことと思う。

しかし、変数のキャプチャはどうか。例えば以下のコード。

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

    for ( int i = 0 ; i != 10 ; ++i )
        v.push_back(i) ;

    int sum = 0 ;

    std::for_each(v.begin(), v.end(),
        [&]( int value ){ sum += value ; }
    ) ;

    std::cout << sum << std::endl ;
}

これは実に不可思議である。lambdaが関数オブジェクトであるのはまだしも理解もできようが、なぜ、その関数オブジェクトであるlambdaが、定義された場所の関数の変数を、自分でも使えるのか、理解に苦しむ。こんな奇妙なことが、単なる既存の関数オブジェクトのシンタックスシュガーであるわけがない――と思うかも知れない。

しかし、依然としてlambdaは、関数オブジェクトのシンタックスシュガーなのである。

class lambda
{
private:
    int & sum ;

public :
    lambda(int & ref) : sum( ref )
    { }
    
    void operator ()( int value ) const
    {
        sum += value ;
    }
} ;


int main()
{
    std::vector v ;

    for ( int i = 0 ; i != 10 ; ++i )
        v.push_back(i) ;

    int sum = 0 ;

    std::for_each(v.begin(), v.end(),
        lambda( sum )
    ) ;

    std::cout << sum << std::endl ;
}

ご覧のように、lambda関数オブジェクトは、コンストラクタの引数で、その関数内の変数を参照に取るだけである。lambdaのキャプチャとは、根本的には、この程度のものなのである。

C++0x以前では、関数オブジェクトは、お世辞にも、活用されているとは言い難かった。既存の関数ポインタより圧倒的に便利なのだが、如何せん、その文法が汚い。上記のlambda関数はいずれも、たった一行の、短いコードである。本質的には一行のコードを書くために、関数オブジェクトは、あまりにも文法上必要なゴミが多い。そのため、使用を敬遠されていたのだ。lambdaなら、その場所に、直に書くことが出来る。

lisp、python、javascript、ruby、C#等の言語を話す人からみたら、C++のlambdaは、制限が多いように思われるかも知れない。しかし、強い静的な言語であるC++としては、仕方がないのである。

2009/12/01

oldnewthing: WIN32_LEAN_AND_MEANの由来

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

The Old New Thing : Where did WIN32_LEAN_AND_MEAN come from?

asdfはWIN32_LEAN_AND_MEANの由来が疑問であるようだ。

WIN32_LEAN_AND_MEANシンボルは、Windows 95時代に、windows.hをincludeした際、多くのヘッダファイルを省略する目的で導入された。windows.hを覗けば、どのファイルが省略されるのかが分かる。

このシンボルは16-bit Windowsから32-bit Windowsへの移行の一環として、追加されたものである。16bitのwindows.hは、多くのヘッダファイルをincludeしておらず、WIN32_LEAN_AND_MEANを定義することによって、16bit Windowsの、Windows プログラムを書くに当たって最小限のヘッダという思想を、取り戻すことができるようにするものであった。これは、ヘッダファイルを細かく自分で管理したいプログラマに歓迎されたし、また、利点もあった。というのも、このシンボルが作られた当時は、プリコンパイルドヘッダーファイルは、一般的ではなかった。私の記憶する限りでは、50MHzの80486に8MBのメモリでは、WIN32_LEAN_AND_MEANを使うことによって、Cファイル一個あたり、三秒のコンパイル時間の短縮に繋がったのである。あるプロジェクトで20個のCファイルを使っていたならば、実に一分間もの時間の節約になる。

ムーアの法則とプリコンパイルドヘッダーとが、WIN32_LEAN_AND_MEANを、ほとんど無用にしてしまった。もはや、時間の短縮にはならぬ。しかしある時代では、確かに役立っていたのだ。

帰宅

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

予備自補の訓練より帰宅。

予備自補の訓練で、前々から思っていることをひとつ。

予備自補の訓練は、五日間単位で行われる。駐屯地の中は、かなりの異世界である。どのように異なっているかを文章で表現するには、遺憾ながら私の筆の及ぶ所ではない。ただし、強いて拙筆を用いれば、駐屯地の中は、外との接点があまりないと、隔絶された別世界である。加ふるに規則正しい生活を以てす。

つまり、訓練は非日常であり、精神的に疲れるのである。精神が疲労すると、自ずと挙動に表れてくる。では、予備自補の訓練においては、どのような挙動に表れるのか。下ネタである。

人の精神というのは、以外と脆いものである。

ちなみに、すでに達観した感のある私としては、「枯楊春老更生稊(こやうはるおいてさらにていをしょうず)」といった具合なのだ。もうトシだろうか。そういう話にはついて行けない。

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は、依然として変数の型を指定しなければならないことに変わりない。何も変わっていないのである。

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というクラスがあり、これでもって、エンジンを初期化できる。

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

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

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位に坂本龍馬、先見の明と決断力が日本を変える!?

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

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

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

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

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

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

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

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

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ファイルを無効にする。