ネタ
自称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