是否有另一种方法可以在不使用 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< char, int >
就足够了。并且不需要初始化地图。如果密钥尚未使用,operator[]
将插入一个零元素。
为什么不std::unordered_map< char, int >
?当你只需要一个字符时支付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标准对<climits>
和<limits.h>
中宏的规范,C标准设定了8
的下限。
对;我误解了你的回答。删除了我的评论。以上是关于是否有另一种方法可以在不使用 if 语句的情况下有条件地增加数组值? C++的主要内容,如果未能解决你的问题,请参考以下文章
SQL-'08:多个 Replace 语句是一种不好的做法/是不是有另一种方法来编写此查询?
在不使用地图Android的情况下获取用户(纬度,经度)位置[重复]
是否有另一种方法可以在 PHP 中编写字符串文字(不带 ' 或 ")?
是否有另一种方法可以从 Spring MVC 中的 HttpServletRequest 对象获取用户的时区? [复制]