LINUX.ORG.RU

Найти «максимальный» элемент в контейнере по имени поля

 , ,


1

2

Рабочий пример на расте:

struct Data {
    number: usize,
    letter: char,
}

impl Data {
    fn new(number: usize, letter: char) -> Self {
        Data { number, letter }
    }
}

fn main() {
    let numbers = vec![
        Data::new(1, 'a'),
        Data::new(2, 'b'),
        Data::new(3, 'c'),
    ];
    println!("{:?}", numbers.iter().max_by_key(|v| v.number).map(|v| v.letter)); // Some('c')
}

Как это повторить в C++14?

PS: с tuple пример был бы проще, но для C++ это слишком сложно.

Что значит «по имени поля»? В любом контейнере или в конкретном? std::map::rbegin()?

intelfx ★★★★★ ()
Ответ на: комментарий от Crocodoom

Я в курсе. Только мне не нужна функция, мне нужно по полю. Без лишнего мусора.

RazrFalcon ★★★★★ ()

Я бы что-то такое написал.

#include <vector>
#include <iostream>
#include <numeric>

struct Data {
	size_t number;
	char letter;
};

int main()
{
	std::vector<Data> numbers = { Data{1, 'a'},Data{2, 'b'}, Data{3, 'c'} };
	const auto res = std::accumulate(numbers.cbegin(), numbers.cend(), numbers[0], [](Data max, Data current) {
		if (max.number > current.number) {
			return max;
		}
		return current;
	}).letter;
	std::cout << res << std::endl;
}


Хотя наверное и с max_element можно сделать...

fsb4000 ★★★★ ()
Ответ на: комментарий от RazrFalcon

Это не мусор, это более общая конструкция, твой частный к нему сводится.

Crocodoom ★★★ ()
Ответ на: комментарий от RazrFalcon

Пример для кого.

«Звонок — для учителя!»

#include <algorithm>
#include <vector>
#include <iostream>

struct Data {
    size_t number;
    char letter;
};

int main() {
  std::vector<Data> numbers{
      {1, 'a'}, {2, 'b'}, {4, 'd'}, {3, 'c'},
  };

  std::cout << std::max_element(numbers.begin(), numbers.end(),
                                [](Data const &a, Data const &b) {
                                  return a.number < b.number;
                                })
                   ->letter
            << "\n";
}
i-rinat ★★★★★ ()
Ответ на: комментарий от Crocodoom

Я в курсе. В расте для этого есть отдельный метод.

Мне же нужно по полю.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от fsb4000

А зачем нам accumulate? Сравнение в лямбде я могу и в max_element сделать.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от i-rinat

Показываю суть:

max_by_key(|v| v.number)

// vs

max_by(|a, b| a.number.cmp(b.number))

Мне нужен первый вариант. max_element с предикатом даёт второй.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon
#include <iostream>
#include <algorithm>
#include <vector>

struct Data {
    int number;
    char letter;
};

int main(int argc, char *argv[])
{
    std::vector<Data> a = { Data { 1, 'a' }, Data { 2, 'b' }, Data { 3, 'c' }};
    // Подразумевается что контейнер не пуст.
    char letter = std::max_element(std::begin(a), std::end(a), [](Data &l, Data &r) { return l.number < r.number; })->letter;
    std::cout << letter << std::endl;
    return 0;
}

По ссылке же всё описано.

Deleted ()
Ответ на: комментарий от RazrFalcon

а результаты у них разные или одинаковые ? я как трушный сппшник раста не знаю, поэтому мне не понятно в чем разница

anonymous ()
Ответ на: комментарий от anonymous

а что это ОР ? чет не понимаю

Original Post, исходное сообщение треда, корень всего дерева обсуждения.

Virtuos86 ★★★★★ ()
Ответ на: комментарий от anonymous

Результат одинаковый. Просто если поле и так реализует оператор сравнения, то нет смысла писать лишний код.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Т.е. тебе нужны конкретно те 2 функции? Их нет. Но можешь поискать в boost или написать сам (Вроде бы уже).

Deleted ()
#include <iostream>
#include <algorithm>
#include <vector>

struct Datum {
    int n;
    char c;
};

template <class F, class G> F max_field(F first, F last, G getter)
{
    return std::max_element(first, last, [&getter](auto &a, auto &b) { return getter(a) < getter(b); });
}

int main(void)
{
    std::vector <Datum> data = { Datum{1, 'a'}
                               , Datum{2, 'b'}
                               , Datum{3, 'c'}
                               };
    char letter = max_field(std::begin(data), std::end(data), [](Datum &d) { return d.n; })->c;
    std::cout << letter << std::endl;
}

Держи мой костыльный вариант. Нашкрябал щаз за 5 минут после пробежки, но вроде работает. На C++ тыщу лет не писал. Вместо нетипизированного G лучше бы обернуть это в std::function, но с этим ты сам справишься.

hateyoufeel ★★★★★ ()
Последнее исправление: hateyoufeel (всего исправлений: 3)
Ответ на: комментарий от hateyoufeel

он не хочет рабочий функционал, он хочет писать в С++ кодом и идиомами раста

anonymous ()
Ответ на: комментарий от anonymous

Да нет, эта функция была бы и в C++ полезна. Никакого rocket science я тут не вижу.

hateyoufeel ★★★★★ ()
Ответ на: комментарий от hateyoufeel

я уже писал буст мульти индекс это делает, в прочем ничто не мешает написать шаблонный предикат который по поинтеру на структуру сравнение выводит

anonymous ()
#include <iostream>
#include <string>
#include <vector>

template<class InputIt, class UnaryOperation>
InputIt max_by_key(InputIt first, InputIt last, UnaryOperation op)
{
    InputIt result = first;
    
    for (; first != last; ++first) {
        result = op(std::move(*first)) > op(std::move(*result))? first : result;
    }
    
    return result;
}

struct Data {
    size_t number;
    char letter;
};

int main()
{
  std::vector<Data> numbers{
      {1, 'a'}, {2, 'b'}, {4, 'd'}, {3, 'c'},
  };

  std::cout << max_by_key(std::begin(numbers), std::end(numbers), [](const Data &a)  {return a.number; })->letter
            << "\n";
}
anonymous ()

Господа вроде Sean Parent (C++ Seasoning, 38-й слайд) рекомендуют для подобного использовать сторонние библиотеки для работы с ranges.

Softwayer ★★ ()
Ответ на: комментарий от anonymous
template<class KeyExtractor1,class KeyExtractor2>
struct key_from_key
{
public:
  typedef typename KeyExtractor1::result_type result_type;

  key_from_key(
    const KeyExtractor1& key1_=KeyExtractor1(),
    const KeyExtractor2& key2_=KeyExtractor2()):
    key1(key1_),key2(key2_)
  {}

  template<typename Arg>
  result_type operator()(Arg& arg)const
  {
    return key1(key2(arg));
  }

private:
  KeyExtractor1 key1;
  KeyExtractor2 key2;
};

Если там вот такие вещи надо вместо лямбды присовывать, то катился бы этот multi-index куда подальше.

hateyoufeel ★★★★★ ()
Ответ на: комментарий от anonymous

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

hateyoufeel ★★★★★ ()

Так а русским языком если сформулировать, то должно быть что?

Найти в последовательности максимальный элемент по полю number, а затем, если таковой найден, то значение поля letter от этого максимального по number элемента?

eao197 ★★★★★ ()
Ответ на: комментарий от hateyoufeel

Ох. Я пытался в параметры шаблона запихнуть возвращаемый геттером тип. И вроде получалось, но компилятор не мог свести лямбду к указателю функции при инстанциировании шаблона. С auto в параметрах компаратора это и не нужно, получается.

i-rinat ★★★★★ ()
Ответ на: комментарий от i-rinat

Ох. Я пытался в параметры шаблона запихнуть возвращаемый геттером тип. И вроде получалось, но компилятор не мог свести лямбду к указателю функции при инстанциировании шаблона. С auto в параметрах компаратора это и не нужно, получается.

Покажи код. Я думаю, это можно через std::function сделать. Но мне сейчас откровенно лень ковыряться.

hateyoufeel ★★★★★ ()
Ответ на: комментарий от eao197

он хочет в предикат алгоритма запихивать имя поля

anonymous ()
Ответ на: комментарий от hateyoufeel

Я думаю, это можно через std::function сделать.

С std::function делать нет смысла. Разве что если C++14 нет.

Покажи код.

#include <algorithm>
#include <iostream>
#include <vector>

struct Data {
  size_t number;
  char letter;
};

template <class ForwardIterator, class DataType, typename KeyType>
ForwardIterator max_by_key(ForwardIterator first, ForwardIterator last,
                           KeyType (*get_key)(DataType &)) {
  auto cmp = [get_key](DataType const &a, DataType const &b) {
    return get_key(a) < get_key(b);
  };
  return std::max_element(first, last, cmp);
}

int main() {
  std::vector<Data> numbers{
      {1, 'a'}, {2, 'b'}, {4, 'd'}, {3, 'c'},
  };

#if 1
  // Работает.
  size_t (*getter)(Data const &a) = [](Data const &a) { return a.number; };
  std::cout << max_by_key(numbers.begin(), numbers.end(), getter)->letter
            << "\n";
#else
  // Не работает.
  std::cout << max_by_key(numbers.begin(), numbers.end(),
                          [](Data const &a) { return a.number; })
                   ->letter
            << "\n";
#endif
}

i-rinat ★★★★★ ()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Почему? Как насчёт чтобы указать, что в качестве последнего параметра нужна функция, принимающая тип из итератора и возвращающая что-то, что можно сравнивать? Добавить сюда концепты, и получается весьма неплохо в плане типобезопасности.

hateyoufeel ★★★★★ ()
Ответ на: комментарий от eao197

Достаточно первой части.

Найти в последовательности максимальный элемент по полю number

без лямды со сравнением. Только поле указываем.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon
struct data {
    int i;
    char c;
};

template <typename Field, typename ItrType>
ItrType max_el(ItrType begin, ItrType end, Field fld)
{
    return std::max_element(begin, end, [fld](auto lh, auto rh) {
        return lh.*fld < rh.*fld;
    });
}

int main()
{
    std::vector<data> d = {
        {3, 'a'},
        {2, 'b'},
        {1, 'c'},
    };
    auto mmm = max_el(d.cbegin(), d.cend(), &data::i);
    auto nnn = max_el(d.cbegin(), d.cend(), &data::c);

    std::cout << mmm->i << " " << mmm->c << "\n";
    std::cout << nnn->i << " " << nnn->c << "\n";

    return 0;
}
3 a
1 c

типа этого чтоль?

anonymous ()
Ответ на: комментарий от anonymous
[fld](auto lh, auto rh)

тут только константы ссылки...нутыпонил

anonymous ()
Ответ на: комментарий от RazrFalcon

Вам нужен будет специальный компаратор для max_element. Что-то вроде (https://wandbox.org/permlink/JkFE4XwRkrP3Dg4v):

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

template<typename Getter>
auto compare_by(Getter && getter) {
  return [getter](auto && a, auto && b) { return getter(a) < getter(b); };
}

struct Data {
    size_t number;
    char letter;
};

int main()
{
  std::vector<Data> numbers{
      {1, 'a'}, {2, 'b'}, {4, 'd'}, {3, 'c'},
  };

  std::cout << max_element(begin(numbers), end(numbers), compare_by([](auto && a)  {return a.number; }))->letter
            << "\n";
}

eao197 ★★★★★ ()
Ответ на: комментарий от hateyoufeel

Почему?

Почему не std::function? Потому что этот вызов будет выполняться много раз, и всё лишнее там будет копиться. Если типобезопасности важнее скорости, стоит взять какой-нибудь другой язык. C++ — это про выстрелы в ногу, но быстрые.

i-rinat ★★★★★ ()
#include <vector>
#include <iostream>
#include <numeric>
#include <algorithm>

struct Data {
	size_t number;
	char letter;
	bool operator<(const Data& a) const { return this->number < a.number;}
};

int main()
{
	std::vector<Data> numbers = { Data{1, 'a'},Data{3, 'b'}, Data{2, 'c'} };
	const auto res = std::max_element(numbers.cbegin(), numbers.cend())->letter;
	std::cout << res << std::endl;
}
anonymous ()
Ответ на: комментарий от eao197

Интересно. Аноним выше подобное предлагал.

А как у него с производительностью? Если тип сложнее числа - не будет тормозов/лишних копирований?

RazrFalcon ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.