Russian Qt Forum
Январь 31, 2023, 00:22 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 2 3 [4]   Вниз
  Печать  
Автор Тема: Compile-time определение реверсивности итератора (reverse_iterator)  (Прочитано 11868 раз)
AkonResumed
Чайник
*
Offline Offline

Сообщений: 81


Просмотр профиля
« Ответ #45 : Апрель 15, 2021, 18:36 »

Итак, задача: есть диапазон дат (QDate), задаваемый парой итераторов [begin, end). Даты идут с шагом в 7-дней. Необходимо сдвинуть диапазон влево или вправо на любое заданное число элементов, при этом новые даты должны продолжать последовательность.

я так понимаю, сдвиг на N - это "прибавить N недель к каждой дате"?

тогда (псевдокод):

Код:
void shift(IteratorT begin, IteratorT end, int offset)
{
  for (auto& it = begin; it != end; it++)
  {
    *it = (*it).addDays(offset * 7);
  }
}

это имелось в виду? Улыбающийся
Шокированный Блин, ну здорово!! На самом деле я неудачно упростил задачу, и вместо QDate там шаред-поинтер на класс с членом, возвращающим QDate. И нужно именно "двигать", т.е. для выдвигаемых элементов должен вызываться деструктор, а вдвигаемые должны создаваться конструктором с новой датой.

Мое упущение. И мне не пришло в голову, что вот так вот можно шунтировать!

Непосредственно на то, что я сформулировал вы привели исчерпывающее решение, ну может быть
Код:
void shift(IteratorT begin, IteratorT end, int offset)
{
  while (begin != end)
  {
    *begin++ = (*begin).addDays(offset * 7);
  }
}
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #46 : Апрель 15, 2021, 18:53 »

Согласен, через while покошернее смотрится Улыбающийся
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #47 : Апрель 16, 2021, 09:41 »

адреса могут поехать, но в qt5 он был старым-добрым массивом линкед-листов и давал гарантию при insert'e (по крайней мере, оно работало на практике)
Но как минимум странно имея имплементацию "по стандарту" менять ее на другую которую захотела левая пятка, поломав кучу кода.
Бегло глянул исходники на вебе, не вижу что/как там "поломано", ноды создаются с помощью allocateNode, их адреса остаются неизменными. Итераторы - да, уплыть могут, но это было всегда (см erase)

Просто ваши задачи требуют неадекватного количества времени чтобы понять вообще в чем задача - требования меняются каждую страницу потому что вы их "забыли" указать, но предложенное решение им (конечно же!) не удовлетворяет.
Улыбающийся Ну вот задача на тот же хеш вверху. Я не предлагаю ее решить, она достаточно сложна. До "собсно программирования" там еще довольно далеко. Да, на таких данных/кратности что-то ускорить может только хеш, но что должно быть ключом? Конечно, такие задачи возникают не каждый день (типовых больше, и намного), но они есть, и решать их придется.

И вот как только случается нечто "нетривиальное" (что угодно) - у знатоков возникают большие проблемы Улыбающийся Им ведь нужно "знать" как решать (какой класс или ф-цию хапнуть), а такового почему-то не находится. Это как же, писать свой хеш?? (да). Ну и начинается естественная реакция - "плохая задача". И заодно "никудышняя постановка", а там недалеко и до "хреновый постановщик" и.т.п.  Улыбающийся Это не совсем так, все только потому что задача не та на которую можно сразу выпалить "правельный ответ".
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #48 : Апрель 16, 2021, 09:52 »

Ага, оказывается "добавить дни" ф-ция есть, а я думал проблема в этом.
и вместо QDate там шаред-поинтер на класс с членом, возвращающим QDate. И нужно именно "двигать", т.е. для выдвигаемых элементов должен вызываться деструктор, а вдвигаемые должны создаваться конструктором с новой датой.
Ну извлечь диапазон, впарить новые даты, потом найти место вставки (lower_bound), и вставить. Все банально (или я чего-то не знаю)
« Последнее редактирование: Апрель 16, 2021, 10:13 от Igors » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4338



Просмотр профиля
« Ответ #49 : Апрель 16, 2021, 09:59 »

И вот как только случается нечто "нетривиальное" (что угодно) - у знатоков возникают большие проблемы Улыбающийся
Конечно, а неучи сразу решают. Улыбающийся

Только решений что-то не видно, ну не считая финдреплейсов и переходничков. Улыбающийся

А как только появляется хоть какая то задача, сложнее финдреплейса, неучи спешать Выдрать код из игрового движка
или утащить посмотреть как сделано у других:
[off]Недавно пришлось "собирать" больше десятка приложений чтобы посмотреть "как это там сделано". Прослеживается четкая зависимость: где больше С++ 17 и.т.п. - там меньше мыслей (вернее вообще нет, что-то придумать даже не пытался). Конечно "лучше быть богатым и здоровым", но голова-то одна. Ни к чему "через край" жрать этот сахар
[/off]
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3251


Просмотр профиля
« Ответ #50 : Апрель 16, 2021, 10:44 »

Бегло глянул исходники на вебе, не вижу что/как там "поломано", ноды создаются с помощью allocateNode, их адреса остаются неизменными. Итераторы - да, уплыть могут, но это было всегда (см erase)


хз куда вы смотрели

https://doc.qt.io/qt-6/qtcore-changes-qt6.html#stability-of-references
Записан
AkonResumed
Чайник
*
Offline Offline

Сообщений: 81


Просмотр профиля
« Ответ #51 : Апрель 16, 2021, 11:04 »

Ага, оказывается "добавить дни" ф-ция есть, а я думал проблема в этом.
и вместо QDate там шаред-поинтер на класс с членом, возвращающим QDate. И нужно именно "двигать", т.е. для выдвигаемых элементов должен вызываться деструктор, а вдвигаемые должны создаваться конструктором с новой датой.
Ну извлечь диапазон, впарить новые даты, потом найти место вставки (lower_bound), и вставить. Все банально (или я чего-то не знаю)

Выдвигаемый элемент уничтожается (деструктор). Вдвигаемый создается конструктором с новой датой. Если же элемент остается в диапазоне (т.е. он просто сдвигается, но не выдвигается), то он остается нетронутым.

[1 2 3] -> shift_left -> 1 (dtor) [2(untoched) 3(untoched) 4(ctor)].

Короче, элемент можно удалить, добавить, сдвинуть, но не модифицировать (нет интерфеса для модификации даты).

« Последнее редактирование: Апрель 16, 2021, 11:09 от AkonResumed » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #52 : Апрель 16, 2021, 12:41 »

Выдвигаемый элемент уничтожается (деструктор). Вдвигаемый создается конструктором с новой датой. Если же элемент остается в диапазоне (т.е. он просто сдвигается, но не выдвигается), то он остается нетронутым.

[1 2 3] -> shift_left -> 1 (dtor) [2(untoched) 3(untoched) 4(ctor)].

Короче, элемент можно удалить, добавить, сдвинуть, но не модифицировать (нет интерфеса для модификации даты).
Ну и почему это не сделать обычным удалением/вставкой? Не понимаю где проблема
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #53 : Апрель 16, 2021, 12:47 »

Вопрос: а какой критерий должен быть у вставляемых элементов? Т.е. откуда должны браться значения для них?
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
AkonResumed
Чайник
*
Offline Offline

Сообщений: 81


Просмотр профиля
« Ответ #54 : Апрель 16, 2021, 13:35 »

Можно сделать как угодно в рамках заданного прототипа (указывал выше).

Элемент такой (все лишнее выкинул). Он создается с даты. Дату первого вставляемого элемента мы определяем из даты первого/последнего элемента диапазона (в зависимости от направления сдвига) + или - (в зависимости от направления сдвига) смещение * 7. 
Элемент:
Код:
class Week
{
public:
explicit Week(QDate date) : date_(date) {}
QDate date() const { return date_; }

private:
QDate date_;
};

Диапазон может быть задан, например, так (шаред-поинтер выкинул для упрощения):
Код:
QList<Week*> weeks = {
new Week(QDate::currentDate().addDays(-7)),
new Week(QDate::currentDate()),
new Week(QDate::currentDate().addDays(7))
};
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #55 : Апрель 17, 2021, 09:58 »

Ну и почему это не сделать обычным удалением/вставкой? Не понимаю где проблема
Если установлено требование "keep ascending order" то вставка диапазона - некорректная операция, т.к. несдвинутые эл-ты могут оказаться между сдвинутыми. По той же причине некорректны все игры с шифтами. Можно конечно вставлять "по одному", но это затратно (уж точно не high-end). Предлагаю так
Код
C++ (Qt)
void OffsetRange( QList<Week *> & lst, Iter beg, Iter end, int offset )
{
// calc sort range
  auto sortBeg = beg;
  auto sortEnd = end;
 
  if (offset > 0) {
   if (end != lst.end())
    sortEnd = std::lower_bound(beg, lst.end(), (*end)->data().addDays(offset), CompDate1);
  }
  else {
   if (beg != lst.end())
    sortBeg = std::lower_bound(lst.begin(), beg, (*beg)->data().addDays(offset), CompDate1);
 }
 
// modify elements
 while (beg != end) {
   auto & dst= *beg;
   auto newP = new Week(dst->data().addDays(offset));
   delete dst;
   dst = newP;
   ++beg;
 }
 
// sort it
 std::sort(sortBeg, sortEnd, CompDate2);
}
« Последнее редактирование: Апрель 17, 2021, 11:01 от Igors » Записан
AkonResumed
Чайник
*
Offline Offline

Сообщений: 81


Просмотр профиля
« Ответ #56 : Апрель 19, 2021, 15:34 »

Это несколько не то. Привожу свое решение. Это ready-to-use консольная прога. Собственно, то что нужно сделать находится в тэгах //<<<<<<<< ... //>>>>>>>>. Осмелюсь заявить, что это очень элегантное/эффективное решение ввиду того, что код инвариантен направлению сдвига. Единственный участок, использующий направление, это IsReverseIterator<IteratorT>, и притом это compule-time.

Если кто приведет еще лучше - буду очень признателен.

main.cpp
Код:
#include <QtCore/QCoreApplication>
#include <QtCore/QDate>
#include <QtCore/QDebug>
#include <QtCore/QList>
#include <QtCore/QSharedPointer>

class Week
{
public:
explicit Week(QDate date) : date_(date) { qDebug() << "Week: ctor()"; }
~Week() { qDebug() << "Week: dtor()"; }

QDate date() const { return date_; }

private:
QDate date_;
};

//<<<<<<<<

template <typename, typename = void>
inline constexpr bool IsReverseIterator = false;

template <typename T>
inline constexpr bool IsReverseIterator<T, std::void_t<decltype(std::declval<T>().base())>> = true;

/// Shifts the range of Week*s to left or right on \a offset positions (weeks). The shift direction
/// is determined by the iterators kind - forward ones indicate shifting to left (with insertion on
/// the right side future weeks) and reverse ones - to right (with insertion on the left side past
///  weeks). \a offset may be any value (even greater then the range size). Shifted out Weeks
/// are destroyed, shifted in ones are created with the proper dates.
template <typename IteratorT>
static void shift(IteratorT begin, IteratorT end,
typename std::iterator_traits<IteratorT>::difference_type offset)
{
if (Q_UNLIKELY(!offset)) return;

// std::copy() uses '!=', not '<' for iterator comparison, so values above end are invalid; we
// decompose offset as offset + offsetExceeding, where the offset < end.
auto size = std::distance(begin, end);
auto offsetExceeding = offset <= size ? 0 : offset - size;
offset -= offsetExceeding;

// Date step and first shifting into element's date
static const int DayStep = 7 * (IsReverseIterator<IteratorT> ? -1 : 1);
QDate date = (*std::prev(end))->date().addDays(DayStep * (offsetExceeding + 1));

// Shift left/right, shifted out elements are freed
qDeleteAll(begin, begin + offset);
auto pos = std::copy(begin + offset, end, begin);

// Create new elements placing them from left/right side
for (; pos != end; date = date.addDays(DayStep))
*pos++ = new Week(date);
}

//>>>>>>>>

// Just syntactic sugar for whole container. Shift direction is determined by offset's sign.
template <typename ContainerT>
static void shift(ContainerT& container, int offset)
{
if (offset > 0)
shift(container.begin(), container.end(), offset);
else if (offset < 0)
shift(container.rbegin(), container.rend(), -offset);
}

static void print(const QList<Week*>& weeks)
{
for (auto week : weeks)
qDebug() << week->date();
}

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QList<Week*> weeks = {
new Week(QDate::currentDate().addDays(-7)),
new Week(QDate::currentDate()),
new Week(QDate::currentDate().addDays(7))
};
qDebug() << "\nOriginal:";
print(weeks);

qDebug() << "\nShift left on 1:";
shift(weeks, 1);
print(weeks);

qDebug() << "\nShift right on 1, so we get the original range:";
shift(weeks, -1);
print(weeks);

qDebug() << "\nShift left on 4:";
shift(weeks, 4);
print(weeks);

qDebug() << "\nShift right on 4, so we get the original range:";
shift(weeks, -4);
print(weeks);

return a.exec();
}

ContainerShifting.pro
Код:
QT -= gui

CONFIG += c++17 console
CONFIG -= app_bundle

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        main.cpp

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

Записан
Страниц: 1 2 3 [4]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.082 секунд. Запросов: 23.