LINUX.ORG.RU

[stl]Можно ли как-то иначе

 


0

0

В проекте активно используются stl sort & find списков структур по различным критериям; критериев много, структуры сложные, из чего вытекло наличие кучи-малы маленьких локальных функций сравнения. Имхо, это очень некрасиво, да и, похоже, становится плохо поддерживаемым.

Так вот, вопрос: есть ли возможность описывать все эти предикаты in-placе? Может, в бусте есть что-то позволяющее сваять что-то лаконичное, похожее на лямбды или блоки кода(ruby)?


В GCC 4.5 будет поддержка лямбд из C++0x

Begemoth ★★★★★
()

>вытекло наличие кучи-малы маленьких локальных функций сравнения

Меня мама в детстве учила, что можно определять оператор «<» для каждого типа структур.

Так вот, вопрос: есть ли возможность описывать все эти предикаты in-placе?

А еще мама учила меня, что копипастить в коде нехорошо.

А папа рассказал мне про модульность. Модульность — это когда похожий функционал собирается в одном месте и опять-таки не копипастится.

Я думаю, что мои родители — мудрые люди, и тебе следует послушать их, а не городить лямбды копипастом.

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

>Меня мама в детстве учила, что можно определять оператор «<» для каждого типа структур.

по различным критериям;

Жаль, что мама не научила тебя читать, а только писать.

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

>>вытекло наличие кучи-малы маленьких локальных функций сравнения

Меня мама в детстве учила, что можно определять оператор «<» для каждого типа структур.

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

Я думаю, что мои родители — мудрые люди, и тебе следует послушать их, а не городить лямбды копипастом.

Твои родители лохи, зря ты в них пошел :(

SV0L0CH
()

«родные» лямбды будут в gcc 4.5 и MSVC 2010

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

>Жаль, что мама не научила тебя читать, а только писать.

Жаль, что тебя родители не пороли за Ctrl-C, Ctrl-V. Глядишь, мог бы человек получиться.

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

Емко, но практически бесполезно...

Продемонстрирую. Вот модельная задача из проекта, размер входа уменьшен раз в десять в сравнении с практическим:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <stdio.h>
#include <time.h>
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
using namespace boost::lambda;

struct A {
	int x;
	char y[64];
};

vector<struct A> a;
vector<struct A> b;

void fill(vector<struct A> &v) {
	struct A *s;

	for(int i=0; i<50000; i++) {
		s = new struct A;
		s->x = i;
		sprintf(s->y, "test-%d", i%10);
		v.push_back(*s);
	}
}

inline bool sort_p(struct A x, struct A y) {
	return (x.x < y.x && strcmp(x.y, y.y) == 0) || (x.x >= y.x && strcmp(x.y, y.y) != 0);
}

int main() {
	fill(a);
	cout << "begin test 1: " << time(0) << endl;
	sort<vector<struct A>::iterator>(a.begin(), a.end(), sort_p);
	cout << "end test 1: " << time(0) << endl;

	fill(b);
	int (*pStrCmp)(const char*, const char*) = &strcmp;
	cout << "begin test 2: " << time(0) << endl;
	std::sort<vector<A>::iterator>(b.begin(), b.end(), (&_1->*&A::x < &_2->*&A::x && bind(pStrCmp, &_1->*&A::y, &_2->*&A::y) == 0) || (&_1->*&A::x >= &_2->*&A::x && bind(pStrCmp, &_1->*&A::y, &_2->*&A::y) != 0) );
	cout << "end test 2: " << time(0) << endl;

	return 0;
}

Результат примерно такой:
begin test 1: 1264960117
end test 1: 1264960119
begin test 2: 1264960119
end test 2: 1264960133
Во-первых, падение производительности в 5+ раз. Во-вторых, читабельность ушла в говно.

:(

impfp
() автор топика
Ответ на: комментарий от linuxfan

Уймись уже, а?

var sortedByName = list.sort(function(x1, x2) x1.name < x2.name)
var sortedByAge  = list.sort(function(x1, x2) x1.age < x2.age)

Где здесь копипаст? Как ты это сделаешь одним оператором?

P.S. Синтаксис javacript 1.8

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

Результаты такие с объемом данным увеличенным в 100 раз.

begin test 1: 1264961934
end test 1: 1264961938
begin test 2: 1264961939
end test 2: 1264961942
Begemoth ★★★★★
()
Ответ на: Емко, но практически бесполезно... от impfp

На самом деле надо было определять класс с единственным полем типа указателя на функцию сравнения и с оператором скобки. Тогда сравнение можно было бы определять в любом месте и передавать _единственной_ функции сортировки по значению.

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

>Где здесь копипаст?

Если ты повторишь «function(x1, x2) x1.age < x2.age» более одного раза — это копипаст и он должен быть вынесен в отдельную функцию с вразумительным названием и пояснением. Хотя учить жабаскриптовебмастеров практически бесполезно. Лучшее, чего им можно, так это чтоб они не писали на C/C++ кривыми ручонками, а оставили это дело Профессионалам.

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

Да, с оптимизациями пошустрее стало :)

impfp
() автор топика
Ответ на: комментарий от linuxfan

>>Где здесь копипаст?

Если ты повторишь «function(x1, x2) x1.age < x2.age» более одного раза — это копипаст и он должен быть вынесен в отдельную функцию с вразумительным названием и пояснением.

Ну и покажи нам как это выносится.

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

>Если ты повторишь

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

Я жду решения на операторе сравнения, про которое ты тут кричал.

Лучшее, чего им можно, так это чтоб они не писали на C/C++

Спасибо, я только об этом и мечтаю, мне совсем не в кайф писать на с++, на с - еще куда ни шло.

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

Лучшее, чего им можно, так это чтоб они не писали на C/C++ кривыми ручонками, а оставили это дело Профессионалам.

Фраза достойная Г. Шилдта. Ну расскажи мне как в приведённом случае введение новой функции (кстати, как-бы ты её назвал? smthLessByAge?) улучшит читаемость. Абстракции эта функция не добавит, зачем она нужна?

Begemoth ★★★★★
()
Ответ на: Емко, но практически бесполезно... от impfp

Во-первых, падение производительности в 5+ раз.

Про -O2 уже сказали.

Во-вторых, читабельность ушла в говно.

А что ты хотел? Даже в языках, где лямбды встроены, они предназначены для мелких функций. Если в них пихать что-то более-менее сложное и длинное, то нечитаемое говно гарантировано.

P.S. Не думал вместо внешней функции определить оператор сравнения у структуры?

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

«Если» - это было бы замечательно...

Вот тут есть много копипасты, вопрос - стоит ли от нее избавляться? :)

_dt = [
	(1,"Moscow", 10000000), (2,"Moscow", 3000000),
	(3,"New York", 18000000), (4,"New York", 3000000),
	(5,"Paris", 7000000), (6,"Roma",2000000),
	(7,"Antarctica", 0), (8, "Minsk", 2000000)]

main = let
		_s (a, b, c) = b
		_t (a, b, c) = c
	in
	map
		(\x->(x, foldr
				(\y z -> if x == (_s y) then (_t y)+z else z)
				0
				_dt
				)
		)
		(foldr
			(\x y -> if length(filter (\a->a==x) y) > 0 then y else x:y)
			[]
			(map (\a->_s a) _dt)
		)

impfp
() автор топика
Ответ на: комментарий от SV0L0CH

>А где он сейчас используется и как называется стандарт?

очепятался, javascript, конечно же. А javascript у нас вообще один, у мозиллы, кодовое имя спайдерманки, все остальные по-другому называются (JScript, EJScript, V8 и т.д), покойной сан подарил торговую марку мозилле.

В стандарте синтаксиса нет, гады из МС, яху и эппл загубили ES4 и ES5, но в javascript - есть, и возможно будет в ES6.

//Топикстартер, извини за оффтоп.

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

>Ну расскажи мне как в приведённом случае введение новой функции (кстати, как-бы ты её назвал? smthLessByAge?) улучшит читаемость.

Жадные дети, кто вас пустил в программирование? $type_order_by_$category.

Абстракции эта функция не добавит

Если ты дрочишь на умные слова, можешь успокоиться с того, что это повысит градус инксапсуляции.

зачем она нужна?

Если сортировака по заданному критерию встречается более чем в одном месте кода, при каких-либо изменениях структуры править надо будет только в одном месте. Не расстраивайся: попадешь пару раз в большие проекты, написанные лямбда-умельцами, сразу поймешь.

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

Ну... В C# 2.0 с анонимными делегатами и то красивее вышоло бы :(

определить оператор сравнения у структуры?

Мысль интересная, но там такое дело, что список структур надо переупорядочивать в зависимости от подзадачи, и сортировки/группировки пляшут именно от этого факта... (Задача собсно - построение нескольких взаимосвязанных иерархий из содержимого PDF-документа)

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

>вопрос - стоит ли от нее избавляться?

От копипасты на хацкиле следует избаляться в любом случае, тем более от такой низкокачественной. Это какой-то write-only peace of perl, только регэкспов для полного счастья не хватает.

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

>Что-то не совсем уловил мысль, можно чуть поподробнее?

typedef bool cmpfn(struct A,struct A); struct CMP {cmpfn *fn;bool operator(struct A a,struct A b)(){return fn(a,b);}};

//Приблезительно так, можно и откорректировать.

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

Помоги провести правильную декомпозицию и унифицировать:

bool a(A x, A y) {return x.f1 < y.f1 && x.f2 > y.f2;} bool b(B x, B y) {return x.h1 == y.h1;}

? Буду только благодарен совету от гуру.

impfp
() автор топика
Ответ на: комментарий от linuxfan

Если ты дрочишь на умные слова, можешь успокоиться с того, что это повысит градус инксапсуляции.

Фразу придумал, подрочив на свой Профессионализм?

Если сортировака по заданному критерию встречается более чем в одном месте кода, при каких-либо изменениях структуры править надо будет только в одном месте.

Чистейшее 4.2. Потому, что поля структуры используютс не только для сортировки значений, так что возможность изменения структуры без изменения интерфйеса надо закладывать заранее. Дрочи дальше на свой Профессионализм.

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

>typedef bool cmpfn(struct A,struct A); struct CMP {cmpfn *fn;bool operator(struct A a,struct A b)(){return fn(a,b);}};

OMG. И после этого я еще удивляюсь, почему с каждым годом софт все тормознее и тормознее.

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

>очепятался

да я не об этом... ладно, птом погуглю на тему 1.8, может что и найду.

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

Я тут не вижу никакой композиции.

Если ты об именах, я бы порекомендовал a -> A_order_by_f1_f2_desc; b -> B_equals_h1. Причем желательно, чтобы f1, f2 и h1 были не тупо полями структурами, а обладали семантическим смыслом (возрас, пол, имя и т. п.). В этом случае соответствующий sort будет интуитивно понятен.

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

>OMG. И после этого я еще удивляюсь, почему с каждым годом софт все тормознее и тормознее.

Ну и где тут тормознутость? Раскажи как исправить.

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

>Чистейшее 4.2.

Незамутненный образчик индусского стиля мышления. Сортировка производится не по полю, а по критерию. Если не хочешь через неделю заблудиться в своей лапше из говнокода, создавай функции сортировки по критерию. В таком случае менять их придется только при изменении смысла структуры данных.

Правда могут быть подводные камни: у полей структуры может не быть семантического значения, но в таком случае встает логичный вопрос: а нахера тебе структура из бессмысленных данных? Лабу сдать, к зачету допуститься?

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

>Ну и где тут тормознутость?

Косвенный вызов.

Раскажи как исправить.

Заменить на вызов, адрес которого известен во время компиляции (в идеале — способный заинлайниться).

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

Вариация на тему:

#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <boost/mem_fn.hpp>

struct Foo
{
  int a;
  int b;
  std::string c;

  Foo(int a, int b, const std::string& c)
    : a(a), b(b), c(c)
  { }
};

template<typename TransformFunction, 
         typename CompareFunction>
class TransformedComparator
{
  TransformFunction tf;
  CompareFunction cf;
public:
  TransformedComparator(TransformFunction tf, CompareFunction cf)
    : tf(tf), cf(cf)
  { }

  template<typename T>
  bool operator() (T x, T y)
  {
    return cf(tf(x), tf(y));
  }
};


template<typename TransformFunction, 
         typename CompareFunction>
TransformedComparator<TransformFunction, CompareFunction>
makeTransformedComparator(TransformFunction tf, CompareFunction cf)
{
  return TransformedComparator<TransformFunction, CompareFunction>(tf, cf);
}

std::ostream& operator<< (std::ostream& ostm, const Foo& f)
{
  return ostm << "Foo { a = " << f.a << ", b = " << f.b << ", c = \"" << f.c << "\" }";
} 

int main()
{
  std::vector<Foo> a({Foo(1,2, "a"), Foo(3,4,"b"), Foo(-5,-6, "qqq")});

  std::sort(a.begin(), a.end(), makeTransformedComparator(boost::mem_fn(&Foo::a), std::less<int>()));
  std::copy(a.begin(), a.end(), std::ostream_iterator<Foo>(std::cout, "\n"));  

  std::cout << std::endl;

  std::sort(a.begin(), a.end(), makeTransformedComparator(boost::mem_fn(&Foo::c), std::less<std::string>()));
  std::copy(a.begin(), a.end(), std::ostream_iterator<Foo>(std::cout, "\n"));
}

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

Т.е., как указано в начале темы,

из чего вытекло наличие кучи-малы маленьких локальных функций сравнения

от кучи-малы и разброда в коде твой совет не спасет, а лишь оттянет мучительный fail.. а жаль :(

impfp
() автор топика
Ответ на: комментарий от linuxfan

>>Ну и где тут тормознутость?

Косвенный вызов.

А то что аргументы там передаются по значению ты, значит, не заметил?

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

Сортировка производится не по полю, а по критерию.

Кто бы спорил.

Если не хочешь через неделю заблудиться в своей лапше из говнокода, создавай функции сортировки по критерию.

Ты уже видел мою лапшу из говнокода?

В таком случае менять их придется только при изменении смысла структуры данных.

Т.е. ты подтверждаешь мои слова: весь вопрос в интерфейса типа. Те поля/методы/функции которые используются в коде должны сооветсвовать характеристакам объекта из предметной области, а не тому как эти объекты реализованы.

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

от кучи-малы и разброда в коде твой совет не спасет

Как раз таки от разброда спасает. Просто все методы сортировки надо объявлять не по месту первой надобности, а в одном централизованном месте.

а лишь оттянет мучительный fail..

Никакого фейла. Набор критериев сортировки конечен => конечно и количество таких функций. Вся проблема лишь в том, что тысячи таких функций очень лениво писать. У Богов на этот случай существует лисп с его макросами, простые же смертные могут воспользоваться препроцессором:

#define MAKE_CMP_FUNTION(struct_name, field) \
  bool #struct_name#_order_by_#field(const struct struct_name *a, const struct struct_name *b) { return a->field < b->field; }
#define MAKE_CMP_DESC_FUNTION(struct_name, field) \
  bool #struct_name#_order_by_#field#_desc(const struct struct_name *a, const struct struct_name *b) { return a->field > b->field; }
linuxfan
()
Ответ на: комментарий от SV0L0CH

>А то что аргументы там передаются по значению ты, значит, не заметил?

Я вообще-то заметил даже то, что ты не умеешь писать typedef'ы указателей на функцию и operator().

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

>Это С++, таким кодом приходится платить за отсутствие в языке замыканий.

Это С++, таким кодом приходиться платить, чтобы показать отсутствие мозга и слепое следование заветам Александреску.

fixed!

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

>>А то что аргументы там передаются по значению ты, значит, не заметил?

Я вообще-то заметил даже то, что ты не умеешь писать typedef'ы указателей на функцию и operator().

Наблюдательный... А есчё я не умею отлаживать код с шаблонами и пользоватся кастами... Много чего не умею... Зачем лишние умения когда всё делается проще?

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

> Это С++

ну собственно С++ уже позволяет писать проще, дело за реализацией - а она уже на пороге( в вижуале 2010, например, все работает, в gcc 4.5 вроде как тоже - но не проверял ), надеюсь, что после полной реализации C++0x буст торжественно и с музыкой закопают, как нечитабельное и костыльное уродство

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

Мысль понятна, однако - надо, как минимум, еще функции-комбинаторы иметь, т.к. ф-ции сравнения не такие уж и тривиальные (да еще некоторые с побочными эффектами). - у меня ощущение, что объем кода такой подходу вкупе не уменьшит, и «красивее» (читабельнее+понятнее) не сделает.

impfp
() автор топика
Ответ на: комментарий от linuxfan

С подучи, что бы уж совсем глупы ляпы не делать.

max@neptune:~/
% cat bar.c
#define MAKE_CMP_FUNTION(struct_name, field) \ 
  bool #struct_name#_order_by_#field(const struct struct_name *a, const struct struct_name *b) { return a->field < b->field; } 
#define MAKE_CMP_DESC_FUNTION(struct_name, field) \ 
  bool #struct_name#_order_by_#field#_desc(const struct struct_name *a, const struct struct_name *b) { return a->field > b->field; }

MAKE_CMP_FUNTION(foo, a)
max@neptune:~/
% cpp bar.c
# 1 "bar.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "bar.c"
bar.c:1:46: warning: backslash and newline separated by space
bar.c:1:44: error: '#' is not followed by a macro parameter
bar.c:3:51: warning: backslash and newline separated by space
bar.c:3:49: error: '#' is not followed by a macro parameter





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