【问题标题】:Get an iterator from a char pointer (C++)从 char 指针获取迭代器 (C++)
【发布时间】:2016-11-08 20:15:20
【问题描述】:

我正在挑战自己编写一个仅使用 SL 算法、迭代器等的回文测试器。我还想通过编程来处理原始字符串。下面,我在copy_if 算法中使用了原始指针pal,但相反,我如何定义一个迭代器到这里,即使用begin(pal)end(pal + size) 之类的东西?

#include <algorithm>
#include <iterator>
#include <cctype>

using namespace std;

bool isPalindrome(const char* pal) {
    if (!pal) { return(false); }

    int size = strlen(pal);
    string pal_raw;
    pal_raw.reserve(size);

    // Copy alphabetical chars only (no spaces, punctuations etc.) into pal_raw
    copy_if(pal, pal+size, back_inserter(pal_raw),
        [](char item) {return isalpha(item); }
    );

    // Test if palindromic, ignoring capitalisation
    bool same = equal(begin(pal_raw), end(pal_raw), rbegin(pal_raw), rend(pal_raw),
        [](char item1, char item2) {return tolower(item1) == tolower(item2); }
    );

    return same;
}

int main(){
    char pal[] = "Straw? No, too stupid a fad. I put soot on warts.";
    bool same = isPalindrome(pal);
    return 0;
}

额外问题:是否可以通过在equal() 内(即!isalpha(item))“就地”递增迭代器来消除对copy_if() 的需要?

【问题讨论】:

  • 为什么不使用std::string 而不是char数组?
  • @NathanOliver 我已经为std::string 编写了一个重载,希望也能够处理字符数组。
  • 一个指针已经一个迭代器。
  • 一个字符数组/指针可以转换成一个字符串,所以你不需要一个特殊的函数。
  • @GregBrown -- 我已经为 std::string 编写了一个重载,希望也能够处理 char 数组。 -- 那是 1 行函数 -- 只需从 char* 版本中调用 std::string 版本,并将转换后的 char 数组转换为 std::string

标签: c++ pointers iterator c++-standard-library stl-algorithm


【解决方案1】:

当涉及到 C++ 库算法时,迭代器实现了指针的概念。而且,正如您所发现的,采用迭代器的 C++ 库算法也非常乐意采用指针。是同一个概念。

当您已经有了开始的指针时,就没有某种迭代器可以将指针转换为。

这是真的

std::begin(arr)

std::end(arr)

在平面数组上定义。但是,你猜怎么着:它们返回一个指向数组开头和结尾的指针,而不是某种迭代器类。

但是,您不能使用std::begin()std::end(),因为当您需要使用它时,在您的函数内部,数组已经衰减为char *std::begin()std::end() 适用于真正的数组,而不是衰减的指针。

如果您坚持使用迭代器,则应将 std::string 传递给回文函数,而不是 char *std::string 实现了一个 begin() 和一个 end() 方法,它们返回一个 std::string::iterator,您可以使用它们。

【讨论】:

    【解决方案2】:

    如果我这样做并且我想让它适用于不同的类型,我会模板化该方法 - 例如:

    template<class ITER_T>
    bool isPalindrome(ITER_T begin, ITER_T end) {
       // check for palindrome generically between begin and end
    }
    

    这样,const char * 迭代器、std::stringstd::vector&lt;char&gt;std::map&lt;char&gt; 使用相同的代码即可。如果实施得当,即使对于其他类型的向量、地图和任何其他类型的向量、映射和任何其他您可以获得迭代器的项目类型都定义了比较运算符。

    作为奖励,我还可以检查字符数组、字符串或向量的一部分是否是回文。

    顺便说一句:

    equal(begin(pal_raw), end(pal_raw), rbegin(pal_raw), rend(pal_raw), ...
    

    不必要地检查每个字符两次,如果字符串是回文......不是很有效。在这种情况下,您可以使用“手动”循环做得更好(不确定是否有标准算法)。


    如果您想让 begin(arr)/end(arr) 在重载函数中工作,则无论如何都需要对其进行模板化,如下所示:

    template<size_t N>
    bool isPalindrome(const char (&arr)[N]) {
    ...
    

    但是,对于每个不同的数组大小,您都会获得单独的实例化。因此,无论如何最好使用迭代器进行模板化,并且只获得任何 char 数组大小的单个实例化。


    所以要回答“奖励问题”,确实可以通过直接迭代数组来完全避免创建临时字符串(即动态内存分配):

    template<typename ITER_T>
    bool isPalindrome(ITER_T begin, ITER_T end) {
        while (begin < end) {
            if (tolower(*begin++) != tolower(*--end))
                return false;
        }
        return true;
    }
    bool same = isPalindrome(begin(pal), end(pal));
    

    为了测试isalpha,我留给你练习。提示:您可以在相等检查之前执行此操作并适当地增加/减少开始/结束(提示#2:我想到的解决方案将使用 continue 关键字)。

    同样可以使其适用于不同于char 的任意类型 - 然后可以使用类型特征通过模板特化抽象出 isalpha/tolower 调用。

    【讨论】:

    • 它甚至可以与wstringvector&lt;int&gt; 一起使用(当然,假设后者包含ASCII 字符)。只要在元素类型上调用 std::tolower 在您的上下文中是有意义的。
    • 是的,对tolower() 的调用可以通过类型特征抽象出来,因此它将返回不支持“小写”转换的类型的标识(例如vector&lt;int&gt; 检查任意回文整数数组)。
    • @axalis 我还没有介绍类型特征,如果回文中有大写字母,那会阻止它返回 true 吗?另外,是的,就像您说的那样,如果算法只检查每个字符一次,效率会更高。但是,这很难做到,因为 end() 不一定在中间(想象一个字符串的前半部分是空格) - 我知道我无论如何都删除了所有空格,但我正在努力寻找解决方案我避免任何额外的复制/删除,而是通过增加迭代器跳过非 alpha 字符
    猜你喜欢
    • 1970-01-01
    • 2016-09-03
    • 1970-01-01
    • 2014-10-13
    • 2012-02-27
    • 2011-05-22
    • 2020-05-29
    • 2012-07-11
    • 2019-01-24
    相关资源
    最近更新 更多