如何获取字符串中的 std::set 个字符作为字符串?
How can I get a std::set of characters in a string, as strings?
我有一个std::string
。我想要其中的一组唯一字符,每个字符表示为 std::string
.
我可以很容易地得到字符集:
std::string some_string = ...
std::set<char> char_set(some_string.begin(), some_string.end());
我可以将它们转换成这样的字符串:
std::set<std::string> string_set;
for (char c: char_set) {
string_set.emplace(1, c);
}
但这样的做法似乎有些别扭。有没有更好的(最好是标准库单行)方法来做到这一点?
一个transform
可以作为一行:
transform(begin(some_string), end(some_string),
inserter(string_set, begin(string_set)),
[] (char c) -> std::string { return {c}; });
我不建议使用此解决方案,因为它非常难读。 通常您希望编写直观且易于理解的代码。您在答案中所写的内容已经足够了,我不建议您寻找捷径来将您的代码缩减为一行,同时牺牲其清晰度。
您可以使用:
std::for_each(some_string.begin(), some_string.end(),
[&string_set] (char c) -> void { string_set.insert(std::string({c}));});
您还可以使用:
for (char c: some_string)
{
string_set.insert(std::string{c});
}
工作程序:
#include <iostream>
#include <string>
#include <set>
#include <algorithm>
int main()
{
std::string some_string = "I want the set of unique characters in it";
std::set<std::string> string_set;
for (char c: some_string)
{
string_set.insert(std::string{c});
}
for (std::string const& s: string_set)
{
std::cout << s << std::endl;
}
}
输出:
I
a
c
e
f
h
i
n
o
q
r
s
t
u
w
Is there a better (preferrably standard-library one-liner) way to do this?
没有。您在 C++ 标准库中找到的任何内容都适用于更复杂的情况,在这些情况下,它们 简化了 否则您必须编写的代码。在您的情况下,您的代码更简单。试图强迫自己为此使用标准库中的某些东西会使您的代码更加复杂。
已经发布了三个答案来证明这一点 - 它们完全符合您的要求,但一眼看去几乎不可读,并且当编译器无法优化它们时它们会增加不必要的开销。
您的 for 循环是更好的解决方案。它很简单,它向 reader 传达了意图,编译器也很容易优化。没有理由浪费时间为简单问题寻找更复杂的解决方案。
所有解决方案都是正确的,但您应该始终选择最简单 正确的解决方案。少写代码,而不是多写。
我怀疑你想要的是个好主意,如果你真的坚持,你可以创建一个 class 支持从 char
的隐式转换,到 std::string
的隐式转换,并且可以与自身的另一个实例或字符串进行比较:
class cvt {
char val;
public:
cvt(char val) : val(val) {}
bool operator<(cvt other) const { return val < other.val; }
bool operator<(std::string const &s) const {
return !s.empty() && val < s[0];
}
friend bool operator<(std::string const &s, cvt const &c) {
return !s.empty() && s[0] < c.val;
}
operator std::string() const { return std::string(1, val); }
};
有了这个,我们可以创建我们的 set<cvt>
,但使用它就好像它是 set<std::string>
(因为其中的元素 can/will 隐式转换为 std::string
并与 std::string
):
进行比较
int main() {
std::string some_string = "ZABCDECD";
// Create our (sort of) set<string> from characters in some_string:
std::set<cvt> char_set(some_string.begin(), some_string.end());
// An actual set<string> to use with it:
std::set<std::string> strings{ "A", "C", "E", "F", "Y" };
// demonstrate compatibility:
std::set_intersection(char_set.begin(), char_set.end(), strings.begin(), strings.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
如果我们查看 generated code for this on Godbolt,我们会发现它几乎全是语法糖——为 cvt
class 实际生成的唯一代码是微小的部分从 char
复制一个字节以创建 cvt
,并将 cvt
与 string
进行比较。其他一切都已优化不复存在。
如果我们确定我们的字符串不会为空,我们可以简化与 return val < s[0];
和 return s[0] < val;
的比较,在这种情况下它们也会被优化掉,所以唯一的代码使用 cvt
生成的是从源复制一个字节以构建 cvt
对象。
根据您的想法,可能符合您的要求。这是相当多的额外输入,但它优化得很好——将 cvt
与字符串进行比较可能比将 string
与 string
进行比较更快。到目前为止,最大的缺点可能源于质疑您的基本前提,并想知道为什么您不只是编写一个循环并完成它。
string setToString(const set<char> &s) {
string str = "";
std::accumulate(s.begin(), s.end(), str);
return str;
}
也许这可能有用。
我有一个std::string
。我想要其中的一组唯一字符,每个字符表示为 std::string
.
我可以很容易地得到字符集:
std::string some_string = ...
std::set<char> char_set(some_string.begin(), some_string.end());
我可以将它们转换成这样的字符串:
std::set<std::string> string_set;
for (char c: char_set) {
string_set.emplace(1, c);
}
但这样的做法似乎有些别扭。有没有更好的(最好是标准库单行)方法来做到这一点?
一个transform
可以作为一行:
transform(begin(some_string), end(some_string),
inserter(string_set, begin(string_set)),
[] (char c) -> std::string { return {c}; });
我不建议使用此解决方案,因为它非常难读。 通常您希望编写直观且易于理解的代码。您在答案中所写的内容已经足够了,我不建议您寻找捷径来将您的代码缩减为一行,同时牺牲其清晰度。
您可以使用:
std::for_each(some_string.begin(), some_string.end(),
[&string_set] (char c) -> void { string_set.insert(std::string({c}));});
您还可以使用:
for (char c: some_string)
{
string_set.insert(std::string{c});
}
工作程序:
#include <iostream>
#include <string>
#include <set>
#include <algorithm>
int main()
{
std::string some_string = "I want the set of unique characters in it";
std::set<std::string> string_set;
for (char c: some_string)
{
string_set.insert(std::string{c});
}
for (std::string const& s: string_set)
{
std::cout << s << std::endl;
}
}
输出:
I a c e f h i n o q r s t u w
Is there a better (preferrably standard-library one-liner) way to do this?
没有。您在 C++ 标准库中找到的任何内容都适用于更复杂的情况,在这些情况下,它们 简化了 否则您必须编写的代码。在您的情况下,您的代码更简单。试图强迫自己为此使用标准库中的某些东西会使您的代码更加复杂。
已经发布了三个答案来证明这一点 - 它们完全符合您的要求,但一眼看去几乎不可读,并且当编译器无法优化它们时它们会增加不必要的开销。
您的 for 循环是更好的解决方案。它很简单,它向 reader 传达了意图,编译器也很容易优化。没有理由浪费时间为简单问题寻找更复杂的解决方案。
所有解决方案都是正确的,但您应该始终选择最简单 正确的解决方案。少写代码,而不是多写。
我怀疑你想要的是个好主意,如果你真的坚持,你可以创建一个 class 支持从 char
的隐式转换,到 std::string
的隐式转换,并且可以与自身的另一个实例或字符串进行比较:
class cvt {
char val;
public:
cvt(char val) : val(val) {}
bool operator<(cvt other) const { return val < other.val; }
bool operator<(std::string const &s) const {
return !s.empty() && val < s[0];
}
friend bool operator<(std::string const &s, cvt const &c) {
return !s.empty() && s[0] < c.val;
}
operator std::string() const { return std::string(1, val); }
};
有了这个,我们可以创建我们的 set<cvt>
,但使用它就好像它是 set<std::string>
(因为其中的元素 can/will 隐式转换为 std::string
并与 std::string
):
int main() {
std::string some_string = "ZABCDECD";
// Create our (sort of) set<string> from characters in some_string:
std::set<cvt> char_set(some_string.begin(), some_string.end());
// An actual set<string> to use with it:
std::set<std::string> strings{ "A", "C", "E", "F", "Y" };
// demonstrate compatibility:
std::set_intersection(char_set.begin(), char_set.end(), strings.begin(), strings.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
如果我们查看 generated code for this on Godbolt,我们会发现它几乎全是语法糖——为 cvt
class 实际生成的唯一代码是微小的部分从 char
复制一个字节以创建 cvt
,并将 cvt
与 string
进行比较。其他一切都已优化不复存在。
如果我们确定我们的字符串不会为空,我们可以简化与 return val < s[0];
和 return s[0] < val;
的比较,在这种情况下它们也会被优化掉,所以唯一的代码使用 cvt
生成的是从源复制一个字节以构建 cvt
对象。
根据您的想法,可能符合您的要求。这是相当多的额外输入,但它优化得很好——将 cvt
与字符串进行比较可能比将 string
与 string
进行比较更快。到目前为止,最大的缺点可能源于质疑您的基本前提,并想知道为什么您不只是编写一个循环并完成它。
string setToString(const set<char> &s) {
string str = "";
std::accumulate(s.begin(), s.end(), str);
return str;
}
也许这可能有用。