【问题标题】:std::map<T, bool>, count values that are truestd::map<T, bool>,计数为真的值
【发布时间】:2015-04-15 10:14:06
【问题描述】:

我有一张地图:

std::map<std::string, bool> all_triggers_didfire;

我填写它,最后想获得真实值的数量。以下代码有效:

int count_did_fire = std::count_if(
  all_triggers_didfire.begin(), 
  all_triggers_didfire.end(), 
  [](std::pair<std::string, bool> p){return p.second;}
);

有没有比为此定义 lambda 表达式更简单的方法?

【问题讨论】:

  • 如果你有c++14,使用[](auto p) { return p.second; }..
  • [](decltype(all_triggers)::value_type p) { return p.second; }
  • lambda 对我来说似乎很好,除了它应该通过引用来获取它的参数以避免为复制字符串分配内存。 (同样适用于上面的两个 cmets)。
  • @JonathanWakely 除了它需要是std::pair&lt;const std::string, bool&gt; 才能真正避免复制:)

标签: c++ dictionary lambda countif


【解决方案1】:

我会使用 std::set 而不是 std::map。它们在语义上是等效的,但使用 std::set 更容易。示例:

std::set<std::string> triggers_that_did_fire;
int count_did_fire = triggers_that_did_fire.size();

当您最初填充 triggers_that_did_fire 集时,您可以执行以下操作:

triggers_that_did_fire.insert(mystring); //equivalent to setting to "true" in your map
triggers_that_did_fire.remove(mystring); //equivalent to setting to "false"

【讨论】:

  • @Steephen,我相信作者有他/她自己的逻辑理由来使用 map/set 而不是 std::vector。例如,也许作者的软件也想查询特定触发器是否确实触发了。
  • 这不是问题的答案。
  • @Steephen: "如果在这里可以使用 std::set,则 std::vector 可能更合适" 完全错误
  • 不,它们在语义上不等价。布尔映射可以将 X 映射为 true,将 X 映射为 false,或者根本不映射 X。一个集合只能包含 X 或不包含 X。
  • @Steephen:他可以,但这将是一种糟糕的方法。这是集合的完美用例。我不知道您为什么建议切换到不太合适的容器。
【解决方案2】:

有时,一个简单的 for 循环会更清晰一些:

auto count = 0;
for (auto&& p : all_triggers_didfire)
  if (p.second)
    ++count;

编辑 1:如果有人看不到编辑历史,我会发布原始代码..

auto count = 0;
for (auto& p : all_triggers_didfire)
  count += p.second;

【讨论】:

  • @rightfold,为什么要改变?以前的方式有什么问题?依赖boolint转换有那么可怕吗?
  • 依赖这样的转换会令人困惑,并且会使代码的可读性不如 if 语句。
  • @rightfold,很有意思,所以你引入了一个通用参考来“澄清”代码? :) 现在当您查看上面的代码时,首先引起您注意的是什么? &amp;&amp; 这是什么东西,它在那里做什么? IMO 在这一点上做了一个双重考虑,并试图弄清楚那里发生了什么,而不是跳过之前的代码,因为它非常简洁......无论如何,我猜这只是我......跨度>
  • @rightfold:我不同意你的编辑——它违背了发帖人的意图。我认为它是基于意见的。您应该发表评论或发布我们自己的答案。
  • @rightfold,在这里使用转发引用有的好处。我们知道取消引用 map&lt;T,U&gt;::iterator 会产生一个左值,那么为什么还要让代码足够通用以处理不可能的右值呢?
【解决方案3】:

您可以使用std::mem_fn 将对数据成员的访问封装到一个可调用对象中:

int count_did_fire = std::count_if(
  all_triggers_didfire.begin(), 
  all_triggers_didfire.end(), 
  std::mem_fn(&decltype(all_triggers_didfire)::value_type::second)
);

【讨论】:

  • 有什么理由不使用std::mem_fn
【解决方案4】:

有没有比为此定义 lambda 表达式更简单的方法?

取决于你的意思是更容易。这里要记住的一件重要事情是,在 C++ 中,std::mapvalue_typepair&lt;const key_type,mapped_type&gt; 而不仅仅是 mapped_type。 std::map::iterator 迭代此 value_type,您需要一个包装器来获取键或映射类型。

所有 C++ 标准库算法都适用于迭代器,对于 std::map,它是一个迭代器到 value_type。因此,为了让算法在映射类型上工作,我们需要将 value_type 重新映射到映射类型,然后

  1. 您需要一个名为函数的助手(C++11 之前)
  2. 你需要一个仿函数
  3. 或者,您需要一个 lambda。

值得注意的是

“如果 C++ 标准库算法使用起来会更愉快 C++ 支持 lambda"

向 C++ 标准添加 lambda 函数的提议,N1958=06-002。

因此,如果您认为您的代码看起来很难看,那么您清理代码的意图会破坏 lambda 的最初动机。

因此,如果您打算使用 C++ 标准库算法,则需要在需要时使用 lambda,例如 std::map(句点)。 当然,您仍然可以使用iterative manner 重新编写,但这是一个选择和可读性的问题,“可读性在于审稿人的眼中”

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-10
    • 2016-10-16
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多