【问题标题】:Optimal iteration though char string通过 char 字符串进行优化迭代
【发布时间】:2013-08-01 22:06:37
【问题描述】:

这源自other question。如果我们有:

const std::string& S = ...;
int freq[CHAR_MAX-CHAR_MIN+1]={0};

以下四个循环是否等效?你更喜欢什么?

for (int           c: S) ++freq[c-CHAR_MIN];  // (1)
for (char          c: S) ++freq[c-CHAR_MIN];  // (2)
for (unsigned      c: S) ++freq[c];           // (3) <-- BAD!
for (unsigned char c: S) ++freq[c];           // (4)

【问题讨论】:

标签: c++ char type-conversion


【解决方案1】:

2 是最好的选择,因为它清楚地展示了您打算将每个字符用于什么(很简单:作为一个字符)。该含义在 1、3 和 4 中消失了。正如 Rapptz 所提到的,如果您有支持它的编译器(C++11 标准),您也可以使用 for (auto c : S)

此外,在 int (1)、unsigned int (3) 或 unsigned char (4) 中存储 char 是没有意义的,因为它们可以存储大于 char 的值。

【讨论】:

  • 基于范围的 for 是 C++11 添加的,所以如果它支持它但不支持 auto 会很奇怪。
  • 另外,int 和朋友可能会遭受不必要的符号扩展。但是,您没有提及/考虑std::wstring
  • @sehe:很好,谢谢。不过,我想知道是否值得一提(OP 使用的是普通的std::string
  • unsigned char 比 char 好?
  • @Quonux 上下文是什么?任何类型的 basic_ifstream 的标准规范都不支持它,例如因此,如果您使用的是chars,则没有char 很好。但是,当传递给例如std::is_space, std::to_lower 等确保你双重施法:static_cast&lt;int&gt;(static_cast&lt;unsigned char&gt;(c))
【解决方案2】:

使这个适当的通用:

#include <limits>
#include <vector>

template <typename C, typename T = typename C::value_type>
  std::vector<unsigned> histogram(C const& container)
{
    std::vector<unsigned> result(std::numeric_limits<T>::max() - std::numeric_limits<T>::min());
    for(auto& el : container)
        result[el - std::numeric_limits<T>::min()]++;

    return result;
}

现在,对于大元素类型T(无论输入长度如何),这将导致无用的大结果向量。考虑使用地图:

// for very large char types, consider
#include <map>

template <typename C, typename T = typename C::value_type>
  std::map<T, unsigned> histogram_ex(C const& container)
{
    std::map<T, unsigned> result;

    for(auto& el : container)
        result[el]++;

    return result;
}

一些使用演示:

#include <algorithm>
#include <string>
#include <iostream>

int main()
{
     auto v = histogram   (std::string ("hello world"));
     auto m = histogram_ex(std::wstring(L"hello world"));

     std::wcout << L"Sum of frequencies: " << std::accumulate(v.begin(), v.end(), 0) << "\n";

     for (auto p : m)
         std::wcout << L"'" << p.first << L"': " << p.second << L'\n';
}

打印:

Sum of frequencies: 11
' ': 1
'd': 1
'e': 1
'h': 1
'l': 3
'o': 2
'r': 1
'w': 1

全部查看Live on Coliru

【讨论】:

  • +1,即使不完全回答“这 4 个循环是否等效?”
  • 但是:它是完全通用的,不需要&lt;limits.h&gt; :/(map 方法甚至不需要&lt;limits&gt;
【解决方案3】:

我自己找到了答案,到目前为止还没有其他人的正确答案,所以我正在回答我自己的问题。

循环不等价。在(3)中,如果char有符号且值为-1,它将被转换为unsigned,符号扩展为4294967295

至于个人偏好哪个循环更好,我更喜欢(4),因为它不依赖于&lt;limits.h&gt;

编辑
(3) 在非二补系统上可能无法正常工作。所以(1)和(2)更好。将char 转换为int(或size_t)应该不会有任何性能意外。

【讨论】:

  • "在 (3) 中,如果 char 是有符号且值为 -1" 那是误导; char 永远不会是负数,因为它们来自字符串!因此,我的观点是明确表明意图,只有 #2 这样做。
  • 为什么字符串中的字符不能为负数? string S="abc"; S[0]=-1;。也许这个 S 有 utf8 字符串。
  • 好的,当然,您可以手动将字符设置为负数。但大概您的程序将在人类可读的字符串上运行,而不是极端情况。至于 Unicode,你是正确的 char 不适合。但是unsigned int 也不合适。使用实际的字符类型来明确您的意图。您不会将布尔变量存储在 char 中,因此不要将 chars 存储在 unsigned ints 中。
猜你喜欢
  • 1970-01-01
  • 2021-10-02
  • 1970-01-01
  • 1970-01-01
  • 2017-04-20
  • 2014-10-18
  • 1970-01-01
  • 2014-01-23
  • 2010-12-14
相关资源
最近更新 更多