应用函数后查找重复值的函数

Function to find duplicate values after applying a function

本文关键字:函数 查找 应用      更新时间:2023-10-16

我想要一个C++函数,该函数在集合中查找与集合中另一个项目具有相同值function(key)的项目。

例如

std::set<int> ints = {1, -1, 3};
// Finds an item in the set with the same absolute value as
// another item.
int* dupe = find_duplicates(ints, [](int x) { return std::abs(x); });
// Should print -1 or 1
if (dupe != 0) std::cout << "Found dupe: " << *dupe << std::endl;

但是,我什至无法为此方法编写函数签名。

在Java中,它会像static Integer findDuplicates<T, U>(Iterable<T>, Function<T, U> func)

。在C++中,我已经了解了以下内容,但它无法编译:

template<template <typename T> Collection, typename U>
T* find_duplicates(
const Collection<T>& collection,
const std::function<U(T)>& func) { ... }

我得到的错误是error: 'T' does not name a type.

有什么指示吗?(我也对一种绕过使用指向T*的"原始"指针的方法感兴趣,但对于单独的问题来说,这可能更好(

在您的示例中,T无法识别且几乎无用。您应该更早地介绍类型T然后使用它来指定Collection,如下所示:

template<typename T, template <typename> typename Collection, typename U>
T* find_duplicates(const Collection<T>& collection,
const std::function<U(T)>& func)
{
// some logic here
}

另外:请注意,正如我之前在评论中指出的那样,您在Collection之前错过了一次typename。上面的示例针对该建议进行了调整。

对于您的特定示例,这样的事情应该有效,

#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
int main() {
std::set<int> ints = {1, -1, 3};
auto dupe = std::find_if(ints.begin(),ints.end(),[&](const int& first){
return std::find_if(ints.begin(),ints.end(),[&](const int& second) {
// so a the same value isn't checked against itself..
if (&first == &second) return false;
return std::abs(first) == std::abs(second);
}) != ints.end();
} );
if (dupe != ints.end()) std::cout << "Found dupe: " << *dupe << std::endl;
}

演示

不要对函数参数类型太挑剔。 你真的不需要collection成为类模板专用化,只需要你可以迭代的东西(甚至可以是 C 风格的数组(。func只需要是可调用的,并且强制它成为std::function对象可能比直接使用函数指针或 lambda 效率低,因为std::function添加然后解析的类型擦除。

因此,工作声明可能是

template <typename Collection, typename Func>
auto find_duplicates(Collection& collection, const Func& func)
-> typename std::iterator_traits<decltype(std::begin(collection))>::value_type*;

我会做的:

#include <utility>
#include <iterator>
#include <set>
// Calling with an rvalue collection would be bad news.
template <typename Collection, typename Func>
void find_duplicates(Collection&&, const Func&) = delete;
template <typename Collection, typename Func>
auto find_duplicates(Collection& collection, const Func& func)
-> typename std::iterator_traits<decltype(std::begin(collection))>::value_type*
{
auto iter = std::begin(collection);
using value_type = typename std::iterator_traits<decltype(iter)>::value_type;
auto ptr_compare = [&func](value_type* p, value_type* q)
{ return func(*p) < func(*q); };
std::set<value_type*, decltype(ptr_compare)> iter_set{ptr_compare};
for (; iter != std::end(collection); ++iter) {
auto insert_result = iter_set.insert(std::addressof(*iter));
if (!insert_result.second)
return *insert_result.first;
}
return nullptr;
}

有时,原始指针确实是答案。这意味着该值可能为 null,并且指针与对象没有所有权关系。在这种情况下,在指针通过修改或销毁容器使其失效后不使用指针取决于调用方,但它具有通常的指向容器元素失效语义,具体取决于容器详细信息。

这不适用于std::vector<bool>*collection.begin()具有"代理"类型的任何其他伪容器。 如果要支持这些并且可以使用 C++17std::optionalboost::optional,则可以返回optional<value_type>而不是原始指针,尽管这将复制返回的重复值。 如果你不能使用这些或副本是不需要的,也许可以编写一个模板类iter_or_null<InputIter>包含一个迭代器,具有只调用迭代器的运算符的operator*operator->,并满足NullablePointer概念。

你实际上对你的模板过于严格。您希望模板采用定义了 std::begin 和 std::end 的任何容器,以及可以接受容器包含的任何内容的实例的任何函数对象。在模板世界中,你只接受任何类型,按照你想要的方式使用它,如果调用方没有提供兼容类型的对象,他们将得到编译错误。

此外,没有必要使用 std::function - 当你只接受函数对象作为具有推导类型的参数时,它最终可能会分配内存。

使用 C++14,自动推导的返回类型和 lambda 的自动参数允许您编写非常通用的代码。这是我的解决方案。它将迭代器返回到容器中,指向找到的第一个重复项,如果未找到重复项,则返回 std::end(c(。

#include <algorithm>
#include <cmath>
#include <iterator>
#include <iostream>
#include <set>
template<typename C, typename F>
auto find_duplicates(const C & c, const F & func)
{
return std::find_if(std::begin(c), std::end(c), [&](const auto & first) {
return std::any_of(std::begin(c), std::end(c), [&](const auto & second) {
return &first != &second && func(first) == func(second);
});
});
}
int main()
{
std::set<int> s{1, 3, -1};
auto const iter = find_duplicates(s, [](int x) { return std::abs(x); });
if (iter != std::end(s))
{
std::cout << "Duplicate found: " << *iter << 'n';
}
}

编辑:我刚刚注意到您指定了C++11标签。在 C++11 中,它只是稍微复杂一点:

template<typename C, typename F>
auto find_duplicates(const C & c, const F & func) -> decltype(std::begin(c))
{
using ContainedType = decltype(*std::begin(c));
return std::find_if(std::begin(c), std::end(c), [&](const ContainedType & first) {
return std::any_of(std::begin(c), std::end(c), [&](const ContainedType & second) {
return &first != &second && func(first) == func(second);
});
});
}