查找向量中值为偶数的元素

5

您能解释一下这段代码是如何工作的吗?它成功地计算了向量元素中偶数值的数量,但我不清楚在这种特定情况下绑定是如何工作的。

count_if(vec.begin(), vec.end(),
         std::bind(logical_not<bool>(),
                   std::bind(modulus<int>(), placeholders::_1, 2)));

你能否贴出一个完整的代码示例呢? - Cheers and hth. - Alf
无论如何,你都不应该使用你不理解的代码。如果对你来说很困难,那么对维护者来说也是困难的。因此,以一种你能理解的方式重新表达其效果。 - Cheers and hth. - Alf
这是一个SSCCE:http://coliru.stacked-crooked.com/a/f6a841c14269bfe7 - dyp
1
你可以用[](int x){return x % 2 != 0;}替换那个混乱的代码。 - Mike Seymour
http://ideone.com/80VmsZ - 4pie0
4个回答

3
注意,您发布的代码计算向量中的偶数而不是奇数:

count_if(vec.begin(), vec.end(),
         bind(logical_not<bool>(),
              bind(modulus<int>(), placeholders::_1, 2)));
< p > count_if 是一个算法,它返回指定范围内满足特定条件的元素数量:

count_if(first, last, criteria)

在您的情况下,firstvec.begin()lastvec.end():因此整个向量都被考虑在内。
现在让我们把注意力集中在标准部分上。
从内向外开始: modulus<int>是一个function object,它返回整数除法的余数(就像%运算符)。它接受两个参数:第一个表示为placeholders::_1,这是源向量中的通用元素。将其视为扫描整个向量内容的变量x
第二个参数是数字2,因为要检查整数是偶数还是奇数,可以计算x % 2并将结果与0进行比较:
x % 2 == 0 --> even number
x % 2 == 1 --> odd number

bind 用于指定 modulus 函数对象的参数。

这个模数运算的结果作为输入传递给另一个 function object: logical_not<bool>,它只是对输入取反,例如如果输入是 false (0),logical_not<bool> 返回 true,反之亦然。

因此,“计数条件”由以下操作流程表示:

  1. 计算:placeholders::_1 % 2,即使用 modulus 计算 <<generic vector element>> % 2
  2. 如果上述操作的结果是 0(false),则使用 logical_not 返回 true(反之亦然)。

因此,如果一个数字是偶数:

  1. even number % 2 == 0
  2. 将 0 取反得到 true

如果一个数字是奇数:

  1. 奇数 % 2 == 1
  2. 取反1,你会得到false

由于count_if计算的是满足条件为true的元素数量,因此你正在计算向量中的偶数数量。

如果你真的想计算向量中的奇数数量,你可以摆脱逻辑反转(即logical_not):

auto odds = count_if(vec.begin(), vec.end(),
                     bind(modulus<int>(), placeholders::_1, 2));    

请注意,在这种情况下,使用模数和逻辑非的函数式方法似乎过于复杂:使用一个lambda表达式(甚至是一个临时的IsEven()简单函数)会更清晰。请参考以下代码(在Ideone上实时查看)以比较这三种方法:
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
using namespace std;

bool IsEven(int n) {
    return (n % 2) == 0;
}

int main() {
    // Test vector
    vector<int> vec{ 11, 22, 33, 44, 55 };

    // Using functional approach
    auto n = count_if(vec.begin(), vec.end(),
         bind(logical_not<bool>(),
             bind(modulus<int>(), placeholders::_1, 2)));    
    cout << n << endl;

    // Using lambdas
    n = count_if(vec.begin(), vec.end(), 
                 [](int n) { return (n % 2) == 0; });
    cout << n << endl;

    // Using boolean returning ad hoc function
    n = count_if(vec.begin(), vec.end(), IsEven);
    cout << n << endl;
}

太棒了!非常感谢! - Leopoldo

2

从内部调用解开到外部调用:

modulus<int>( a, 2 )

返回除以2的余数:==0或!=0。

logical_not<bool>( x )

对x进行逻辑反转(因此0 / false变为1 / true,而1 / true变为0 / false)

count_if(from, to, cond )

评估从迭代器的开始/结束位置定义的所有元素的条件(倒置模数)。
并且 placeholders::_1 是一种硬编码的方式,用于将迭代驱动循环运行中确定的某些内容(即当前元素)插入到下面嵌套的函数中。

1
“返回除以2后的余数:0或1。” 注意:对于“int”,可能的结果为-1、+1和0。 - dyp
@dyp 感谢您提出的改进建议 - 准确无误总是最好的;-) - laune
@dyp 余数不能为负数,它总是正数,x%2 返回0或1。 - 4pie0
@privatedatapublicchannel2 在C++11中不是这样的,在C++03中符号是实现定义的。请参见https://dev59.com/oWcs5IYBdhLWcg3wJQi8#13100805 - dyp

1

这不计算奇数元素,只计算偶数元素。

要计算具有奇数值的向量元素,我们必须检查每个元素是否可被2整除,并在结果为1时返回true。

因此,我们将使用modulus(),它是一个实现operator()的函数对象。

constexpr T operator()( const T& lhs, const T& rhs ) const;

并返回lhs除以rhs的余数。 我们必须使用std :: bind将一个且仅一个参数粘合到传递给的函数中。
count_if( InputIt first, InputIt last, UnaryPredicate p )

将我们的模数一元谓词 p 作为模数的第一个参数,常量 2 作为第二个参数。
std::bind(modulus<int>(), placeholders::_1, 2))

现在我们的函数 std::bind(modulus<int>(), placeholders::_1, 2)) 如果参数是奇数,则返回true(1),如果参数是偶数,则返回false(0)。如果我们想要计算偶数参数,我们必须忽略这个,因此我们的谓词必须返回相反的结果:
std::bind(logical_not<bool>(),
                           std::bind(modulus<int>(), placeholders::_1, 2))

http://ideone.com/80VmsZ

正如Mike Seymour建议的那样,更简单、更清晰的设计是用短lambda函数替换这些绑定。
[](int x){return x % 2 == 0;} // to count even elements
[](int x){return x % 2 != 0;} // to count odds

0

这是一个双重绑定,首先计算参数的2的模数,这似乎相当于

y = x % 2

然后将结果绑定到logical_not,以反转结果。


好的,这听起来很合理。但是为什么第二个绑定需要使用 placeholder::_1?为什么代码不能使用 placeholder::_2? - Leopoldo
因为你想要的是 var % 2 而不是 2 % var - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接