我发现,在输出信息时,一个常见的模式是需要将一个单词多元化。例如,如果需要输出找到的文件数量,则需要编写以下内容
std::cout << "Found " << count << " " << (count == 1 ? "file" : "files");为了使用正确的单数或复数形式的"file“,而不使用字符串"file(s)"作为任何count值的通用用法。
为了减少这段代码的冗长性(并练习编写字符串操作代码),我开发了一个函数,如果一个单词(string)附加到一个不等于1的数字上,那么它的单数形式(String)就会多元化(如果数字是0 --就像"0文件“中那样--或者即使它不是整数--就像"0.2事物”中那样)。由于有些词有不寻常的多元规则,函数接受复数形式后缀的附加参数,以及移除的单数形式的任何部分--这些附加参数的默认值对应于通常的多元规则,只需在单数形式中添加一个"s“。
下面是函数及其文档,以及运行它的一个小示例程序:
#include <iostream>
#include <string>
/** \brief Pluralizes a string if the given quantity is not equal to exactly 1.
\tparam number an arithmetic type
\param[in] n the quantity of the string to possibly pluralize. The singular form is used if the quantity
is equal to exactly 1, otherwise the plural form is used. The plural form is used if the quantity is a
decimal that is not equal to 1 (e.g. 1.1), 0, negative (including -1), etc.
\param[in] singular the singular form of the string.
\param[in] plural_suffix the suffix string which is appended to the root of the plural form.
\param[in] replaced_root the characters at the end of the singular form which are removed/replaced to form
the root of the plural form.
\return `singular` if `n == 1`, otherwise `replaced_root` is removed from the end of the `singular` and `plural_suffix` is appended to that.
For example:
\li `pluralize(1, "test")` returns `"test"`
\li `pluralize(2, "test")` returns `"tests"`
\li `pluralize(2, "ox", "en")` returns `"oxen"`
\li `pluralize(2, "story", "ies", "y")` returns `"stories"`
\li `pluralize(2, "life", "ves", "fe")` returns `"lives"`
\li `pluralize(2, "mouse", "ice", "ouse")` returns `"mice"`
\li `pluralize(2, "sheep", "")` returns `"sheep"`
\li `pluralize(2, "he", "they", "he")` returns `"they"`
*/
template<typename number>
std::string pluralize(number n, const std::string& singular, const std::string& plural_suffix = "s", const std::string& replaced_root = "") {
using namespace std::string_literals;
if (n == number{1}) return singular;
auto plural_root = ""s;
const auto singular_size = singular.size();
const auto replaced_root_size = replaced_root.size();
// If replaced_root is an empty string then the singular form is the same as the plural root
// replaced_root_size > singular_size should never be true, but fall back on singular as the plural root
if (replaced_root_size == 0 || replaced_root_size > singular_size) {
plural_root = singular;
} else {
// Index of the first character of the root replacement section
// Cannot be less than 0 because singular.size() >= replaced_root.size()
// Cannot be greater than singular.size() -- i.e. past singular.end() -- because replaced_root.size() >= 0
const auto index = singular_size - replaced_root_size;
// Iterators of interest
const auto singular_begin = singular.begin();
const auto iter = singular_begin + index;
// The last replaced_root.size() characters of singular must match replaced_root
// If there is no match just use the singular form as the plural root
if (std::string{iter, singular.end()} == replaced_root) {
plural_root = {singular_begin, iter};
} else {
plural_root = singular;
}
}
return plural_root + plural_suffix;
}
int main() {
int count = 2;
std::cout << "Found " << count << " " << pluralize(count, "file");
}我还使用以下Google测试代码对其进行了测试,该代码通过了所有测试用例:
TEST(Strings, Pluralize) {
EXPECT_EQ(pluralize(1, "test"s), "test"s);
EXPECT_EQ(pluralize(2, "test"s), "tests"s);
EXPECT_EQ(pluralize(0, "ox"s, "en"s), "oxen"s);
EXPECT_EQ(pluralize(1.1, "story"s, "ies"s, "y"s), "stories"s);
EXPECT_EQ(pluralize(-1.0, "life"s, "ves"s, "fe"s), "lives"s);
EXPECT_EQ(pluralize(-0.9, "mouse"s, "ice"s, "ouse"s), "mice"s);
EXPECT_EQ(pluralize(-2, "sheep"s, ""s), "sheep"s);
EXPECT_EQ(pluralize(-2.5, "he"s, "they"s, "he"s), "they"s);
}有什么改进代码的建议(命名,注释,效率,风格等等)?
发布于 2021-03-22 19:40:10
老实说,我不明白为什么我们需要根替代的东西。我们就不能把单数和复数的全部单词传递给函数吗?这将简单地变成:
template<class N>
std::string pluralize(N n, std::string const& singular, std::string const& plural) {
return n == N{ 1 } ? singular : plural;
}我们也许可以提供一个重载版本,只需一个参数,并添加"s“,以节省打字的简单复数。
我不认为“别名”变量在这种情况下真的有帮助。键入singular.size()和键入singular_size一样容易。这也更清楚了,因为我们不需要检查变量声明,就可以看到singular_size实际上是singular.size()。
if (replaced_root_size == 0 || replaced_root_size > singular_size) {
plural_root = singular;我们已经完成了这个执行分支所需要的所有工作,所以我们应该从这里的函数返回。然后,我们还不需要plural_root变量,以后也不需要将代码缩进else子句。
// Cannot be less than 0 because singular.size() >= replaced_root.size()
// Cannot be greater than singular.size() -- i.e. past singular.end() -- because replaced_root.size() >= 0我们应该提出这些assert离子,而不是评论。
使用singular.substr(...)可能会更清楚,而不是用迭代器构造新的字符串。
// The last replaced_root.size() characters of singular must match replaced_root
// If there is no match just use the singular form as the plural root
if (std::string{iter, singular.end()} == replaced_root)我不喜欢在这里默默地失败。如果函数的用户调用不正确,他们可能想知道这一点(也许我们应该抛出一个错误?)
注意,std::string在C++20中有一个ends_with()函数。
如前所述,我只提供完整的复数词。
但是,如果方法保持不变,我可能会对代码做些什么。
template<typename number>
std::string pluralize(number n, const std::string& singular, const std::string& plural_suffix = "s", const std::string& replaced_root = "") {
if (n == number{ 1 })
return singular;
if (replaced_root.empty())
return singular + plural_suffix;
if (!singular.ends_with(replaced_root)) // C++ 20
throw std::runtime_error("Invalid root replacement.");
auto const plural_root = singular.substr(0, singular.size() - replaced_root.size());
return plural_root + plural_suffix;
}(未实际编译/测试)。
https://codereview.stackexchange.com/questions/257537
复制相似问题