Menu
Coddy logo textTech

関数オブジェクトとラムダ式

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 74/104。

多くのSTLアルゴリズムは、動作をカスタマイズする呼び出し可能オブジェクトを受け取ります。すでにstd::transformとともにラムダを使う方法を見てきました。ここでは、C++で呼び出し可能オブジェクトを作成する2つの方法、ファンクタラムダ式について見ていきましょう。

ファンクタ(関数オブジェクト)は、operator() をオーバーロードする class であり、インスタンスを関数のように呼び出せます。

#include <algorithm>
#include <vector>
#include <iostream>

struct MultiplyBy {
    int factor;
    MultiplyBy(int f) : factor(f) {}
    
    int operator()(int x) const {
        return x * factor;
    }
};

int main() {
    std::vector<int> nums = {1, 2, 3, 4};
    std::vector<int> result(nums.size());
    
    std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));
    // 結果: {3, 6, 9, 12}
}

各 functor インスタンスは、構築時に設定されたメンバー変数を通じて、それぞれ独立した状態を保持できます。これは通常の function 呼び出しではできません。しかし、単純な操作のために class を定義するのは冗長です。ラムダ式は、簡潔な代替手段を提供します。

int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
               [factor](int x) { return x * factor; });

lambda構文は[capture](parameters) { body }です。キャプチャ句は、lambdaがアクセスできる外部変数を指定します。すべてを値でキャプチャするには[=]を、すべてを参照でキャプチャするには[&]を使用するか、[factor][&factor]のように特定の変数を列挙します。

ラムダ式は、カスタム条件によるソートなどのアルゴリズムで、一度限りの操作を行う場合に特に便利です。

std::vector<int> nums = {5, -2, 8, -1};
std::sort(nums.begin(), nums.end(), 
          [](int a, int b) { return std::abs(a) < std::abs(b); });
// 絶対値でソート済み: {-1, -2, 5, 8}
challenge icon

チャレンジ

簡単

製品価格にさまざまなdiscount戦略を適用するためのfunctorとlambda式の両方を示す、価格計算機を作成しましょう。

コードを2つのファイルに分けて整理します。

  • Discounts.h:ここでdiscount functorとユーティリティ関数をdefineします。

    PercentageDiscountというfunctorをCreateし、discountの割合を(integerとして)保持させます。そのoperator()double型の価格を受け取り、discount後の価格を返すものとします。たとえば、$100に20%のdiscountを適用すると$80を返します。

    FixedDiscountという別のfunctorをCreateし、差し引く固定額を(doubleとして)保持させます。そのoperator()は価格を受け取り、固定額を差し引いた価格を返すものとします(ただし、0をnever下回らないものとします)。

    printPricesというfunctionをCreateし、const std::vector<double>&を受け取って、すべての価格をスペースで区切って出力し、その後にnewlineを出力するようにします。各価格は小数点以下2桁のFormatにします。

  • main.cpp:5つのinputsを読み取ります(それぞれ別の行に入力します)。
    1. 1つ目の製品価格(double)
    2. 2つ目の製品価格(double)
    3. 3つ目の製品価格(double)
    4. 適用するPercentage discount(integer、例:20%の場合は20)
    5. Fixed discount amount(double)

    3つの価格を含むvectorをCreateし、両方のアプローチを示します。

    1. Original prices:を出力し、その後に価格を出力する
    2. PercentageDiscount functorを使ってstd::transformを実行し、discount済み価格の新しいvectorをCreateする。After percentage discount:を出力し、その後に結果を出力する
    3. 元の価格に対してFixedDiscount functorとstd::transformを使い、別のvectorをCreateする。After fixed discount:を出力し、その後に結果を出力する
    4. 各Original価格をdoubledするlambda式とstd::transformを使う。Premium prices (doubled):を出力し、その後に結果を出力する
    5. lambdaとstd::sortを使い、Original価格をdescending orderに並べ替える。Sorted (high to low):を出力し、その後に並べ替えた価格を出力する

たとえば、inputsが100.0050.0075.002015.00の場合:

Original prices: 100.00 50.00 75.00 
After percentage discount: 80.00 40.00 60.00 
After fixed discount: 85.00 35.00 60.00 
Premium prices (doubled): 200.00 100.00 150.00 
Sorted (high to low): 100.00 75.00 50.00 

このchallengeでは、discount amountのようにstateを維持するfunctorと、素早いinline操作のために変数をcaptureするlambdaを比較できます。どちらのアプローチも、std::transformstd::sortのようなSTL algorithmとシームレスに連携します。

自分で試してみよう

#include <iostream>
#include <vector>
#include <algorithm>
#include "Discounts.h"

int main() {
    // 入力を読み取る
    double price1, price2, price3;
    int percentageDiscount;
    double fixedDiscount;
    
    std::cin >> price1;
    std::cin >> price2;
    std::cin >> price3;
    std::cin >> percentageDiscount;
    std::cin >> fixedDiscount;
    
    // 3つの価格を持つベクターを作成する
    std::vector<double> prices = {price1, price2, price3};
    
    // TODO: Print "Original prices:" followed by the prices using printPrices
    
    // TODO: Use std::transform with PercentageDiscount functor
    // 結果用の新しいベクターを作成する
    // Print "After percentage discount:" followed by the results
    
    // TODO: Use std::transform with FixedDiscount functor on original prices
    // 結果用の新しいベクターを作成する
    // Print "After fixed discount:" followed by the results
    
    // TODO: Use std::transform with a lambda that doubles each original price
    // 結果用の新しいベクターを作成する
    // Print "Premium prices (doubled):" followed by the results
    
    // TODO: Use std::sort with a lambda to sort original prices in descending order
    // Print "Sorted (high to low):" followed by the sorted prices
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: C++オンラインコンパイラ