Tosainu Lab

関数の戻り値が最も小さくなる配列の要素は何番目か

関数foo()と何らかの値の入った配列nyanがあって,

double foo(double arg) {
  return std::acos(arg);
}

double nyan[] = {.0, -.1, .3, -.5, .7};

foo(nyan[n])が最小になるn取得するって感じのコードを某所で見つけた.
こんな感じ.

int result;
double prev = INFINITY;

for (int i = 0; i < 5; i++) {
  if (foo(nyan[i]) < prev) {
    prev = foo(nyan[i]);
    result = i;
  }
}

std::cout << result << std::endl; // = 4

別に問題ないんだけれども, とにかくCoolじゃなくて個人的にもにょる…

ってことでこんな感じなのを思いついた. もっとよさ気な書き方があったら教えてくださいー.
動作確認はclang++ -Wall -Wextra -std=c++14 prog.ccでしました.

std::min_element

これくらい標準ライブラリの何かでパパッとやって終わりそうだなとと思って<algorithm>ヘッダで定義されてる関数一覧を眺めていたら, 面白そうなのを見つけた.

std::min_element

template< class ForwardIt, class Compare >
ForwardIt min_element( ForwardIt first, ForwardIt last, Compare comp );
Parameters

first, last - forward iterators defining the range to examine
cmp - comparison function object (i.e. an object that satisfies the requirements of Compare) which returns true if a is less than b.

これを使ってみることにした.

const auto result_itr = std::min_element(std::cbegin(nyan), std::cend(nyan),
  [](const auto& a, const auto& b) {
    return foo(a) < foo(b);
  });
int result = result_itr - std::cbegin(nyan);

std::cout << result << std::endl;

constとかcbegin(), cend()を使わなければもう少し短くなるけれど, 気分的に.

正直Coolになったかは微妙なんだけど, std::min_elementに関しては複雑な比較もできそうで面白いなと思った.

番外編

minがあれば当然maxもある.
std::minmax_element - cppreference.com

そして, 同じく3番目の引数に関数オブジェクトを渡して動作をカスタムできる.

あれ…? もしかして…

#include <algorithm>
#include <array>
#include <iostream>

auto main() -> int {
  std::array<int, 5> a{{2, 6, 1, 9, 4}};

  auto min = std::min_element(a.cbegin(), a.cend(), [](const auto& a, const auto& b) {
    return a > b;
  });

  auto max = std::max_element(a.cbegin(), a.cend(), [](const auto& a, const auto& b) {
    return a > b;
  });

  std::cout << "std::min_element() : " << *min << std::endl;
  std::cout << "std::max_element() : " << *max << std::endl;
}

実行結果

逆転できちゃったよ()