【问题标题】:How can I get a std::set of characters in a string, as strings?如何将字符串中的 std::set 字符作为字符串?
【发布时间】:2015-06-29 01:33:02
【问题描述】:

我有一个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);
}

但这样的做法似乎很尴尬。有没有更好的(最好是标准库单行)方法来做到这一点?

【问题讨论】:

  • 究竟是什么看起来很尴尬?对我来说,整个事情似乎很尴尬。你为什么要这样做?
  • @Jefffrey 当它不使用标准库时,我讨厌循环,我的意思是,即时 O(n)。我想这样做是因为我将把这个集合与另一组std::strings 合并,所以两个集合需要具有相同的类型。
  • @EMBLEM 所以我们这里基本上有一个 XY 问题?
  • 我无法理解问题。
  • @EMBLEM,无论如何你都会得到一个循环,这是一个典型的 O(n) 问题。标准库或 boost 可以为您做的唯一事情就是从普通视图中隐藏循环。

标签: c++ string stdset


【解决方案1】:

你可以使用:

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;
   }
}

输出:

一世 一种 C e F H 一世 n ○ q r s 吨 你 w

【讨论】:

  • 您的解决方案基本上是基于范围的不可读版本!
  • @Columbo,这是我能想到的最好的了。
  • }));}); lisp 令人讨厌的一切,但更糟糕的是。 差很多
【解决方案2】:

transform 可以用作单行:

transform(begin(some_string), end(some_string),
          inserter(string_set, begin(string_set)),
          [] (char c) -> std::string { return {c}; });

我不建议使用此解决方案,因为它非常难以阅读。 通常您希望编写直观且易于理解的代码。您在答案中所写的内容已经足够了,我不建议您寻找捷径来将您的代码简化为单行代码,同时牺牲其清晰度。

【讨论】:

  • 这基本上与 OP 中不需要的代码相同,但现在开销更大,代码可读性更低。
  • 为什么这被否决了? OP想要一个单线,他得到了一个单线。它工作不是吗? +1
  • @0x499602D2 .. 是的,但是从字面上理解并提供一些仍然符合标准的不可读代码块并没有帮助。 OP显然问错了问题。
  • @EMBLEM 什么?这 any 比你的 for 循环好多少?
  • @Columbo 很明显,OP 的原始解决方案更清晰,更容易理解,但如果他想要单线,他就有了。我可以在回答中解释为什么他的更好。
【解决方案3】:

有没有更好的(最好是标准库单行)方法来做到这一点?

没有。您在 C++ 标准库中找到的任何内容都适用于更复杂的情况,它们简化您必须以其他方式编写的代码。在您的情况下,您的代码更简单。试图强迫自己使用标准库中的东西会使你的代码更加复杂。

已经发布了三个答案来证明这一点 - 它们完全符合您的要求,但它们几乎一目了然,并且当编译器无法优化它们时它们会增加不必要的开销。

您的 for 循环是更好的解决方案。它很简单,它向读者传达了意图,并且编译器很容易优化。没有理由浪费时间为一个简单的问题寻找更复杂的解决方案。

所有解决方案都是正确的,但您应该始终选择最简单正确的解决方案。编写更少的代码,而不是更多。

【讨论】:

    【解决方案4】:

    我怀疑你想要的是一个好主意,如果你真的坚持,你可以创建一个支持从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&lt;cvt&gt;,但是像使用set&lt;std::string&gt;一样使用它(因为其中的元素可以/将隐式转换为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"));
    }
    

    Live on Coliru.

    如果我们查看generated code for this on Godbolt,我们会发现它几乎都是语法糖——实际为cvt 类生成的唯一代码是复制一个字节以创建@987654332 的微小位@ 来自char,并将cvtstring 进行比较。其他所有内容都已优化不存在。

    如果我们确定我们的字符串不会为空,我们可以简化与return val &lt; s[0];return s[0] &lt; val; 的比较,在这种情况下它们也会被优化掉,所以唯一的代码是使用@987654338 生成的@ 是从源中复制一个字节来构造一个cvt 对象。

    根据您的想法,可能符合您的要求。这是相当多的额外输入,但它可以很好地优化——将cvt 与字符串进行比较可能比将stringstring 进行比较可能更快。到目前为止,最大的缺点可能源于质疑您的基本前提,并想知道为什么您不只是编写一个循环并完成它。

    【讨论】:

    • 这是过多的额外输入,它增加了编译器难以优化的开销,而且它违反了 KISS。但是它非常聪明,如果 for 循环不存在,我会这样做。
    【解决方案5】:
    string setToString(const set<char> &s) {
        string str = "";
        std::accumulate(s.begin(), s.end(), str);
        return str;
    }
    

    也许这可能有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-08
      • 1970-01-01
      • 2014-06-28
      • 2019-02-08
      • 1970-01-01
      相关资源
      最近更新 更多