LoginSignup
285
246

More than 5 years have passed since last update.

忘れがちな C++ の標準ライブラリの使い方

Last updated at Posted at 2015-09-02

実は,C++ 標準ライブラリで基本的なアルゴリズムは用意されている.案外これが知られてなかったり忘れられてたりするのでメモしておく.

std::max_element, std::min_element

min_element, max_element は,最小値,最大値のイテレータを返してくれる関数.

std::vector<int> vec(100);
for (size_t i = 0; i < vec.size(); ++i) {
    vec[i] = i;
}

std::random_shuffle (vec.begin(), vec.end());

// std::*_element は,イテレーターを返すので '*' で値を取得する
int min = *std::min_element(vec.begin(), vec.end());
int max = *std::max_element(vec.begin(), vec.end());

とこのようにイテレーターを取得するすることができる.ここで,最大値と最小値の添え字を取得したい場合も多いその場合は,std::distance と組み合わせればうまいこと取得できる.

std::vector<int>::iterator minIt = *std::min_element(vec.begin(), vec.end());
std::vector<int>::iterator maxIt = *std::max_element(vec.begin(), vec.end());

// distance で vec の先頭イテレーターと minIt, maxIt との距離を取得する.
// インデックスを取得したいときは,vec の先頭イテレーターを指定する必要がある.
// 例えば,vec.begin() + 1 とか指定すると答えは変わる.
size_t minIndex = std::distance(vec.begin(), minIt);
size_t maxIndex = std::distance(vec.begin(), maxIt);

std::accumulate

総和を自分で実装している人は多いのでは,accumulate を使えば一発でできる.

#include <numeric>

int main() 
{
    int list[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    const size_t size = sizeof(list) / sizeof(list[0]);

    int sum = std::accumulate(list, list + size, 0);    // ans = 55
    return 0;
}

std::transform

A から B へ変換するといったときに使う.よく出る事例は,全て小文字にする tolower と全てを大文字にする toupper

#include <algorithm>

std::string src ="abc";
std::string dst = src;

// src と dst のサイズが同じでなければダメ.
std::transform(src.begin(), src.end(), dst.begin(), toupper);

何をしているのか.単に,src と dst に関してループを回して toupper を適応しているだけ.ちょっとわかりやすく書くと? 以下の様になる.

std::string::const_iterator it = src.begin();    // 入力用のイテレーター
std::string::iterator result = dst.begin();      // 結果用のイテレーター
while (it != src.end()) {
    *result = toupper(*it);    // 一文字ずつ toupper を適応して result に入れる
    ++it;        // 入力用イテレーターを進める
    ++result;    // 出力用のイテレーターを進める
}

ここで,toupper に値する関数が好きに指定できることが特徴.

だから,例えば,コンテナの要素が cv::Mat のような浅いコピー系であるとき,全要素に関して深いコピーにしたいとき,次の様にできる,意味的に少し合わないので使わない方が無難か?

cv::Mat toCopyDeep(const cv::Mat &src)
{
    return src.clone();
}

std::vector<cv::Mat> src;
std::vector<cv::Mat> dst(src.size());
std::transform(src.begin(), src.end(), dst.begin(), toCopyDeep);

std::copy

std::iostream と組み合わせて相当変態なコードができる.

//http://www.cplusplus.com/reference/iterator/ostream_iterator/
#include <iostream>     // std::cout
#include <iterator>     // std::ostream_iterator
#include <vector>       // std::vector
#include <algorithm>    // std::copy

int main () {
    // vector の初期化 10-90 の代入
    std::vector<int> myvector;
    for (int i=1; i<10; ++i) {
        myvector.push_back(i*10);
    }

    std::ostream_iterator<int> out_it (std::cout,", ");
    std::copy(myvector.begin(), myvector.end(), out_it );    // ? 一瞬理解できない ?
    return 0;
}

結果

10, 20, 30, 40, 50, 60, 70, 80, 90, 

これを使えばあっさりと以下の様なvectorの中身を表示する関数を作れる.vector に限らずランダムイテレーターであれば何でもいける.

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

template <typename T>
std::string printVector(const std::vector<T> &data)
{
    std::stringstream ss;
    std::ostream_iterator<T> out_it(ss, ", ");
    ss << "[";
    std::copy(data.begin(), data.end() - 1, out_it);
    ss << data.back() << "]";
    return ss.str();
}


int main() {
    // vector の初期化 10-90 の代入
    std::vector<int> myvector;
    for (int i = 1; i<10; ++i) {
        myvector.push_back(i * 10);
    }

    std::cout << "myVector = " << printVector(myvector) << std::endl;
    return 0;
}
myVector = [10, 20, 30, 40, 50, 60, 70, 80, 90]
285
246
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
285
246