基于长度</string>对集合<string>进行排序

时间:2010-10-02 08:10:23

标签: c++ stl lambda c++11 set

我的问题与this有关。

我希望借助lambda表达式作为谓词,对sort()执行set操作。

我的代码是

#include <set>
#include <string>
#include <iostream>
#include <algorithm>
int main() {
  using namespace std;
  string s = "abc";
  set<string> results;
  do {
    for (int n = 1; n <= s.size(); ++n) {
      results.insert(s.substr(0, n));
    }
  } while (next_permutation(s.begin(), s.end()));

  sort (results.begin(),results.end());[](string a, string b)->bool{

              size_t alength = a.length();
              size_t blength = b.length();
              return (alength < blength);
  });
  for (set<string>::const_iterator x = results.begin(); x != results.end(); ++x) {
    cout << *x << '\n';
  }
  return 0;
}

但错误的数量和类型非常复杂,我无法理解如何解决它们。有人能告诉我这段代码有什么不对。

7 个答案:

答案 0 :(得分:8)

编辑请注意,Steve Townsend's solution实际上是您正在搜索的那个,因为他将其编写为C ++ 0x Lambda,我将其编写为C ++ 03代码如下。

另一种解决方案是自定义std::set排序功能:

std::set已订购......

std::set有自己的排序,一旦构造,你就不应该改变它。那么,以下代码:

int main(int argc, char* argv[])
{
    std::set<std::string> aSet ;

    aSet.insert("aaaaa") ;
    aSet.insert("bbbbb") ;
    aSet.insert("ccccccc") ;
    aSet.insert("ddddddd") ;
    aSet.insert("e") ;
    aSet.insert("f") ;

    outputSet(aSet) ;

    return 0 ;
}

将输出以下结果:

 - aaaaa
 - bbbbb
 - ccccccc
 - ddddddd
 - e
 - f

...但您可以自定义其订购功能

现在,如果需要,您可以使用自己的比较功能自定义您的设置:

struct MyStringLengthCompare
{
    bool operator () (const std::string & p_lhs, const std::string & p_rhs)
    {
        const size_t lhsLength = p_lhs.length() ;
        const size_t rhsLength = p_rhs.length() ;

        if(lhsLength == rhsLength)
        {
            return (p_lhs < p_rhs) ; // when two strings have the same
                                     // length, defaults to the normal
                                     // string comparison
        }

        return (lhsLength < rhsLength) ; // compares with the length
    }
} ;

在这个比较函子中,我确实处理了“相同长度但不同内容意味着不同字符串”的情况,因为我相信(可能是错误地)原始程序中的行为是错误的。要在原始程序中编码行为,请从代码中删除if块。

现在,您构建集合:

int main(int argc, char* argv[])
{
    std::set<std::string, MyStringLengthCompare> aSet ;

    aSet.insert("aaaaa") ;
    aSet.insert("bbbbb") ;
    aSet.insert("ccccccc") ;
    aSet.insert("ddddddd") ;
    aSet.insert("e") ;
    aSet.insert("f") ;

    outputSet(aSet) ;

    return 0 ;
}

该集合现在将使用仿函数MyStringLengthCompare来订购其项目,因此,此代码将输出:

 - e
 - f
 - aaaaa
 - bbbbb
 - ccccccc
 - ddddddd

但要注意订购错误!

创建自己的订购功能时,必须遵循以下规则:

  

如果(lhs&lt; rhs)为真,则返回true,否则返回false

如果由于某种原因你的订购功能不尊重它,那么你的手上就会有一套破损。

答案 1 :(得分:5)

std::sort重新排列您给出的序列元素。 set中序列的排列是固定的,因此您可以拥有的唯一迭代器是const迭代器。

您需要先将results复制到vectordeque(或类似)。

vector sortable_results( results.begin(), results.end() );

答案 2 :(得分:3)

您可以通过提供自定义谓词来自定义set中元素的顺序,以确定相对于现存成员的添加元素的顺序。 set定义为

template <
    class Key, 
    class Traits=less<Key>, 
    class Allocator=allocator<Key> 
>
class set

Traits是

  

提供功能的类型   可以比较两个元素的对象   值作为排序键来确定它们   集合中的相对顺序。这个   参数是可选的,二进制   谓词少是默认值   值。

how to use lambda expression as a template parameter here上有背景信息。

在您的情况下,这转换为:

auto comp = [](const string& a, const string& b) -> bool 
    { return a.length() < b.length(); };
auto results = std::set <string, decltype(comp)> (comp);

请注意,这将导致具有相同字符串长度的set元素被视为重复项,这不是您想要的,只要我能理解所需的结果。

答案 3 :(得分:2)

sort需要set未提供的随机访问迭代器(它是双向迭代器)。如果您更改代码以使用vector,则编译正常。

答案 4 :(得分:1)

您无法对一组进行排序。它总是在键上排序(它们本身就是元素)。

更具体地说,std::sort需要随机访问迭代器。 std::set提供的迭代器不是随机的。

答案 5 :(得分:1)

由于我编写了您正在使用的原始代码,或许我可以扩展它...:)

struct cmp_by_length {
  template<class T>
  bool operator()(T const &a, T const &b) {
    return a.length() < b.length() or (a.length() == b.length() and a < b);
  }
};

首先按长度比较,然后按值进行比较。修改集定义:

set<string, cmp_by_length> results;

你很高兴:

int main() {
  using namespace std;
  string s = "abc";
  typedef set<string, cmp_by_length> Results;  // convenience for below
  Results results;
  do {
    for (int n = 1; n <= s.size(); ++n) {
      results.insert(s.substr(0, n));
    }
  } while (next_permutation(s.begin(), s.end()));

  // would need to add cmp_by_length below, if I hadn't changed to the typedef
  // i.e. set<string, cmp_by_length>::const_iterator
  // but, once you start using nested types on a template, a typedef is smart
  for (Results::const_iterator x = results.begin(); x != results.end(); ++x) {
    cout << *x << '\n';
  }

  // of course, I'd rather write... ;)
  //for (auto const &x : results) {
  //  cout << x << '\n';
  //}

  return 0;
}

答案 6 :(得分:0)

std :: set对于维护已排序和变异列表最有用。当设置本身一旦建成后它就不会改变太多,使用矢量会更快更小。

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
int main() {
  using namespace std;
  string s = "abc";
  vector<string> results;
  do {
    for (size_t n = 1; n <= s.size(); ++n) {
      results.push_back(s.substr(0, n));
    }
  } while (next_permutation(s.begin(), s.end()));

  //make it unique
  sort( results.begin(), results.end() );
  auto end_sorted = unique( results.begin(), results.end() );
  results.erase( end_sorted, results.end() );

  //sort by length
  sort (results.begin(),results.end());
          [](string lhs, string rhs)->bool
             { return lhs.length() < rhs.length(); } );

  for ( const auto& result: results ) {
    cout << result << '\n';
  }
}

我使用了经典的排序/唯一/擦除组合来使结果集唯一。我还清理了你的代码,使其更加c ++ 0x-y。