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."); }

わーい。やったね!


続く(わけが無い)