我正在尝试使用递归来计算字符串中的元音数量。以下是我到目前为止的情况:
int vowels(string str, int pos, int length)
{
if (str.length() == 0)
return 0;
switch (str[pos])
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return 1 + vowels(str.substr(1), pos, length);
default:
return vowels(str.substr(1), pos, length);
}
}
int main()
{
string str;
int len;
cout << "Enter a string: ";
getline(cin, str);
cout << endl;
len = static_cast<int>(str.length());
cout << "Number of vowels in \"" << str << "\" = "
<< vowels(str, 0, len) << endl;
return 0;
}
问题是,我需要在第一次调用元音函数时将pos设置为0,而不是在后续的递归调用中将其重置为0。另外,我不需要使用子串,而是需要在每次递归调用元音()之前递增pos。此外,基本情况应该是pos == length(当没有更多字符要检查字符串时)。
答案 0 :(得分:1)
好像你忘了增加pos:
/* inside the vowels function switch statement */
return 1 + vowels(str.substr(1), pos+1, length);
default:
return vowels(str.substr(1), pos+1, length);
此外,如果将递归条件的结尾更改为“pos == str.length()”,则根本不需要str.substr(...)。另外,如果跳过substr(...),则通过const引用传递std :: string(这是一个好习惯)。
答案 1 :(得分:1)
实现这一目标的方式与西伯利亚夏季的白天时间一样多。从简单开始。将条件逻辑与枚举分开。无论条件(它是元音)是否真实,你总是转到下一个要测试的字符(或者你完成了你的字符串和你刚刚完成的字符)。所以从一个脑死亡的简单条件检查开始:
static bool is_vowel(char c)
{
return (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' ||
c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U');
}
一旦你有了这个,设计一个简单的方法来走你的字符串。 C ++ 重度提升了迭代器模型,这也不例外:
// string iterator examiner
static int count_vowels(std::string::const_iterator cur, std::string::const_iterator end)
{
return (cur == end) ? 0 : is_vowel(*cur) + count_vowels(std::next(cur), end);
}
最后,设计一种从调用方调用它的方法,而不必直接指定那些迭代器:
// friendly wrapper for checking a string for vowels.
int count_vowels(const std::string& s)
{
return count_vowels(s.begin(), s.end());
}
结果是一个可调用的接口,使用如下:
int main()
{
std::string s = "A simple string for testing vowel counting.";
std::cout << count_vowels(s) << '\n';
return 0;
}
<强>输出强>
12
非递归替代:更好的方式
老实说,这是执行您所负责的操作的可怕方式,尽管它至少会向您介绍递归。如果您对如何执行此操作感兴趣没有递归,我们可以使用我们编写的简单条件函数(is_vowel()
),使用标准库提供的更好的算法count_if
。这样,您的递归可以被删除并替换为:
// friendly wrapper for checking a string for vowels.
long vowels(const std::string& s)
{
return std::count_if(s.begin(), s.end(), is_vowel);
}
结果将是相同的,并且直观地说,函数名称清楚地表明您正在做什么:计算字符串中is_vowel
为真的所有字符。
祝你好运,我希望你能从中得到一些东西。
答案 2 :(得分:0)
我会按以下方式定义函数
#include <cstring>
#include <string>
std::string::size_type count_vowels( const std::string &s,
std::string::size_type pos,
std::string::size_type length )
{
const char *vowels = "aeiouAEIOU";
if ( s.length() <= pos || length == 0 ) return 0;
return ( s[pos] && std::strchr( vowels, s[pos] ) != 0 ) +
count_vowels( s, pos + 1, length - 1 );
}
并使用您的方法
#include <string>
std::string::size_type count_vowels( const std::string &s,
std::string::size_type pos,
std::string::size_type length )
{
if ( s.length() <= pos || length == 0 ) return 0;
std::string::size_type n = 0;
switch ( s[pos] )
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
++n;
default:
return n + count_vowels( s, pos + 1, length - 1 );
}
}
答案 3 :(得分:0)
您不应该使用递归来解决此问题。有时在进行C ++开发时使用递归是明智的,但对于一般用途,缺少tail-call elision代表了一点性能瓶颈。一般来说,如果递归过程每次调用只调用一次,那么最好使用循环。如果一个recusive进程调用自己两次(例如,下降到树型数据结构的左右分支,或者可能计算一个fibonacci序列),它可能更容易使用递归函数一个循环。
尽管如此,这显然是一项家庭作业,而合理的软件开发实践也没有。
传入pos
和length
以及string
似乎很愚蠢...... C ++已经为您提供了一个很好的通用工具来处理迭代集合(和一个字符串只是一个可迭代的字符集合,以... Iterators的形式出现!
迭代器有效地将“位置”的概念封装在容器中。 string::begin()
会为您指定一个指向字符串开头的迭代器,string::end()
会指向一个指向结尾的Iterator。您可以取消引用非结束迭代器以获取它指向的字符(很像指针),并递增迭代器以将其前进到集合中的下一个元素。
这是一个简单计算字符数量的例子,但我相信你能够根据自己的需要进行调整。
template<class Iterator>
int count_characters(Iterator begin, Iterator end)
{
// if we're at the end of the string, there's nothing more to count
if (begin == end)
return 0;
else // note the pre-increment here. post-increment won't work.
return 1 + count_characters(++begin, end);
}
可以像这样使用:
std::string foo = "a phrase, yay";
std::cout << count_characters(foo.begin(), foo.end());
作为奖励,它可用于任何其他简单容器...... vector
或set
char
与string
一样有效这里,因为迭代器提供了一种很好的通用方法来处理容器。然而,这个设施可能对这个特定的家庭作业问题不感兴趣。
一个函数,不需要包装器或传递多余的参数。如果你将这种情况复制到你交付的工作中,要小心;如果你不理解template
,这将是一个轻率剽窃的确定标志; - )