Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Март 15, 2011, 16:24



Название: Как сделать кратко/красиво
Отправлено: Igors от Март 15, 2011, 16:24
Добрый день

Есть структура
Код
C++ (Qt)
struct CMesh {
void Append( const CMesh & );  // импорт данных
void Clear( void );  // освобождает память занимаемую данными
CShape * CreateShape( void ) const;  // создает др. (внешнюю) структуру
 
// данные
float mFriction;
//... контейнеры и.т.п
};
typedef std::vector <CMesh *> TMeshPtrVec;
typedef std::vector <CShape *> TShapePtrVec;
 

Теперь надо написать ф-цию
Код
C++ (Qt)
// static
void CMesh::CreateShapes( TMeshPtrVec & mesh, TShapePtrVec & shape )
{
...
}
 
которая создает из элементов входного mesh вектора CShape * и помещает их в выходной вектор shape. но с одним условием: входные элементы с одинаковым mFriction должны быть "слиты" (с помощью методов Append и Clear) и только одно CShape создано для таких. Примеры: все элементы mesh имеют одинаковый mFriction - значит всего 1 новый элемент добавлен в shape. И наоборот - все mFriction разные - значит для каждого надо создать CShape.

Вопрос в том как это сделать красиво/элегантно - у меня получается коряво  :)

Спасибо


Название: Re: Как сделать кратко/красиво
Отправлено: m_ax от Март 15, 2011, 17:05
Я бы посмотрел в сторону алгоритма find_if, в сочетании с count_if

Или вот, пожалуй, лучше всего подходит под это дело:
http://www.cplusplus.com/reference/algorithm/unique_copy/
 


Название: Re: Как сделать кратко/красиво
Отправлено: Igors от Март 15, 2011, 19:38
Я бы посмотрел в сторону алгоритма find_if, в сочетании с count_if

Или вот, пожалуй, лучше всего подходит под это дело:
http://www.cplusplus.com/reference/algorithm/unique_copy/
Хммм...  "прошу исполнить"  :)


Название: Re: Как сделать кратко/красиво
Отправлено: m_ax от Март 15, 2011, 20:17
Ну вот, например:
Код
C++ (Qt)
bool myBinaryPredicate(CMesh* a, CMesh* b) {
   return (a->mFriction == b->mFriction);
}
 
bool compare (CMesh* a, CMesh* b) {
   return (a->mFriction < b->mFriction);
}
 
 
void CMesh::CreateShapes( TMeshPtrVec & mesh, TShapePtrVec & shape )
{
    TMeshPtrVec v = mesh;
    TMeshPtrVec::iterator it = unique_copy (mesh.begin(), mesh.end(), v.begin(), myBinaryPredicate);
    sort (v.begin(), it, compare);
    it=unique_copy (v.begin(), it, v.begin(), myBinaryPredicate);
    v.resize( it - v.begin());
/* v - теперь содержит указатели на CMesh c уникальными значениями mFriction */
/* Дальше все действия с ним..  */
}
 

Хотя я это не проверял)


Название: Re: Как сделать кратко/красиво
Отправлено: Igors от Март 15, 2011, 20:42
Хотя я это не проверял)
Та вижу что нет  :)
Пусть первые 2 элемента имеют одинаковый ключ (mFriction). Тогда, если я правильно понимаю, unique_copy поместит в выходной вектор лишь первый из них, второй не будет долит к первому - а должен


Название: Re: Как сделать кратко/красиво
Отправлено: m_ax от Март 15, 2011, 20:47
Хотя я это не проверял)
Та вижу что нет  :)
Пусть первые 2 элемента имеют одинаковый ключ (mFriction). Тогда, если я правильно понимаю, unique_copy поместит в выходной вектор лишь первый из них, второй не будет долит к первому - а должен
В смысле долит? Этот код создаёт только вектор с указателями на уникальные mFriction.. Он ничё никуда не доливает)  

Если я правильно понимаю, то доливание одного вектора в другой, если у них одинаковые mFriction можно произвести в myBinaryPredicate...


Название: Re: Как сделать кратко/красиво
Отправлено: Igors от Март 15, 2011, 20:55
В смысле долит? Этот код создаёт только вектор с указателями на уникальные mFriction.. Он ничё никуда не доливает) 
Ну а зачем такой вектор нужен? Ведь задача объединить (с помощью Append) подходящие элементы и создать для них одну CShape.

Кстати о птичках: если уж создавать вектор уникальных, то не лучше ли сначала отсортировать, а затем один раз вызвать unique_copy (а то увлеклись списыванием  :))


Название: Re: Как сделать кратко/красиво
Отправлено: m_ax от Март 15, 2011, 21:05
В смысле долит? Этот код создаёт только вектор с указателями на уникальные mFriction.. Он ничё никуда не доливает) 
Ну а зачем такой вектор нужен? Ведь задача объединить (с помощью Append) подходящие элементы и создать для них одну CShape.
Можно непосредственно в функции myBinaryPredicate "сливать" данные из одного (a) в другой (b) (с помощью Append), если они равны.

Цитировать
Кстати о птичках: если уж создавать вектор уникальных, то не лучше ли сначала отсортировать, а затем один раз вызвать unique_copy (а то увлеклись списыванием  Улыбающийся)

Можно, конечно) Я просто Вашу бдительность проверял))


Название: Re: Как сделать кратко/красиво
Отправлено: Igors от Март 15, 2011, 21:26
Можно непосредственно в функции myBinaryPredicate "сливать" данные из одного (a) в другой (b) (с помощью Append), если они равны.
Может даже это и будет работать (не знаю), но делать такие "масштабные" действия в ф-ции предназначенной просто для сравнения - никак не гуд.

Да и копирование вектора (с последующим resize) хорошего впечатления не производит. Нельзя сделать как-то "через голову", не слишком привлекая мутность STL?


Название: Re: Как сделать кратко/красиво
Отправлено: m_ax от Март 15, 2011, 21:44
Можно непосредственно в функции myBinaryPredicate "сливать" данные из одного (a) в другой (b) (с помощью Append), если они равны.
Может даже это и будет работать (не знаю), но делать такие "масштабные" действия в ф-ции предназначенной просто для сравнения - никак не гуд.
Почему эт? Это всё равно придётся делать) Кстати, зачем тогда придумали ещё и объекты функций?
Правильно, для большей гибкости.

Цитировать
Да и копирование вектора (с последующим resize) хорошего впечатления не производит. Нельзя сделать как-то "через голову", не слишком привлекая мутность STL?
Да он всё равно локален и разрушится при выходе из функции.
Сколько там (в mesh) в среднем элементов планируется держать?

Если без этого временного вектора v, то тогда лучше использовать find_if, но это будет медленее и так кратко не думаю что получится. 


Название: Re: Как сделать кратко/красиво
Отправлено: Igors от Март 15, 2011, 22:02
Почему эт? Это всё равно придётся делать) Кстати, зачем тогда придумали ещё и объекты функций?
Правильно, для большей гибкости.
Мда, шансы убедить равны нулю  :) Лучше расскажу как я сделал

Код
C++ (Qt)
bool CompareFriction( const CMesh * m0, const CMesh * m1 )
{
return m0->mFriction < m1->mFriction;
}
 
bool NextRange( const TMeshPtrVec & vec, size_t & beg, size_t & end )
{
if (end >= vec.size()) return false;
beg = end;
for (end = beg + 1; end < vec.size(); ++end)
 if (vec[end]->mFriction != vec[beg]->mFriction) break;
 
return true;
}
 
 
void CMesh::CreateShapes( TMeshPtrVec & mesh, TShapePtrVec & shape )
{
std::sort(mesh.begin(), mesh.end(), CompareFriction);
 
size_t beg, end = 0;
while (NextRange(vec, beg, end)) {
 for (size_t i = beg + 1; i < end; ++i) {
  vec[beg]->Append(*vec[i]);
  vec[i]->Clear();
 }
 shape.push_back(vec[beg]->CreateShape());
}
}
 


Название: Re: Как сделать кратко/красиво
Отправлено: m_ax от Март 15, 2011, 22:35
Тогда как вариант:
Код
C++ (Qt)
class MyFunctor
{
public:
   CMesh * operator()(CMesh *a, CMesh *b) {
       if ((a->mFriction == b->mFriction) && (a->mFriction == m_unique_friction.back()) && m_tag)
      {
           a->Append(b);
           b->Clear();
           return a;
       }
        return 0;
   }
 
bool frictionIsContains(float f) {
   vector<double>::iterator it = find(m_unique_friction.begin(), m_unique_friction.end(), f);
   if (it != m_unique_friction.end()) {
       m_unique_friction.push_back(f);
       m_tag = true;
       return m_tag;
   }
    m_tag = false;
    return m_tag;
}
private:
vector<float> m_unique_friction;
bool m_tag;
};
 
void CMesh::CreateShapes( TMeshPtrVec & mesh, TShapePtrVec & shape )
{
   TMeshPtrVec::iterator it = mesh.begin();
   MyFunctor func;
    for (; it != mesh.end(); ++it) {
       if (!func.frictionIsContains((*it)->mFriction))
           continue;
       CMesh *cm = accumulate(it, mesh.end(), func);
       shape.push_back(cm->CreateShape());
   }
}
 
 


Название: Re: Как сделать кратко/красиво
Отправлено: Igors от Март 15, 2011, 23:11
Тогда как вариант:
Код
C++ (Qt)
..
       CMesh *cm = accumulate(it, mesh.end(), func);
..
 
Не въехал с сынтаксысом. Вроде "начальное значение" для accumulate обязательно, значит так
Код
C++ (Qt)
       CMesh *cm = accumulate(it, mesh.end(), *it, func);
 
Но тогда последующая проверка на NULL теряет смысл, cm == *it


Название: Re: Как сделать кратко/красиво
Отправлено: m_ax от Март 15, 2011, 23:17
Цитировать
Не въехал с сынтаксысом. Вроде "начальное значение" для accumulate обязательно,
Да, попутал))

Вот окончательно:
Код
C++ (Qt)
class MyFunctor
{
public:
   CMesh * operator()(CMesh *a, CMesh *b) {
       if ((a->mFriction == b->mFriction) && (a->mFriction == m_unique_friction.back()) && m_tag)
      {
           a->Append(b);
           b->Clear();
       }
        return a;
   }
 
bool frictionIsContains(float f) {
   vector<float>::iterator it = find(m_unique_friction.begin(), m_unique_friction.end(), f);
   if (it == m_unique_friction.end()) {
       m_unique_friction.push_back(f);
       m_tag = true;
       return !m_tag;
   }
    m_tag = false;
    return !m_tag;
}
private:
vector<float> m_unique_friction;
bool m_tag;
};
 
void CMesh::CreateShapes( TMeshPtrVec & mesh, TShapePtrVec & shape )
{
   TMeshPtrVec::iterator it = mesh.begin();
   MyFunctor func;
    for (; it != mesh.end(); ++it) {
       if (func.frictionIsContains((*it)->mFriction))
           continue;
       CMesh *cm = accumulate(it, mesh.end(), *it,  func);
       shape.push_back(cm->CreateShape());
   }
}
 


Название: Re: Как сделать кратко/красиво
Отправлено: m_ax от Март 15, 2011, 23:25
Это должно быть по быстрее ;)


Название: Re: Как сделать кратко/красиво
Отправлено: _govorilka от Март 16, 2011, 07:43
А так:
Код
C++ (Qt)
 
void createShapes(QVector<CMesh*>& mesh, QVector<CShape*>& shape)
{
    QHash<int, CMesh*> meshCache;
    foreach(CMesh* m, mesh)
   {
         CMesh* m2 = meshChach.value(m->mFriction, 0);
         if(m2)
        {
              m2->Append(m);
        }
        else
        {
              meshCache.insert(m->mFriction, m);
        }
   }
   foreach(CMesh* m, meshCache.values)
  {
        shape.append(m->createShape());
  }
}
 

Это тоже самое, что у тебя но через QHash кода меньше.

Можно оптимизировать. Если добавить указатель на CMesh в класс CShape, то второй QHash будет не нужен.


Название: Re: Как сделать кратко/красиво
Отправлено: Igors от Март 16, 2011, 12:56
Я бы добавил в класс CMesh функцию:
Код
C++ (Qt)
 
void apply(CShape* shape);
 

Тогда можно написать, что типа этого:
Так по задаче нельзя - все Append должны быть применены к CMesh ДО вызова CreateShape (т.е. созданная CShape представляет все слитые данные, изменить их в CShape уже нельзя)


Название: Re: Как сделать кратко/красиво
Отправлено: _govorilka от Март 16, 2011, 13:24
А так:
Код
C++ (Qt)
 
void createShapes(QVector<CMesh*>& mesh, QVector<CShape*>& shape)
{
    QHash<int, CMesh*> meshCache;
    foreach(CMesh* m, mesh)
    {
         CMesh* m2 = meshChach.value(m->mFriction, 0);
         if(m2)
        {
                 m2->Append(m);
        }
        else
        {
              meshCache.insert(m->mFriction, m);
        }
   }
   foreach(CMesh* m, meshCache.values)
  {
        shape.append(m->createShape());
  }
}
 

Это тоже самое, что у тебя но через QHash кода меньше.


Название: Re: Как сделать кратко/красиво
Отправлено: Igors от Март 16, 2011, 14:11
А так:
А так согласен, намного короче. Там правда mFriction float и после Append надо делать Clear - но не суть. Проходят и др ассоциативные контейнеры, для полноты вариант с std::map

Код
C++ (Qt)
void CMesh::CreateShapes( TMeshPtrVec & mesh, TShapePtr & shape )
{
typedef std::map <float, CMesh *> TFrictionMap;
TFrictionMap fMap;
TFrictionMap::iterator it;
 
for (size_t i = 0; i < mesh.size(); ++i) {
  CMesh * m = mesh[i];
  it = fMap.find(m->mFriction);
  if (it == fMap.end())
   fMap.insert(std::make_pair(m->mFriction, m));
  else {
    it->second->Append(*m);
    m->Clear();
  }
}
 
for (it = fMap.begin(); it != fMap.end(); ++it)
 shape.push_back(it->second->CreateShape());  
}
 
Правда запрягается контейнер, но это допустимо/нормально.

По ходу дела (конечно это др. задача, но интересно): а если "нечеткое" совпадение ключа? Например нужно сливать CMesh если их mFriction отличается не более чем на заданный epsilon (напр 1.0e-5)

Спасибо


Название: Re: Как сделать кратко/красиво
Отправлено: _govorilka от Март 16, 2011, 14:41
По ходу дела (конечно это др. задача, но интересно): а если "нечеткое" совпадение ключа? Например нужно сливать CMesh если их mFriction отличается не более чем на заданный epsilon (напр 1.0e-5)

Насколько я помню у std::map третий параметр в шаблоне - функция, которая она вызывает для сравнения элементов. В STL не силен, точно сказать не могу. В Qt для сравнения двух float есть функция:
Код
C++ (Qt)
bool qFuzzyCompare ( float p1, float p2 )