Пользовательские сортировки строк в C ++ - PullRequest
0 голосов
/ 16 мая 2018

У меня небольшая проблема с пользовательскими строками сортировки. В основном у меня есть std::list из std::pair, элементы которого enum class и std::string. Enum - для цветов (которые сортируют правильно), а строки содержат числа с интервалом [2, 10], с добавлением букв J, Q, K и A соответственно. Как вы, возможно, уже догадались, это колода карт, которая после сортировки должна выглядеть так: 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A.

Как должна выглядеть моя лямбда-функция для достижения этой цели? Или есть другой способ?

Это то, с чем я до сих пор соревновался:

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

Ответы [ 5 ]

0 голосов
/ 16 мая 2018

Как насчет перечислений для костюма и значения:

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

Вот минимальный рабочий пример всего этого.

0 голосов
/ 16 мая 2018

Вы можете посмотреть номиналы ваших карт, выраженные как std::map<std::string, int>. Вместо того, чтобы явно указывать значение меньше двух, мы можем использовать 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));
} );
0 голосов
/ 16 мая 2018

Проблема в том, что вы сравниваете string с, то есть вы сравниваете их в алфавитном порядке.Давайте посмотрим, какие сравнения приведут к плохим результатам:

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. Если какой-либо из этих случаев соблюден, тогда верните логическое значение, которое соответствует вашим потребностям.В противном случае работайте так, как вы делали раньше:

0 голосов
/ 16 мая 2018

Одна вещь, которая у вас есть, состоит в том, что первый символ каждого 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);
}

С помощью вспомогательных функций лямбда тривиальна:

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

В качестве примечания, я предполагаю, что вы хотите сравнить номиналы до Colour с, но сейчас я придерживаюсь вашей исходной логики.

0 голосов
/ 16 мая 2018

Это не сортировка, как вы хотите, потому что 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();

Примечание : непроверенный код, также необходимо соответствующееконструкторы, преобразователи и т. д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...