Вы можете использовать full_seq
из tidyr
- он был создан именно для таких задач ( Создать полную последовательность значений в векторе ):
library(tidyr)
library(dplyr)
df %>%
group_by(id, group) %>%
complete(year = full_seq(year, period = 1))
id group year
<fct> <fct> <dbl>
1 a x 2000
2 a x 2001
3 a x 2002
4 a x 2003
5 b y 2003
6 b z 2005
template
все еще кажутся мне чёрной магией. Я успешно использовал специализации шаблона в различных ситуациях (если у меня не было лучшей идеи), но никогда не использовал аргументы шаблона. Итак, я попытался ...
Когда я попробовал coliru (компилятор g++ (GCC) 8.1.0
), я начал с -std=c++11
и вскоре получил несколько ужасных ошибок. К счастью, был также совет перейти к -std=c++17
. Я сделал, и вещи становятся намного лучше сразу. Я грубо вспомнил, что аргументы шаблона не очень хорошо поддерживаются в C ++ 11. Короткий взгляд на cppreference
подтвердил мою правоту:
template < список параметров> имя типа (C ++ 17) | имя класса (необязательно) (1)
шаблон < список параметров> имя типа (C ++ 17) | имя класса (необязательно) = default (2)
шаблон < список параметров> имя типа (C ++ 17) | class ... name (необязательно) (3) (начиная с C ++ 11)
1) Параметр шаблона шаблона с необязательным именем.
blockquote>
2) Параметр шаблона шаблона с необязательным именем и значением по умолчанию.
3) Пакет параметров шаблона шаблона с необязательным именем.
После разъяснения я взял часть кода кода OPs и добавил частичную специализацию для
template
, где я
- оставил первый параметр
- специализированный. второй параметр с
std::stack
.На самом деле, это не сильно отличается от того, как специализируются шаблоны с аргументами типа или значения.
cppreference
есть статья для этой частичной специализации шаблона , но при использовании этого термина в качестве ключевого слова для поиска должно быть много обращений к книгам и учебным пособиям. (Я помню книгу, в которой я когда-то учил шаблоны C ++, упомянул об этом также - конечно.)Итак, вот мой небольшой пример, чтобы продемонстрировать это:
#include <iostream> #include <stack> #include <vector> template<typename T, template <typename...> typename U> class ContainerT { private: U<T> _container; public: ContainerT(): _container() { } ~ContainerT() = default; ContainerT(const ContainerT&) = default; ContainerT& operator=(const ContainerT&) = default; bool empty() const { return _container.empty(); } void push(const T &data) { _container.push_back(data); } T pop() { const T data = _container.back(); _container.pop_back(); return data; } }; template<typename T> class ContainerT<T, std::stack> { private: std::stack<T> _container; public: ContainerT(): _container() { } ~ContainerT() = default; ContainerT(const ContainerT&) = default; ContainerT& operator=(const ContainerT&) = default; bool empty() const { return _container.empty(); } void push(const T &data) { _container.push(data); } T pop() { const T data = _container.top(); _container.pop(); return data; } }; #define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__ int main() { // for std::vector DEBUG(ContainerT<int, std::vector> vec); DEBUG(vec.push(1); vec.push(2); vec.push(3)); DEBUG(while (!vec.empty()) std::cout << vec.pop() << '\n'); // for std::stack DEBUG(ContainerT<int, std::stack> stk); DEBUG(stk.push(1); stk.push(2); stk.push(3)); DEBUG(while (!stk.empty()) std::cout << stk.pop() << '\n'); // done return 0; }
Очевидно, что [1110 ] создан для
ContainerT<int, std::vector>
. В противном случае методpush()
не может быть скомпилирован.Для
ContainerT<int, std::stack>
вместо этого используется специализация. (Опять же, в противном случае методpush()
не может быть скомпилирован.)Вывод:
ContainerT<int, std::vector> vec; vec.push(1); vec.push(2); vec.push(3); while (!vec.empty()) std::cout << vec.pop() << '\n'; 3 2 1 ContainerT<int, std::stack> stk; stk.push(1); stk.push(2); stk.push(3); while (!stk.empty()) std::cout << stk.pop() << '\n'; 3 2 1
Live Demo на колиру kbd>