きままにブログ

プログラミングを主とした私のメモ帳です。寂しいのでコメントください笑

ネタ

自称C++使いですが、

の要望に答えましょう。

〔以下プログラム部分以外は殆ど引用です〕

C++使いの人に、

関数型言語って何がすごいんですか

と聞かれて、

じゃあC++で accumulator すなわち、数nをとり、「数iを取ってnをiだけ増加させ、その増加した値を返す関数」を返すような関数を書いてみろよ

って言ったら、

値を返す関数を返すような関数・・・? C++11までだったら、

auto f(int n) -> function<int(int)> {
	return [n](int i) { return n + i; };
}

C++14だったら

auto f(int n) {
	return [n](int i) { return n + i; };
}

かな!

で、会話が終わってしまった。

さらに、C++使いのターン。

関数に関数を渡せる? C++だって関数ポインタ渡せますよ。
チューリング完全なんだから、どんなプログラムだって書けますよ。

と仕掛けてきた。そこで、

じゃあ、3回呼ぶと動作が変わる関数を書いてみて。

f();
f();
f();
f();

と呼ぶと、
3
2
1
liftoff
って出力されるやつ。ロケットみたいな。

できました。

auto f = []() {
	static int n = 3;
	if(n > 0) {
		printf("%d\n", n);
	}
	else {
		printf("liftoff\n");
	}
	--n;
};

f();
f();
f();
f();

OK. じゃあさ・・・。実はロケットは2機あったんだよ。ロケットg。カウントダウン別々で。

f();
f();
f();
f();

g();
g();

と呼ぶと、
3
2
1
liftoff
3
2
ってなるようにしてくれ。

と問題をだしたら、

状態が変化するクラス……ってことにすればいいかな? できました。

class F {
public:
	F() : n(3) { };
	void operator()() {
		if(n > 0) {
			printf("%d", n);
		}
		else {
			printf("liftoff");
		}
		--n;
	}
private:
	int n;
};

int main() {
	auto f = F();
	auto g = F();
	
	f(); // 3
	f(); // 2
	f(); // 1
	f(); // liftoff
	g(); // 3
	g(); // 2
	
	return 0;
}

となった。続けてこう言い放った。

関数を実行したら、その関数の状態が変化するのは気持ちが悪いので、変化すべきデータを渡して変化させてもらうのが普通だけどね。

auto f = [](int& n) {
	if(n > 0) {
		printf("%d\n", n);
	}
	else {
		printf("liftoff\n");
	}
	--n;
};

int fn = 3, gn = 3;
f(fn);
f(fn);
f(fn);
f(fn);

f(gn);
f(gn);

〔引用ここまで〕

追記

今まで知らなかったが、mutableなラムダ式も生成できて、この際値をコピーキャプチャしたものも変更できるみたい。

int n = 3;

auto f = [n]() mutable {
	printf("%d\n", n);
	--n;
};
auto g = f;

f(); // 3
f(); // 2
f(); // 1
g(); // 3
g(); // 2
f(); // 0

追記2

methodがmutableでもconstでも、値コピーキャプチャはラムダ式を生成した段階でコピーされるようです。

struct C {
	C() { printf("construct\n"); }
	C(const C&) { printf("copy\n"); }
	C(C&&) { printf("move\n"); }
	C& operator=(const C&) { printf("substitute\n"); }
	~C() { printf("destruct\n"); }

	void method() { printf("call\n"); }
};

int main() {
	C c;

	printf("# assign f\n");
	auto f = [c]() mutable {
		c.method();
	};
	printf("# assign g\n");
	auto g = f;

	printf("# execute\n");
	f();
	f();
	g();
	g();
	f();

	getchar();
	return 0;
}
<<

実行結果は、

>>
construct
# assign f
copy
# assign g
copy
# execute
call
call
call
call
call