数値演算の STLアルゴリズム | Programming Place Plus C++編【標準ライブラリ】 第24章

C++編【標準ライブラリ】 第24章 数値演算の STLアルゴリズム

先頭へ戻る

この章の概要

この章の概要です。


数値演算の STLアルゴリズム

この章では、STLアルゴリズムの中から、数値演算を行うものを取り上げます。

ただし、実のところ、数値演算だけに特化しているとは限りません。例えば、accumulate関数は +演算子を使うことによって合計を求めているので、+演算子によって連結を意味する std::string のような型を対象にすれば、数値演算以外の意味で使うことが可能です。

なお、数値演算の STLアルゴリズムは、<algorithm> ではなく、<numeric> という標準ヘッダに含まれています

accumulate

accumulate関数は、指定した範囲内の要素の合計を計算します。

namespace std {
    template <typename InputIterator, typename T>
    T accumulate(InputIterator first, InputIterator last, T init);

    template <typename InputIterator, typename T, typename BinaryOperation>
    T accumulate(InputIterator first, InputIterator last, T init, BinaryOperation binary_op);
}

第1、2引数で指定した範囲内の要素の合計を求めます。第3引数は初期値として機能します。戻り値は、結果(合計)を返します。

1つ目の形式では、合計を +演算子を使って計算します。具体的には、各要素(elem) について「init = init + elem」を行います。

#include <numeric>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v;
    v.push_back(0);
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    std::cout << std::accumulate(v.begin(), v.end(), 0) << std::endl;
}

実行結果

10

2つ目の形式では、第4引数に指定した関数や関数オブジェクトを使って計算します。

#include <numeric>
#include <iostream>
#include <vector>
#include <functional>

int main()
{
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);

    std::cout << std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>()) << std::endl;
}

実行結果

120

std::multiplies は、標準で用意されている関数オブジェクトです。std::multiplies が呼び出されると、2つの引数に *演算子を適用した結果を返します。

標準の関数オブジェクトについては、第26章で取り上げます。

inner_product

inner_product関数は、指定した2つの範囲内の要素の内積を計算します。

namespace std {
    template <typename InputIterator1, typename InputIterator2, typename T>
    T inner_product(InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, T init);

    template <typename InputIterator1, typename InputIterator2, typename T, typename BinaryOperation1, typename BinaryOperation2>
    T inner_product(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, T init, BinaryOperation1 binary_op1, BinaryOperation2 binary_op2);
}

第1、2引数で1つ目の範囲を指定し、第3引数でもう1つの範囲の先頭の位置を指定します。第4引数は初期値として機能します。戻り値は、結果(合計)を返します。

1つ目の形式では、*演算子を使って計算します。具体的には、1つ目の範囲の各要素(elem1) と、2つ目の範囲の各要素(elem2) について、「init = init + elem1 * elem2」を行います。つまり、2つの要素の積の合計ということになります。

#include <numeric>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v;
    v.push_back(0);
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    std::cout << std::inner_product(v.begin(), v.end(), v.begin(), 0) << std::endl;
}

実行結果

30

2つ目の形式では、第5引数(op1) と第6引数(op2) に指定した関数や関数オブジェクトを使って、「init = op1(init, op2(elem1, elem2))」という計算を行います。

#include <numeric>
#include <iostream>
#include <vector>
#include <functional>

int main()
{
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);

    std::cout << std::inner_product(v.begin(), v.end(), v.begin(), 1, std::multiplies<int>(), std::plus<int>()) << std::endl;
}

実行結果

3840

std::multiplies や std::plus は、標準で用意されている関数オブジェクトです。std::multiplies が呼び出されると、2つの引数に *演算子を適用した結果を返し、std::plus の方は、+演算子を適用した結果を返します。

標準の関数オブジェクトについては、第26章で取り上げます。

partial_sum

partial_sum関数は、指定した範囲内の要素の部分和を計算します。

namespace std {
    template <typename InputIterator, typename OutputIterator>
    OutputIterator partial_sum(InputIterator first, InputIterator last, OutputIterator result);

    template <typename InputIterator, typename OutputIterator, typename BinaryOperation>
    OutputIterator partial_sum(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation binary_op); 
}

第1、2引数で指定した範囲内の要素を先頭から順に調べ、そこまでの和を、第3引数の範囲へ出力します。つまり、1つ目の範囲の要素を elem0、elem1、elem2・・・とすると、2つ目の範囲には、次の値が出力されます。[elem0, elem0 + elem1, elem0 + elem1 + elem2, ・・・」

和の計算には、1つ目の形式では +演算子が使われ、2つ目の形式では第4引数の関数や関数オブジェクトが使われます。

戻り値は、最後に出力された要素を指すイテレータです。

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

namespace {
    void Println(int elem)
    {
        std::cout << elem << std::endl;
    }
}

int main()
{
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);

    std::vector<int> result(v.size());
    std::partial_sum(v.begin(), v.end(), result.begin());

    std::for_each(result.begin(), result.end(), Println);
}

実行結果

1
3
6
10
15

adjacent_difference

adjacent_difference関数は、指定した範囲内の要素の階差を計算します。

namespace std {
    template <typename InputIterator, typename OutputIterator>
    OutputIterator adjacent_difference(InputIterator first, InputIterator last, OutputIterator result);

    template <typename InputIterator, typename OutputIterator, typename BinaryOperation>
    OutputIterator adjacent_difference(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation binary_op); 
}

第1、2引数で指定した範囲内の要素を先頭から順に調べ、手前の要素との差を、第3引数の範囲へ出力します。つまり、1つ目の範囲の要素を elem0、elem1、elem2・・・とすると、2つ目の範囲には、次の値が出力されます。[elem0, elem1 - elem0, elem2 - elem1, ・・・」

差の計算には、1つ目の形式では -演算子が使われ、2つ目の形式では第4引数の関数や関数オブジェクトが使われます。

戻り値は、最後に出力された要素を指すイテレータです。

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

namespace {
    void Println(int elem)
    {
        std::cout << elem << std::endl;
    }
}

int main()
{
    std::vector<int> v;
    v.push_back(1);
    v.push_back(3);
    v.push_back(6);
    v.push_back(10);
    v.push_back(15);

    std::vector<int> result(v.size());
    std::adjacent_difference(v.begin(), v.end(), result.begin());

    std::for_each(result.begin(), result.end(), Println);
}

実行結果

1
2
3
4
5

C++11 (iota)

C++11

C++11 で追加された iota関数は、指定した値で始まる連続した値の整数列を生成します。

namespace std {
    template <typename ForwardIterator, typename T>
    void iota(ForwardIterator first, ForwardIterator last, T value);
}

第1、2引数に整数列を書き込む範囲を指定します。第3引数が初期値になります。

最初の要素には value が書き込まれ、以降は、前置インクリメント演算子を適用した結果が書き込まれていきます。

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

int main()
{
    std::vector<int> v(5);
    std::iota(std::begin(v), std::end(v), 0);

    std::for_each(std::begin(v), std::end(v), [](int elem){std::cout << elem << std::endl; });
}

実行結果

0
1
2
3
4


練習問題

問題① accumulate関数を使って、指定範囲内の std::string の連結を行えることを確認して下さい。

問題② accumulate関数を使って、指定範囲内の std::string の長さの合計を求める処理を作成して下さい。


解答ページはこちら

参考リンク



更新履歴

'2018/1/5 コンパイラの対応状況について、対応している場合は明記しない方針にした。

'2017/7/30 clang 3.7 (Xcode 7.3) を、Xcode 8.3.3 に置き換え。

'2017/3/25 VisualC++ 2017 に対応。

'2016/12/3 新規作成。



前の章へ(第23章 ソートされた範囲を扱う STLアルゴリズム)

次の章へ(第25章 STL の関数オブジェクト)

C++編のトップページへ

Programming Place Plus のトップページへ


はてなブックマーク Pocket に保存 Twitter でツイート Twitter をフォロー
Facebook でシェア Google+ で共有 LINE で送る rss1.0 取得ボタン RSS