Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ lambda function - how to return closest vector element compared to target

I have a already-sorted vector called vec, and a target variable. The goal is to return the closest-to-target vector element.

I tried to use C++11 lambda function with [=] to capture outside variable

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> vec{1,2,3,4,5};
    double target = 3.14159;

    int res = min(vec.begin(), vec.end(), [=](int v1, int v2) -> bool {
            return abs(v1-target) < abs(v2-target);
        });

    cout << res << endl;

    return 0;
}

I expect to get res=3, but it returns an error:

error: cannot convert 'const __gnu_cxx::__normal_iterator<int*, std::vector<int> >' to 'int' in initialization
like image 535
Heifetz_Fan Avatar asked Jul 22 '19 10:07

Heifetz_Fan


2 Answers

You are using the wrong algorithm. std::min operates on two specific object passed as is, or on a std::initializer_list. For containers, use std::min_element instead. It returns an iterator, which needs to be dereferenced.

auto res = std::min_element(vec.cbegin(), vec.cend(), [=](int v1, int v2) -> bool {
        return abs(v1-target) < abs(v2-target);
    });

// Make sure that the range wasn't empty, i.e. res != vec.cend()...

const int min = *res;

Note that you might want to consider narrowing down the scope of your lambda closure by explicitly capturing the object that you need (in your case only one after all). And also consider passing .cbegin() and .cend() when you don't modify the container in question.

like image 101
lubgr Avatar answered Nov 15 '22 05:11

lubgr


A solution that might potentially be more efficient and readable could use lower_bound to find the greater of two elements that could potentially be returned.

int custom_bound(std::vector<int> const& vec, double target) {
    if (vec.empty()) {
        // throw or make the function return `optional`, choose to taste
    }

    auto lb = std::lower_bound(vec.begin(), vec.end(), target);

    if (lb == vec.end()) {
        return *(lb - 1);
    } else if (lb == vec.begin()) {
        return *lb;
    } else {
        const int up = *lb;
        const int down = *(lb - 1);
        return std::abs(up-target) < std::abs(down-target) ? up : down;
    }
}

Unfortunately this can't be easily done as a one-liner. It doesn't require any custom functors, though; it merely exploits the fact that the number must lie in between (inclusive) lower_bound and the iterator one before the LB.

We can make another observation that up will be equal or greater to target, while down can only be smaller. Hence the condition could be replaced with

up-target < target-down

which removes the need for std::abs.

like image 36
Bartek Banachewicz Avatar answered Nov 15 '22 05:11

Bartek Banachewicz