6

私は甘やかされて育った Python プログラマーで、あるものに関してaのargmaxを計算することに慣れています。collectionfunction

max(collection, key=function)

例えば:

l = [1,43,10,17]
a = max(l, key=lambda x: -1 * abs(42 - x))

a次に、42 に最も近い数である 43 が含まれます。

上記のように、「反復可能」および任意の関数を取り、argmax を返す C++ 関数を作成することは可能ですか? autoこれには、テンプレート パラメーター、キーワード、および範囲ベースの反復が含まれると思いますが、それらをつなぎ合わせることができませんでした。

4

2 に答える 2

8

これは 2 段階のプロセスです。要素にマップさkeyれる関数を定義します。つまり、最大値を見つける操作の前に適用されます。最大値を見つけるための比較を定義するラムダ式で物事をまとめます。

auto key = [](int x){
    return -abs(42 - x);
};

std::max_element(l.begin(), l.end(), [key](int a, int b){
    return key(a) < key(b);
});

ここではkey、2 番目のラムダ関数の外で定義されたものをキャプチャする必要があります。(内部で定義することもできます)。これを 1 つのラムダ関数に入れることもできます。ラムダの外側から 42 をパラメータ化する必要がある場合は、これを変数としてキャプチャします。

int x = 42;
std::max_element(l.begin(), l.end(), [x](int a, int b){
    return -abs(x - a) < -abs(x - b);
});

std::max_elementイテレータを返すことに注意してください。値/それへの参照にアクセスするには、先頭に次を追加し*ます。

int x = 42;
auto nearest = std::min_element(l.begin(), l.end(), [x](int a, int b){
    return abs(x - a) < abs(x - b);
});
std::cout << "Nearest to " << x << ": " << *nearest << std::endl;

find_nearestこれを汎用関数でうまくラップできます。

template<typename Iter>
Iter find_nearest(Iter begin, Iter end,
                  const typename std::iterator_traits<Iter>::value_type & value)
{
    typedef typename std::iterator_traits<Iter>::value_type T;
    return std::min_element(begin, end, [&value](const T& a, const T& b){
        return abs(value - a) < abs(value - b);
    });
}

auto a = find_nearest(l.begin(), l.end(), 42);
std::cout << *a << std::endl;

ライブデモ find_nearest: http://ideone.com/g7dMYI


質問の関数に似た高階関数は次のようになりargmaxます。

template<typename Iter, typename Function>
Iter argmax(Iter begin, Iter end, Function f)
{
    typedef typename std::iterator_traits<Iter>::value_type T;
    return std::min_element(begin, end, [&f](const T& a, const T& b){
        return f(a) < f(b);
    });
}

質問のラムダ関数を正確に使用して、次のコードでこれを呼び出すことができます。

auto a = argmax(l.begin(), l.end(), [](int x) { return -1 * abs(42 - x); });
std::cout << *a << std::endl;

ライブデモ argmax: http://ideone.com/HxLMap


現在残っている唯一の違いは、このargmax関数が反復子ベースのインターフェイスを使用することです。これは、C++ 標準アルゴリズムの設計に対応しています ( <algorithm>)。独自のコーディング スタイルを、使用しているツールに適応させることは常に良い考えです。

値を直接返すコンテナーベースのインターフェースが必要な場合、Nawaz は、戻り値の型を正しく指定するために decltype-feature を必要とする優れたソリューションを提供しました。私は自分のバージョンをこのままにしておくことにしたので、人々は両方の代替インターフェースデザインを見ることができます.

于 2013-01-07T16:27:18.630 に答える
5

@leemes ソリューションが多すぎるためです。あなたの例でPythonバージョンを模倣しようとするものがないことを除いて、すべて正しいです。これを模倣しようとする私の試みは次のとおりです。

Python バージョンと同様の便利な汎用 argmax-function:

template<typename Container, typename Fn>
auto max(Container const & c, Fn && key) -> decltype(*std::begin(c))
{  
    if ( std::begin(c) == std::end(c) ) 
       throw std::invalid_argument("empty container is not allowed.");

    typedef decltype(*std::begin(c)) V;
    auto cmp = [&](V a, V b){ return key(a) < key(b); };
    return *std::max_element(std::begin(c), std::end(c), cmp);
}

そしてそれを次のように使用します:

std::vector<int> l = {1,43,10,17};
auto a = max(l, [](int x) { return -1 * std::abs(42-x); };

int l[] = {1,43,10,17}; //works with array also!
auto a = max(l, [](int x) { return -1 * std::abs(42-x); };

注:他のソリューションとは異なり、これmax()は要素への反復子ではなく、要素自体を返します!

また、このソリューションはユーザー定義のコンテナーでも機能することに注意してください。

namespace test
{
     template<size_t N>
     struct intcollection
     {
         int _data[N];
         int const * begin() const { return _data; }
         int const * end() const { return _data + N; }
     };
}

test::intcollection<4> c{{1,43,10,17}};
auto r = max(c, [](int x) { return -1 * std::abs(42-x); });

ライブデモをご覧ください

于 2013-01-07T16:48:27.817 に答える