У меня есть std::vector<std:::chrono::system_clock::time_point>
ценности.
Я хочу усреднить разницу во времени в миллисекундах между соседними элементами. Для контейнера требуется как минимум 2 элемента, и у меня возникают проблемы с функцией усреднения. Я использовал std::accumulate, но я не думаю, что смогу заставить это работать, так как получаю массу ошибок компилятора. Лямбда ниже, которую я использую в качестве функции уменьшения с алгоритмом накопления
auto gLambda = [&](
system_clock::time_point t1, system_clock::time_point t2) {
return duration_cast<milliseconds>(t2 - t1).count();
};
хотя работает нормально
Демо live coliru
int main()
{
// lambda called with adjacent samples from the deque
auto gLambda = [&](
system_clock::time_point t1, system_clock::time_point t2) {
return duration_cast<milliseconds>(t2 - t1).count();
};
// this line compiles and the lambda seems to do the right thing
const auto t1 = system_clock::now();
std::this_thread::sleep_for(55ms);
const auto t2 = system_clock::now();
// result is 55 as expected
auto result = gLambda(t1, t2);
std::cout << result << std::endl;
// create a vector of 10 timestamps
std::vector<system_clock::time_point> timeStamps;
for (auto i=0; i<10; i++) {
timeStamps.emplace_back(system_clock::now());
std::this_thread::sleep_for(15ms);
}
// THIS LINE CAUSES THE COMPILATION ERROR
auto result1 = std::accumulate(timeStamps.cbegin(), timeStamps.cend(), 0.0, gLambda);
std::cout << result1 << std::endl;
}
приводит к следующим ошибкам компиляции, которые я не понимаю.
In file included from /usr/local/include/c++/10.2.0/numeric:62,
from main.cpp:4:
/usr/local/include/c++/10.2.0/bits/stl_numeric.h: In instantiation of '_Tp std::accumulate(_InputIterator, _InputIterator, _Tp, _BinaryOperation) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >*, std::vector<std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > > > >; _Tp = double; _BinaryOperation = main()::<lambda(std::chrono::_V2::system_clock::time_point, std::chrono::_V2::system_clock::time_point)>]':
main.cpp:30:88: required from here
/usr/local/include/c++/10.2.0/bits/stl_numeric.h:169:22: error: no match for call to '(main()::<lambda(std::chrono::_V2::system_clock::time_point, std::chrono::_V2::system_clock::time_point)>) (double&, const std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >&)'
169 | __init = __binary_op(_GLIBCXX_MOVE_IF_20(__init), *__first);
| ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:12:20: note: candidate: 'main()::<lambda(std::chrono::_V2::system_clock::time_point, std::chrono::_V2::system_clock::time_point)>'
12 | auto gLambda = [&](
| ^
main.cpp:12:20: note: no known conversion for argument 1 from 'double' to 'std::chrono::_V2::system_clock::time_point' {aka 'std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >'}
Третий аргумент (начальное значение) accumulate
— это 0.0
, то есть double
. Это трансформируется в time_point
? Именно таким окажется первый аргумент лямбды.
@PaulMcKenzie Я не уверен, как преобразовать двойное значение в момент времени, возможно, я попытался установить начальное значение, явно установленное для момента времени сейчас() вместо 0,0, и это вызвало столько же ошибок, как «неизвестное преобразование для аргумента». 1 с «long int» на «const std::chrono::time_point»
@johnco3 Вероятно, мне нужно еще немного поиграть с вашим примером. Но определенно ошибка является классической std::accumulate
, когда аргумент начального значения не соответствует (или не может быть преобразован) первому аргументу в функторе / лямбда / и т. д. Последняя строка в списке ошибок в основном утверждает это.
@PaulMcKenzie спасибо, я думаю так же - буду признателен, если вы поможете мне с примером. Я потратил около часа на эксперименты безрезультатно.
Я не думаю, что накапливать - правильный инструмент для работы, он суммирует все элементы, а не соседние пары, руководство по циклу, вероятно, будет лучшим решением.
@ johnco3 Я думаю, что обычный цикл тоже был бы немного проще. В любом случае, я не эксперт в манипулировании различными типами времени навскидку (стыдно) в C++, но это компилируется. Справится ли он со своей задачей — это отдельная история.
@PaulMcKenzie - забавно, ты должен это сделать, я сам пробовал что-то подобное coliru.stacked-crooked.com/a/fb9c1f10c8e5fd62 :) похоже, у нас была такая же идея. Я думаю, что мне нужно выбрать немного другой алгоритм, как указывает Алан Бертлз. Я использовал смежный_найти раньше - возможно, это даст правильный результат, если я аккумулирую различия между соседними элементами, а затем, наконец, разделю на size-1 вектора
Да, adjacent_find
, который возвращает false
в лямбде, будет работать. Меня ругали за то, что использование adjacent_find
для такой «парной работы» является злоупотреблением функцией, но если это сработает...
Пользователь @PaulMcKenzie sehe предупреждает о злоупотреблении смежным_find и предпочитает std::transform с бинарными предикатами ... хммм, нужно посмотреть, о чем идет речь - спасибо за вашу помощь. Я ценю это
Взгляните также на std::inner_product
, это может быть применимо к вашей проблеме.
Как предложил cigien в комментариях, вот как это сделать с inner_product
:
auto result = inner_product(
timeStamps.begin() + 1, timeStamps.end(),
timeStamps.begin(), 0ms,
[](auto x, auto y) {return x + y;},
[](auto x, auto y) {return round<milliseconds>(x - y);})
/ (timeStamps.size() - 1);
Спасибо за реализацию, какое действительно элегантное злоупотребление std::inner_product
- спасибо @cigien. Намного более интуитивным, чем злоупотребление std::adjacent_find
или std::transform_reduce
, что я думал об использовании.
Одной из неинтуитивных вещей, которые вы закодировали (о которых, я думаю, следует упомянуть), было использование инициализатора 0 мс. Я боролся с этим, так как мне нужно было иметь начальное значение, которое можно было бы преобразовать в момент времени (начальное значение для аккумулятора). Первоначально я использовал двойное значение 0.0, что привело к примерно 20 страницам ошибок, а фактическая ошибка была спрятана где-то посередине :). Хороший и отличный ответ - который работает !!
теперь, когда я злоупотребляю inner_product, мне было интересно, можете ли вы определить, где что-то идет не так, я пытаюсь рассчитать стандартное отклонение (джиттера пакетов), используя временные метки и частичные результаты (сумма квадратов времени отдельных пакетов - среднее время пакета) посмотрите далеко, я ожидал, что стандартное отклонение около 0 в моей живой демонстрации coliru coliru.stacked-crooked.com/a/4823684c18c05680
Похоже на первую операцию ты (t[i]-t[i] - mean)^2
, когда захочешь (t[i]-t[i-1] - mean)^2
. Я считаю, что первый диапазон должен начинаться с timeStamps.begin()+1
. И вам не хватает деления на N (т.е. (timeStamps.size() - 1)
.
Разве это не будет просто равно
(last - first) / length
? Все остальное отменяется, это телескопическая сумма.