2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




 
 Лямбда-функции С++
Сообщение12.11.2020, 11:20 
Здравствуйте!

Прошу помочь разобраться в применении лямбда-функций. В примере (ниже) нужно сделать подсчет CountAndAddNewDogs с помощью стандартного алгоритма count_if и использования лямбды.

Задача. В приют привозят собак разных пород. В словаре shelter по названию хранится количество особей каждого вида на текущий момент. Приют может принять определённое количество собак каждой породы. Оно указано в словаре max_amount. Напишите функцию, которая будет на основании вектора с именами поступивших собак обновлять словарь shelter и возвращать общее количество попавших в приют собак. Гарантируется, что никакие другие собаки, кроме известных из словаря max_amount, в приют не приедут.

Обычным способом я это сделал (закомментированный участок кода). Не пойму, как это сделать с лямбдой через count_if. Как в лямбде обновить словарь shelter по условию shelter.find(dog)->second < max_amount.find(dog)->second. Понимаю, что задача должна с помощью лямбда должна решаться в пару строчек кода, взамен обычному способу.

код: [ скачать ] [ спрятать ]
Используется синтаксис C
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <vector>

using namespace std;

int CountAndAddNewDogs(const vector<string>& new_dogs,
    const map<string, int>& max_amount, map<string, int>& shelter) {
   
    //обычным способом
    /*int count = 0;
      for (auto& dog : new_dogs) {
        int max_dogs = max_amount.find(dog)->second;
        int now_dogs = shelter.find(dog)->second;
        if (now_dogs < max_dogs) {
            shelter[dog] = now_dogs + 1;
            count++;
        }
    }*/


    //через лямбду
    int count = count_if(new_dogs.begin(), new_dogs.end(),
        [&shelter, &max_amount](const string& dog) {
            return shelter.find(dog)->second < max_amount.find(dog)->second;
        });
    return count;
}

int main() {
    map<string, int> shelter = { {"landseer"s, 1}, {"otterhound"s, 2}, {"pekingese"s, 2}, {"pointer"s, 3} };
    const map<string, int>& max_amount = { {"landseer"s, 2}, {"otterhound"s, 3}, {"pekingese"s, 4}, {"pointer"s, 7} };
    const vector<string>& new_dogs = { "landseer"s, "otterhound"s, "otterhound"s, "otterhound"s, "pointer"s };
    int taken_dogs = CountAndAddNewDogs(new_dogs, max_amount, shelter);
    cout << taken_dogs << endl;
    return 0;
}


-- 12.11.2020, 12:07 --

UPD. Вроде бы нашел решение. Считает правильно. Однако система проверки его не принимает. Пишет:

Возможные проблемы:
- Вы неправильно обрабатываете пример из условия
- Вы неправильно обрабатываете случай, когда ни одна собака не помещается


код: [ скачать ] [ спрятать ]
Используется синтаксис C
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <vector>

using namespace std;

int CountAndAddNewDogs(const vector<string>& new_dogs,
    const map<string, int>& max_amount, map<string, int>& shelter) {
   
    //обычным способом
    /*int count = 0;
      for (auto& dog : new_dogs) {
        int max_dogs = max_amount.find(dog)->second;
        int now_dogs = shelter.find(dog)->second;
        if (now_dogs < max_dogs) {
            shelter[dog] = now_dogs + 1;
            count++;
        }
    }*/


    //через лямбду
    int count = count_if(new_dogs.begin(), new_dogs.end(),
        [&shelter, &max_amount](const string& dog) {
            return shelter.find(dog)->second < max_amount.find(dog)->second ? shelter[dog] = shelter.find(dog)->second + 1 : 0;//сделал таким образом
        });
    return count;
}

int main() {
    map<string, int> shelter = { {"landseer"s, 1}, {"otterhound"s, 2}, {"pekingese"s, 2}, {"pointer"s, 3} };
    const map<string, int>& max_amount = { {"landseer"s, 2}, {"otterhound"s, 3}, {"pekingese"s, 4}, {"pointer"s, 7} };
    const vector<string>& new_dogs = { "landseer"s, "otterhound"s, "otterhound"s, "otterhound"s, "pointer"s };
    int taken_dogs = CountAndAddNewDogs(new_dogs, max_amount, shelter);
    cout << taken_dogs << endl;
    return 0;
}

 
 
 
 Re: Лямбда-функции С++
Сообщение12.11.2020, 13:11 
Однако верное решение такое.

Используется синтаксис C
int CountAndAddNewDogs(const vector<string>& new_dogs,
    const map<string, int>& max_amount, map<string, int>& shelter) {
    //...
    int count = count_if(new_dogs.begin(), new_dogs.end(),
        [&shelter, &max_amount](const string& dog) {
            return shelter[dog] < max_amount.at(dog) ? shelter[dog] = shelter[dog] + 1 : 0;
        });
    return count;
}
 

 
 
 
 Re: Лямбда-функции С++
Сообщение12.11.2020, 15:42 
Побочные эффекты в тернарном операторе - плохая привычка.
Смешаны обращения через .at и через [].
Увеличение переменной на единицу сделано не в стиле C++.
Само задание ужасно - дизайн стандартной библиотеки C++ предполагает что count_if не будет обладать побочными эффектами, для этого есть другие алгоритмы.

 
 
 
 Re: Лямбда-функции С++
Сообщение12.11.2020, 17:28 
slavav

Это поправимо:
Используется синтаксис C
int CountAndAddNewDogs(const vector<string>& new_dogs,
    const map<string, int>& max_amount, map<string, int>& shelter) {
    //...
    int count = count_if(new_dogs.begin(), new_dogs.end(),
        [&shelter, &max_amount](const string& dog) {
            return shelter[dog] < max_amount.at(dog) ? shelter[dog] += 1 : 0;
        });
    return count;
}


Или без тернарного оператора:
Используется синтаксис C
int CountAndAddNewDogs(const vector<string>& new_dogs,
    const map<string, int>& max_amount, map<string, int>& shelter) {

    int count = count_if(new_dogs.begin(), new_dogs.end(),
        [&shelter, &max_amount](const string& dog) {
            int& current_amount = shelter[dog];
            if (current_amount < max_amount.at(dog)) {
                return ++current_amount;
            } else return 0;
        });

    return count;
}
 

Может предложите Ваш, более оптимальный способ.

slavav в сообщении #1491877 писал(а):
Само задание ужасно - дизайн стандартной библиотеки C++ предполагает что count_if не будет обладать побочными эффектами, для этого есть другие алгоритмы.
Объясните это подробнее, пожалуйста.

 
 
 
 Re: Лямбда-функции С++
Сообщение12.11.2020, 17:47 
Я не про оптимальность, а про правильность/идиоматичность. Ваш последний вариант стал лучше. Осталось одно замечание - не нужно из булевой функции возвращать целые значения. С точки зрения языка вы всё делаете правильно, а с точки зрения программиста return true;/return false; будет лучше для понимания.

count_if возвращает значение. Для программиста будет сюрпризом/необычным поведением, что внутри еще какие-то контейнеры обновляются. Мои правила большого пальца такие:
в функции, которая возвращает значение (count_if) не делать других модификаций данных;
в предикате (лямбда) никогда ничего не делать кроме вычисления его значения.

Оба правила можно нарушать, если есть веские основания. В данном случае их нет, я бы написал обычный цикл for (const auto& dog : new_dogs) {...}. Но у вас другое задание.

 
 
 [ Сообщений: 5 ] 


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group