どうも、uncleです
セミナーお疲れ様でした!
写真を撮り忘れると言う失態を犯してしまいましたが
当日はかなりの人数が参加して下さりました
勝手がわからず多々まごついてしまった部分もあったとは思うのですが
セミナー後質問に来てくれたりととても活気があり、こちらも励みになりました
さて、今回はC++11で新しく追加されたLambda式を紹介します
中々2年生に向けてのセミナーが行えないので
このGESブログの方で勉強してもらえると幸いです。
今回はかなり長いので気合を入れてください
それでは行きましょう
[1回]
今回紹介するこのLambda式、聞いたことがない、全く知らないと言う人も多いのでは無いかと思います
まず読みはラムダと読みます
元々はJavaやPythonで使われていたものの輸入品で、Lambda関数や無名関数と呼ばれ、関数オブジェクトでもあります
関数といえば私達は普段から使用していますよね
その無名版です。
名前がないのであればどうやって呼び出すんだと思うかもしれません
確かにそこが多少ネックであり、メリットでもあります。
まずはその使い方から見ていきます
最短コードはこのようになります
構文については後述しますので我慢して下さい
初めて見ると何がなんだかと言った感じですね
しかしただの関数です、安心してください
通常の関数で記述するとこうなります
はい、つまり作りはしたものの何もしていない状態ということですね
ん?普通の関数で代用出来るの?と思われた方は鋭い
Lambda式でないと出来ないことはそうそうありません(無いよね??)
このLambda式は普通の関数でも問題ないけどこう記述したほうがスマートになるよね?
と言った場合に使用します
ではこんな関数をLambda式にしてみたいと思います
これがLambdaを使用するとこうなります。
記述はすっきりとしましたがこれだとLambdaは使用せず普通に
std::cout << "Hello Lambda" << std::endl;
と記述するべきですね(^_^;)
ではここで一度構文を勉強しましょう。
先頭の [] ここは
ラムダ導入子と言います。
次に () ここは仮引数になります
通常の関数同様ですね
ちなみにここは省略することも可能です
次に {} ここは上記のサンプルコード同様に関数の本体を記述します
こちらも通常の関数と同じですね
最後に () ここは関数呼び出しを行っている部分です
Func();の()の部分です、引数がある場合にはここに実引数を記述します
要するに「ラムダ導入子」があり、「関数名」が無い
この点を除けば通常の関数と同じであることが分かるかと思います
ですので引数を持たせるためには
こうすることでLambda式に引数を持たせることが出来ます
関数らしくなってきましたね
そうなると欲しくなるのが戻り値ですね
こちらは記述が少し特殊で初め省略していたのでここで紹介します
引数に10をリテラルで渡し、10を掛けて値を返しています
と言うことでこの様に「->型名」を
仮引数の後ろに記述します(今まで紹介したとおり省略が可能です)
明示的に記述したい場合には「->void」と記述するといいと思います
と言うことなんですがこの戻り値の型は省略が可能です。
どういう事かというと
この様に戻り値の型を明示しなくても型を勝手に推測してくれるんです
ただしこれは関数本体にreturnがある場合に限ります
しかし
こちらで紹介したとおり関数の戻り値の型推測にautoは使用できません
これはdecltypeと言うものを使用して実現しています
長くなってしまうので詳細についてはまた別の機会に紹介させていただきます
それではそろそろ
ラムダ導入子について説明をしていきたいと思います
Lambdaの先頭に記述している [] の部分には2種類のキャプチャと呼ばれるものを指定できます
コピーによるキャプチャ、参照によるキャプチャ
この2つです
まず初めにコピーと参照の違いについて少しおさらいします
コピーの場合には関数内で値を書き換えても実引数に影響はなく、
参照の場合にはポインタの様に値を書き換える事ができると言うものでしたね
(※2年生はまだ参照を勉強してないと思います)
ではキャプチャに話を戻したいと思います
まずはコピーキャプチャから見て行きましょう
ラムダ導入子に「 = 」を使用しました。(引数、戻り値の型は省略しました)
これでコピーキャプチャとして変数をキャプチャすることが出来ます
こうすることでこのLambda式が定義されている関数のスコープ内の変数、
つまりTestやTest2の値をコピーしLambda式内部で使用できるように出来るという物です
引数として渡した場合と何が違うんだ?と思うかもしれませんが
コピーキャプチャの場合には値を代入することが出来ないと言う点で異なります。
そして参照によるキャプチャには「 & 」を使用します。
こちらはコピーとは違い、値を書き換えが可能で、Lambda関数の実行後でも影響があります
すると参照によるキャプチャを行う場合にコピーで渡したいものもある、と言う場合も出てくると思います。
そういう場合にはこう記述します
Testは参照キャプチャで渡されているので値が書き換えられます。
そしてTest2はコピーで渡してくれ、と明示的に記述することで問題は解決します
結果は
Testの値は20
Test2の値は20のまま
Test3の値は400となります
これで一通りの使い方は理解できたかと思います。
でもキャプチャだけではメリットは小さいですよね
そこでここからがLambdaの真髄となります。
この様に関数の引数に直接記述が可能です。
処理としてはまずランダムな値を代入し、std::sortでソート、結果を表示と言う流れです。
結果は昇順にソートされたランダムな値が画面に表示されます。
std::sortの引数にLambdaを使用してみました、
何をしているかというと引数を比較し_RightValの値が大きければtrueを、
そうで無いのであればfalseを返しています
std::sort内部ではこのLambdaを呼び出し、その結果を元にソートしています
ですのでLambda関数内部の比較演算子の「 < 」を「 > 」に変えると降順ソートとなります
メリットとしては通常の関数であれば関数宣言の後、
関数定義をしてそこから引数として渡します。
ですがそうするとどういう関数なのか確認に行く手間が発生します
ですのでこの様に直接記述ができ、ソースコードの可読性が向上する。
というのがこのLambdaの利点となります。
勿論自作した関数の引数として受け取ることも可能でこの様に記述します
この様に処理自体を関数オブジェクト側に任せてしまうと言うのも面白いですよね
boost::formatはprintfの様にフォーマットを指定できるという物です
標準C++ライブラリ内にはラムダ式が活躍する関数がいくつかあります
例えばこのstd::all_ofです
これは全てtrueの場合にtrueを返すというもので、
この仲間にstd::none_of(全てfalseの場合にtrueを返す)、std::any_of(1つ以上trueの場合にtrueを返す)などがあります
他にもstd::for_eachなども関数オブジェクトを引数に持っています
1~10の値を二乗し画面に表示しています。
私が知らないだけでもっと便利な関数オブジェクトを引数に持つ関数があると思いますので探してみてください。
長々となりましたが今回は以上です。
お疲れ様でした
PR
COMMENT