【问题标题】:Custom sorting strings in c++C++中的自定义排序字符串
【发布时间】:2018-05-16 09:23:21
【问题描述】:

我在使用自定义排序字符串时遇到了一点问题。我基本上拥有的是std::pairstd::list,其元素是enum classstd::stringEnum 用于颜色(它们确实排序正确),字符串 包含 [2, 10] 区间内的数字,分别添加字母 J、Q、K 和 A。你可能已经猜到了,这是一副纸牌,排序后一定是这样的: 2、3、4、5、6、7、8、9、10、J、Q、K、A。

为了实现这一点,我的 lambda 函数应该是什么样的?还是有别的办法?

这是我到目前为止所遇到的:

enum class Colours
{
Spades, Clubs, Hearts, Diamonds
};

typedef std::list<std::pair<Colours, std::string>> Deck;
typedef std::pair<Boje, std::string> Pair;

这是实际的排序:

deck.sort( [] (const Pair &x, const Pair &y) -> bool {
        if(x.first == y.first) return x.second < y.second;
        else return x.first < y.first;
    } );

而当前排序结果:10 2 3 4 5 6 7 8 9 A J K Q

【问题讨论】:

  • 我刚刚注意到我忘了提供结果,它已被编辑。 10在2之前,字母是A J K Q,但必须是J Q K A。
  • 对卡片面也使用枚举是正常的,需要时带有转换函数来转换为字符串
  • 我建议以正确的顺序创建一个数组/列表,尝试获取两个元素的索引并比较索引
  • 问题可能是您正在比较字符而不是字符串
  • 只需使用 char 而不是 std::string 就可以了。

标签: c++ list sorting


【解决方案1】:

您要做的一件事是每个string 的第一个字符是唯一的。此代码未经测试,但应该很接近:

unsigned int face_value(std::string const &face) {
    if(std::isdigit(face[0])) {
        // if the length is 1 we just need the digit
        if(face.length() == 1) {
            return face[0] - '0';
        }
        // if it's not 1, it has to be 10
        return 10;
    }
    switch(face[0]) {
    case 'J':
        return 11;
        break;

    case 'Q':
        return 12;
        break;

    case 'K':
        return 13;
        break;

    case 'A':
        return 14;
        break;

    default:
        assert(false);
        break;
    }
}

bool face_compare(std::string const &first, std::string const &second) {
    return face_value(first) < face_value(second);
}

使用辅助函数,lambda 是微不足道的:

deck.sort( [] (const Pair &x, const Pair &y) -> bool {
    return std::tie(x.first, face_value(x.second)) <
           std::tie(y.first, face_value(y.second));
});

顺便说一句,我的猜测是你想在Colours 之前比较面值,但我现在坚持你原来的逻辑。

【讨论】:

  • 我将其表达为return std::tie(x.first, face_value(x.second)) &lt; std::tie(y.first, face_value(y.second))
  • @Caleth - 老实说不知道tuples 支持布尔运算符。感谢您的建议!
【解决方案2】:

您可以查找卡片的面值,以std::map&lt;std::string, int&gt; 表示。我们可以使用std::tuple 小于和std::tie,而不是显式指定两个值的小于。

// throws std::out_of_range if not passed a valid face
int face_value (const std::string & face)
{
    static const std::map<std::string, int> faces {
        { "2", 2 },  { "3", 3 },  { "4", 4 },
        { "5", 5 },  { "6", 6 },  { "7", 7 },
        { "8", 8 },  { "9", 9 },  { "10", 10 },
        { "J", 11 }, { "Q", 12 }, { "K", 13 },
        { "A", 14 },
    }

    return faces.at( face );
}

deck.sort( [] (const Pair &x, const Pair &y) -> bool {
    return std::tie(x.first, face_value(x.second)) < std::tie(y.first, face_value(y.second));
} );

【讨论】:

    【解决方案3】:

    问题是你在比较strings,也就是说,你按字母顺序比较它们。让我们看看哪些比较会产生不好的结果:

    2、3、4、5、6、7、8、9 与 10

    J 与 A

    Q 与 K

    K 与 A

    因此,您需要编写比较来检查任何值是否为 10、A 或 J。如果是 10,请检查另一个是否为数字。如果是A,检查另一个是J还是K。如果是K,检查另一个是Q还是A。如果满足任何一种情况,则返回与您的需求对应的逻辑值。否则像以前一样工作:

    deck.sort( [] (const Pair &x, const Pair &y) -> bool {
            if(x.first == y.first) {
                //Here you need to implement your custom cases as described above
                return x.second < y.second;
            }
            else return x.first < y.first;
        } );
    

    【讨论】:

    • 这就是我最终要做的事情。我确实必须写一大堆如果,但它仍然有效。谢谢!
    【解决方案4】:

    因为10 小于2,所以它没有按照您想要的方式排序,因为1 小于2。使用类和比较运算符可能会更好:

    struct Card {
        enum class Suite { Spades, Clubs, Hearts, Diamonds};
    
        operator<(const Card& other) {
            if (suite == other.suite) { return number < other.number; }
            return suite < other.suite;
        }
    
        string toString(); // Function to make your card string representation.
    
        private:
           int number; // 2 - 14 (11, 12, 13, 14: J, Q, K, A)
           Suite suite;
    }
    

    那么你可以这样做:

    std::list<Card> myList;
    myList.sort();
    

    注意:未经测试的代码,还需要适当的构造函数转换器等。

    【讨论】:

    • 那很好,但不幸的是,不允许定义运算符和使用类。
    【解决方案5】:

    花色和价值的枚举怎么样:

    enum class Suit {
        Spades, Clubs, Hearts, Diamonds
    };
    
    enum class Val {
        v2, v3, v4, v5, v6, v7, v8, v9, v10, vJ, vQ, vK, vA
    };
    
    typedef std::pair<Suit, Val> Pair;
    

    然后,如果你想要的话,一个打印Pair的方法:

    std::string GetCardName(Pair p)
    {
        std::string suit_name;
        switch (p.first)
        {
            case Suit::Spades : suit_name = "Spades"; break;
            /* ... */
            case Suit::Diamonds : suit_name = "Diamonds"; break;
        }
    
        std::string val_name;
        switch (p.second)
        {
            case Val::v2 : val_name = "2"; break;
            case Val::v3 : val_name = "3"; break;
            /* ... */
            case Val::vA : val_name = "Ace"; break;
        }
    
        return val_name + " of " + suit_name;
    }
    

    然后您可以完全按照您最初编写的方式进行比较,因为 Suit 和 Val 枚举是隐式排序的:

    std::list<Pair> Deck =
    {
        std::make_pair(Suit::Spades, Val::vA),
        std::make_pair(Suit::Clubs, Val::v7)
    };
    
    Deck.sort( [](const Pair& p1, const Pair& p2)
    {
        if(p1.first == p2.first) return p1.second < p2.second;
        else return p1.first < p2.first;
    } );
    
    for (const auto& pair : Deck)
    {
        std::cout << GetCardName(pair) << std::endl;
    }
    

    输出:

    Ace of Spades
    7 of Clubs
    

    这是整个事情的minimal working example

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-26
      • 1970-01-01
      • 2015-09-22
      • 1970-01-01
      • 1970-01-01
      • 2014-02-02
      • 2020-05-29
      相关资源
      最近更新 更多