J

2004 | 08 | 09 | 10 | 11 | 12 |
2005 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 12 |
2008 | 01 | 02 | 04 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 03 | 04 | 05 | 06 | 07 | 08 | 12 |
2013 | 01 | 02 | 03 | 05 | 06 | 07 | 08 | 09 | 10 | 11 |
2014 | 01 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2015 | 02 | 03 | 04 | 05 | 06 | 07 | 10 | 11 | 12 |
2016 | 01 | 02 | 04 | 05 | 06 | 07 |

ホーム

日記内の"morihyphen.hp.infoseek.co.jp"へのリンクは切れてます。必要な場合はお手数ですが int.main.jp へ書き換えをお願いします。

TODO: ファイル名確認を忘れないこと > 自分

twitter

 | 

2006-10-08

C++ for Haskeller

http://haskell.g.hatena.ne.jp/mr_konn/20061006/1160138831

Haskellから入った人には、cinとかcoutとか微妙すぎてよくないだろ、と、思ったので、適当に。


対象読者は、Haskellわかるし、C++もわかる人(意味ねー)。ネタを理解できる人です。

C++とは

C++とは、以下のような特徴を持ったプログラミング言語です

  • 参照透明(!)
  • 出現評価(occurrence evaluation) (などといういかがわしい単語をつくる)
  • パターンマッチできる
  • 全く直感的でない構文
  • IOモナドを書くための大量のsyntax sugarがある

説明していくと長くなるので、それぞれ、おいおい必要になったときに説明するようにします。

C++の型

C++には、次の型があります。

まあ、おいおい説明。(はやくも飽きてきた)

変数

飽きてきたので、さっさとプログラムを紹介。値を名前に束縛できます。なお、整数、真偽値と、連想配列で、束縛の仕方が違うので注意!

int const n = 10;

整数値10を名前nに束縛します。なお、'const'は必須なので注意。(驚くべきことに、'int n = 10;'とした場合、IOモナドを生成するsyntax sugarになります!これについては後述(しないです))

Haskellで書くとこんな感じ

n :: Int
n = 10

なお、C++には残念ながら型推論が無いので、型を省略することはできません。

続いて、真偽値。

bool const b = true;

真偽値trueを名前bに束縛します。これもconst付けないとsyntax sugarになるので注意。


最後に連想配列連想配列の束縛のしかたは整数、真偽値と違うので注意。

struct AssocArray {
	static int const n = 10;
	static int const m = n+10;
	static bool const b = true;
};

名前'AssocArray'に連想配列 { n::int=>10, m::int=>20, b::bool=>true } を束縛します。

C++の世界では、連想配列を"type(型)"と呼びます!イカれた名前ですね!連想配列は、type型(型型)になります。


連想配列を定義すると、あとで、

int const z = AssocArray::n + AssocArray::m;

とか書けます。(もはやどういう用語を使えばいいかわかんない。教えて!偉い人!)


尚、連想配列のメンバを定義するときは、'static'を忘れないようにしましょう。(何故ですか?知りません。)

struct AssocArray {
	int const n = 10;
	int const m = n+10;
	bool const b = true;
};

これはコンパイルエラーになります。


名前に束縛された連想配列の値をさらに別の名前に束縛するときは、typedefを使います。

// 'AssocArray'に束縛されてる値を'AssocArray2'という名前にも束縛する。
typedef AssocArray AssocArray2;

Haskellで書くと、

assocArray2 :: Type
assocArray2 = assocArray

こんな感じです。

関数

関数を書きましょう!関数の構文はどうかしてるので注意!

  • 何故かキーワード'template'を使います
  • 返せる型は、連想配列のみとなります
  • 大変読みにくいです!

さあ!心を落ち付かせて!何故こんなへんてこりんな仕様になってるのか!それは神のみぞ知る!


整数加算を行う関数は↓のようになります。

template 
<int n, int m>
struct 
add_int
{ static int const result = n+m; };

なお、返せる値が連想配列のみなので、どっかで加算した結果を拾ってくるプログラムが必要になります。

int const n = 10;
int const m = 20;
typedef result_aarray = add_int<n,m>; // add_int<n,m>の評価結果を'result_aarray'に束縛
int const o = result_aarray::result;  // result_aarrayのメンバresultが、加算した結果

さあ、よいですね!(よいわけがないが)関数の書きかたについて説明していきましょう。

template       // 関数であることを示すキーワード
<int n, int m> // 引数は n::int, m::int のふたつ
struct         // 戻り値が連想配列であることを示す(といっても、連想配列しか返せないけど…)
add_int        // 関数名
{ static int const result = n+m; } // この部分が、関数の戻り値
;

あと、パターンマッチが使えます。たとえば、上の、add_intは、

template <int n,int m> struct add_int { static int const result = n+m; };
template <> struct add_int <10,10> { static int const result = 40; };

こうやっておけば、引数が、(10,10)だった場合に、一般のadd_intとは違う値が返せます。

int const z = add_int<10,10>::result;
// z = 40

パターンマッチを使うときは、パターンの位置が変なところに来るのに注意。

template <10,10> struct add_int { static int const result = 40; };

ではないです!


さあ、よいですNE!(よいわけないが)さっそく、階乗ですよ。階乗は、

fact n = fact(n-1) * n
fact 1 = 1

こんな感じですね!C++で書くと、こうなります↓

template <int n> struct fact { static int const result = fact<n-1>::result * n; };
template <> struct fact<1> { static int const result = 1; };
int const z = fact<4>::result;

あとは、こういうのを組み立てていけば、なんかもう、なんでもできます。(いや、なんか、制限があるんだよな…確か)

duck typing

連想配列型(type型)の値は、全部同じ型になるので、duck typingみたいなアレです(←疲れてきた)。C++プログラミングは、Duck Typing大好きなるびーとかぱいそんとかの人にマジお勧め。

template <int n,int m> struct add_int { static int const result = n+m; };

struct duck_a { static int const hogehoge = 10; };
struct duck_b { static int const hogehoge = add_int<duck_a::hogehoge,4>::result; };

template 
<typename T>
struct 
get_hogehoge
{ static int const result = T::hogehoge; }; // 連想配列 T の中の名前'hogehoge'に束縛されてる値を拾ってくる

extern int a, b;

int a = get_hogehoge<duck_a>::result;	// a = 10;
int b = get_hogehoge<duck_b>::result;	// b = 14;

duck_aとduck_bに継承関係が無くても、名前'hogehoge'を持っているので、同じように扱えます。

Hello World

以上の知識の応用として、Hello Worldを書いてみましょう。

Hello Worldは次のように書けます↓

#include <stdio.h>
int main( ) { puts("Hello World."); }

わーい。やったね!


続く(わけが無い)

[] ギガウィング

こしたよ。41兆点であった。

残機0、ボム0でギリギリ。

次は何やるかなー。ギガウィング2とかだろうか。やっぱり。最近、クリアできそうなやつばっかりやってるような…

あー、上のちょっと間違ってた。

- typedef result_aarray = add_int<n,m>; // add_int<n,m>の評価結果を'result_aarray'に束縛
+ typedef add_int<n,m> result_aarray; // add_int<n,m>の評価結果を'result_aarray'に束縛

です。うーん。一応ひととおり動かしたと思ってたんだけど…


C++(というか、Boost)の素晴らしいところは、上で書いたようなネタをネタではなくて、実際にそれを有効活用してる点にあると思う。(関連するかも

僕は、まだそのレベルに達してないので、なんとも言えないけど…


とりあえず、C++プログラマが、JavaのGenericsを見たときに「何それ使えねー」という反応をする理由を理解して、そんで、「C++は時代遅れのオブジェクト指向言語」とかいう考えを修正していただければ、そのうちなんかの役に立つかもしれないし、役に立たないかもしれないです。

mrknmrkn 2006/10/09 03:25 かなり笑いました.すごく面白かったです.こういうの大好き!そして,最後の一文に共感します.ありがとうございます.

w_ow_o 2006/10/12 00:11 どうもありがとうございます。みなさん楽しんでいただけたようで、書いた本人としては嬉しいかぎりです。
いや、しかし、こういうのが受けがよろしいんだなーというか、僕の中では、まあ、中の上くらい(いや、もうちょい上か?)ぐらいだと思ってたので、なかなか、世の中難しいです。
あと、最後の一文に関しては、逆に、Java使いの視点から見た「C++プログラマはJavaを誤解している」的な話があれば面白いかもしれないです。

 |