【问题标题】:Find vector elements with even values查找具有偶数值的向量元素
【发布时间】:2014-04-18 13:28:21
【问题描述】:

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

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

【问题讨论】:

  • 现在发布一个完整的代码示例怎么样。
  • 无论如何,你应该简单地不要使用你不懂的代码。如果对您来说很难,那么对维护者来说也很困难。所以,用你理解的方式重新表达效果。
  • 你可以用[](int x){return x % 2 != 0;}替换那个烂摊子

标签: c++ algorithm stl bind


【解决方案1】:

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

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

count_if 是一种算法,它返回指定范围内满足特定条件的元素数量:

count_if(first, last, criteria)

在您的情况下,firstvec.begin()lastvec.end():所以整个向量都被考虑用于计数。

现在让我们将注意力集中在条件部分。

由内而外:

modulus&lt;int&gt; 是一个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&lt;bool&gt;。这只是否定输入,例如如果输入为false (0),则logical_not&lt;bool&gt; 返回true,反之亦然。

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

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

所以,如果一个数字是偶数:

  1. even number % 2 == 0
  2. 否定 0 你得到true

相反,如果一个数字是奇数:

  1. odd number % 2 == 1
  2. 否定 1 你得到false

由于count_if 计算条件为true 的元素的数量,因此您正在计算向量中的偶数 个数字。

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

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

请注意,在这种情况下,使用 moduluslogical_not 的函数式方法似乎过于复杂:使用 lambda(甚至是 ad hoc IsEven() simple function)会更清晰。
考虑以下代码 (live here on 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;
}

【讨论】:

  • 很好的答案!非常感谢!
【解决方案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 )

计算迭代器 from/to 定义的所有元素的 cond(倒模)。

placeholders::_1 是将在由count_if 运行的迭代器驱动循环中确定的内容(即当前元素)插入到嵌套在下面的函数中的硬连线方式。

【讨论】:

  • "返回除以 2 后的余数:0 或 1。" 注意:int
  • @dyp 感谢您提出改进建议 -- 准确总是最好的 ;-)
  • @dyp 余数不能为负,始终为正,x%2 返回 0 或 1
  • @privatedatapublicchannel2 不在 C++11 中,而在 C++03 中,符号是实现定义的。见stackoverflow.com/a/13100805/420683
【解决方案3】:

这不计算奇数元素,而是偶数元素。

要计算奇数值的向量元素,我们必须检查每个元素是否除以 2,如果结果为 1,则返回 true。

所以我们将使用模数(),它是一个实现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&lt;int&gt;(), placeholders::_1, 2)) 返回 true (1) 如果参数是 odd 和 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

【讨论】:

    【解决方案4】:

    这是一个双重绑定,第一个通过2的参数计算modulus,这似乎相当于

    y = x % 2
    

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

    【讨论】:

    • 好吧,这听起来很合理。但是为什么第二个绑定需要使用 placeholder::_1 。为什么代码不能与 placeholder::_2 一起使用?
    • @user3548972:因为你想要var % 2而不是2 % var
    猜你喜欢
    • 1970-01-01
    • 2010-12-27
    • 2019-12-06
    • 1970-01-01
    • 2018-04-05
    • 1970-01-01
    • 1970-01-01
    • 2017-01-17
    相关资源
    最近更新 更多