2014 dxdy logo

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

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




Начать новую тему Ответить на тему
 
 Лямбда-функции С++
Сообщение12.11.2020, 11:20 


11/12/16
403
сБп
Здравствуйте!

Прошу помочь разобраться в применении лямбда-функций. В примере (ниже) нужно сделать подсчет 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 


11/12/16
403
сБп
Однако верное решение такое.

Используется синтаксис 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 
Заслуженный участник


26/05/14
981
Побочные эффекты в тернарном операторе - плохая привычка.
Смешаны обращения через .at и через [].
Увеличение переменной на единицу сделано не в стиле C++.
Само задание ужасно - дизайн стандартной библиотеки C++ предполагает что count_if не будет обладать побочными эффектами, для этого есть другие алгоритмы.

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


11/12/16
403
сБп
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 
Заслуженный участник


26/05/14
981
Я не про оптимальность, а про правильность/идиоматичность. Ваш последний вариант стал лучше. Осталось одно замечание - не нужно из булевой функции возвращать целые значения. С точки зрения языка вы всё делаете правильно, а с точки зрения программиста return true;/return false; будет лучше для понимания.

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

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

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 5 ] 

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group