是否有另一种方法可以在不使用 if 语句的情况下有条件地增加数组值? C++

Posted

技术标签:

【中文标题】是否有另一种方法可以在不使用 if 语句的情况下有条件地增加数组值? C++【英文标题】:Is there an alternate way to conditionally increment array values without using if statements? C++ 【发布时间】:2019-01-18 18:06:16 【问题描述】:

如果有数组,

int amounts[26] =  0, 0, 0, ...;

并且我希望数组的每个数字表示不同字符串的数量,例如在给定字符串中找到的'a' 中的amounts[0] = amount;,无论如何都可以在不使用 if 语句的情况下递增每个值?

伪代码示例:

int amounts[26] =  0, 0, 0, ...; 
string word = "blahblah";
loop here to check and increment amounts[0] based on amount of 'a's in string
repeat loop for each letter in word.`

在循环结束时,基于字符串单词,数量应该如下:

amounts[0] = 2 ('a')
amounts[1] = 2  ('b')
amounts[2] = 0  ('c')
// etc

【问题讨论】:

你的意思是没有特定的 if 语句,还是没有一般的分支? 如果我理解的话,类似++amounts[word[i] - 'a'];? 您是否愿意将自己限制在特定的字符集,或者您是否正在寻找一个完全通用/便携的解决方案? 【参考方案1】:

鉴于你的例子,假设整个字符串是小写和有效字符,有一个相当简单的解决方案(也就是说,你处理验证)

for (int i = 0; i < word.size(); i++) 
    amounts[word[i]-'a']++; // you can also do a pre-increment if you want

【讨论】:

不要以为这不是便携的。 C++ 需要支持 (EBCDIC) 的字符集之一不会将 a - z 映射到连续范围。 只是补充一下,因为还没有人提到它,这不是便携式的。 就 EBCDIC 问题达成一致 - 尽管很可能没有太大的相关性。更关键的是:如果字符串中同时包含大小写字母或非字母字符怎么办? @Aconcagua 如果您阅读答案的第一部分“假设整个字符串是小写且有效字符,则有一个相当简单的解决方案(也就是说,您处理验证)” 但是,如果您想要一个更通用(我认为更好)的答案,一种解决方案是将所有需要的字符映射到适当的索引,并且如果该字符不在映射它是一个无效的字符,可以随意处理【参考方案2】:

你想要什么:

const char char_offset = 'a';
const int num_chars = 26;
std::vector<int> amounts(num_chars, 0);
std::string word = "blahblah";

for (auto c : word) 
  int i = c - char_offset;
  // this if statement is only for range checking.
  // you can remove it if you are sure about the data range.
  if (i >= 0 && i < num_chars)  
    ++amounts[i];
  


for (int i = 0; i < (int)amounts.size(); ++i) 
  std::cout << (char)(char_offset + i) << ": " << amounts[i] << std::endl;


Output
a: 2
b: 2
c: 0
d: 0
e: 0
f: 0
g: 0
h: 2
i: 0
j: 0
k: 0
l: 2
m: 0
n: 0
...

【讨论】:

"不使用 if 语句" 这仅用于范围检查。如果您不在乎,当然可以跳过它。 我真的不明白为什么这值得一票否决,其中一些有点冗长但是......?【参考方案3】:

使用std::unordered_map< std::string, int >。请注意,如果只需要一个字符,则 std::unordered_map 会更有效。 std::string 允许计算复杂的字符串(例如 map["substring"]++ )

可以使用括号表示法(例如 map[index] )访问地图,因此可以有效地消除对 if 语句的需要。

#include <string>
#include <unordered_map>
#include <iostream>

int main()

    std::unordered_map< std::string, int > map =   "a",0 ;
    map["a"] += 1;
    std::cout << map["a"];

【讨论】:

似乎std::unordered_map&lt; char, int &gt; 就足够了。并且不需要初始化地图。如果密钥尚未使用,operator[] 将插入一个零元素。 为什么不std::unordered_map&lt; char, int &gt;?当你只需要一个字符时支付std::string 有点过分了。 此外,如果您允许用户将字母表指定为std::string,然后使用循环填充地图,即std::string alpha = "abcdef...",然后编写一个循环来填充地图从a, 0开始。 OP 说他想“表示不同字符串的数量”,因此使用了 std::string。我可能太从字面上理解了。是的,char 会更有效,但这更灵活。我只是为了更容易看到值是如何保存的而进行初始化的。【参考方案4】:

一个通用且可移植的解决方案是

const std::string alphabet = "abcdefghijklmnopqrstuvwxyz";
for (int i = 0; alphabet[i]; ++i)
      amounts[i] = std::count(word.begin(), word.end(), alphabet[i]);

如果你可以假设小写字母的集合是一个连续的范围,这可以简化为

for (char c = 'a'; c <= 'z'; ++c)
    amounts[c - 'a'] = std::count(word.begin(), word.end(), c);

上面没有(公开的)if。当然,没有什么可以阻止 std::count() 使用一个来实现。

【讨论】:

【参考方案5】:

以下有相当多的机会成为计数最快的人之一:

std::array<unsigned int, (1U << CHAR_BIT)> counts( );
for(auto c : word)
    counts[c]++;

获取单个值非常有效:

 std::cout << "a: " << counts['a'] << std::endl

遍历字母 - 嗯,需要一个小技巧:

 for(char const* c = "abcdefghijklmnopqrstuvwxyz"; *c; ++c)
     // case-insensitive:
     std::cout << *c << ": " << counts[*c] + counts[toupper(*c)] << std::endl;

当然,您正在浪费一些内存 - 这可能会使您再次获得性能损失:如果数组不再适合缓存...

【讨论】:

@Aconcagua - 允许的CHAR_BIT 的最小值是8。 (C++标准参考C标准对&lt;climits&gt;&lt;limits.h&gt;中宏的规范,C标准设定了8的下限。 对;我误解了你的回答。删除了我的评论。

以上是关于是否有另一种方法可以在不使用 if 语句的情况下有条件地增加数组值? C++的主要内容,如果未能解决你的问题,请参考以下文章

SQL-'08:多个 Replace 语句是一种不好的做法/是不是有另一种方法来编写此查询?

在不使用地图Android的情况下获取用户(纬度,经度)位置[重复]

是否有另一种方法可以在 PHP 中编写字符串文字(不带 ' 或 ")?

应避免使用 flatDirs,因为它不支持任何元数据格式

是否有另一种方法可以从 Spring MVC 中的 HttpServletRequest 对象获取用户的时区? [复制]

是否有另一种方法可以在 Eloquent 模型上“设置连接”?