diff --git a/exercises/00_hello_world/main.cpp b/exercises/00_hello_world/main.cpp index 8866f3c15..eb1a725a7 100644 --- a/exercises/00_hello_world/main.cpp +++ b/exercises/00_hello_world/main.cpp @@ -6,6 +6,6 @@ int main(int argc, char **argv) { // TODO: 在控制台输出 "Hello, InfiniTensor!" 并换行 - std::cout : "Hello, InfiniTensor!" + std::endl; + std::cout<< "Hello, InfiniTensor!" << std::endl; return 0; } diff --git a/exercises/01_variable&add/main.cpp b/exercises/01_variable&add/main.cpp index 5014863fd..fb05c57f3 100644 --- a/exercises/01_variable&add/main.cpp +++ b/exercises/01_variable&add/main.cpp @@ -5,6 +5,7 @@ int main(int argc, char **argv) { // TODO: 补全变量定义并打印加法运算 // x ? + int x = 10; std::cout << x << " + " << x << " = " << x + x << std::endl; return 0; } diff --git a/exercises/02_function/main.cpp b/exercises/02_function/main.cpp index b5eef7f28..603a9ec04 100644 --- a/exercises/02_function/main.cpp +++ b/exercises/02_function/main.cpp @@ -5,6 +5,7 @@ // NOTICE: 补充由内而外读法的机翻解释 // TODO: 在这里声明函数 +int add(int a, int b); int main(int argc, char **argv) { ASSERT(add(123, 456) == 123 + 456, "add(123, 456) should be 123 + 456"); @@ -16,4 +17,5 @@ int main(int argc, char **argv) { int add(int a, int b) { // TODO: 补全函数定义,但不要移动代码行 + return a+b; } diff --git a/exercises/03_argument¶meter/main.cpp b/exercises/03_argument¶meter/main.cpp index 7fb5d3c2f..76136eae6 100644 --- a/exercises/03_argument¶meter/main.cpp +++ b/exercises/03_argument¶meter/main.cpp @@ -8,19 +8,19 @@ void func(int); // TODO: 为下列 ASSERT 填写正确的值 int main(int argc, char **argv) { auto arg = 99; - ASSERT(arg == ?, "arg should be ?"); + ASSERT(arg == 99, "arg should be ?"); std::cout << "befor func call: " << arg << std::endl; func(arg); - ASSERT(arg == ?, "arg should be ?"); + ASSERT(arg == 99, "arg should be ?"); std::cout << "after func call: " << arg << std::endl; return 0; } // TODO: 为下列 ASSERT 填写正确的值 void func(int param) { - ASSERT(param == ?, "param should be ?"); + ASSERT(param == 99, "param should be ?"); std::cout << "befor add: " << param << std::endl; param += 1; - ASSERT(param == ?, "param should be ?"); + ASSERT(param == 100, "param should be ?"); std::cout << "after add: " << param << std::endl; } diff --git a/exercises/04_static/main.cpp b/exercises/04_static/main.cpp index f107762fa..64f417694 100644 --- a/exercises/04_static/main.cpp +++ b/exercises/04_static/main.cpp @@ -10,10 +10,10 @@ static int func(int param) { int main(int argc, char **argv) { // TODO: 将下列 `?` 替换为正确的数字 - ASSERT(func(5) == ?, "static variable value incorrect"); - ASSERT(func(4) == ?, "static variable value incorrect"); - ASSERT(func(3) == ?, "static variable value incorrect"); - ASSERT(func(2) == ?, "static variable value incorrect"); - ASSERT(func(1) == ?, "static variable value incorrect"); + ASSERT(func(5) == 5, "static variable value incorrect"); + ASSERT(func(4) == 6, "static variable value incorrect"); + ASSERT(func(3) == 7, "static variable value incorrect"); + ASSERT(func(2) == 8, "static variable value incorrect"); + ASSERT(func(1) == 9, "static variable value incorrect"); return 0; } diff --git a/exercises/05_constexpr/main.cpp b/exercises/05_constexpr/main.cpp index d1db6c9d8..2f6a5451a 100644 --- a/exercises/05_constexpr/main.cpp +++ b/exercises/05_constexpr/main.cpp @@ -18,8 +18,10 @@ int main(int argc, char **argv) { // TODO: 观察错误信息,修改一处,使代码编译运行 // PS: 编译运行,但是不一定能算出结果…… - constexpr auto ANS_N = 90; - constexpr auto ANS = fibonacci(ANS_N); + constexpr auto ANS_N = 9; + //constexpr auto ANS = fibonacci(ANS_N); + auto ANS = fibonacci(ANS_N); + std::cout << "fibonacci(" << ANS_N << ") = " << ANS << std::endl; return 0; diff --git a/exercises/06_array/main.cpp b/exercises/06_array/main.cpp index 61ed99ec0..bbce324cf 100644 --- a/exercises/06_array/main.cpp +++ b/exercises/06_array/main.cpp @@ -11,13 +11,13 @@ unsigned long long fibonacci(int i) { return 1; default: // TODO: 补全三目表达式缺失的部分 - return ? : (arr[i] = fibonacci(i - 1) + fibonacci(i - 2)); + return arr[i] ? arr[i] : (arr[i] = fibonacci(i - 1) + fibonacci(i - 2)); } } int main(int argc, char **argv) { // TODO: 为此 ASSERT 填写正确的值 - ASSERT(sizeof(arr) == ?, "sizeof array is size of all its elements"); + ASSERT(sizeof(arr) == 720, "sizeof array is size of all its elements"); // ---- 不要修改以下代码 ---- ASSERT(fibonacci(2) == 1, "fibonacci(2) should be 1"); ASSERT(fibonacci(20) == 6765, "fibonacci(20) should be 6765"); diff --git a/exercises/07_loop/main.cpp b/exercises/07_loop/main.cpp index 44fd835cd..ed485308b 100644 --- a/exercises/07_loop/main.cpp +++ b/exercises/07_loop/main.cpp @@ -5,9 +5,11 @@ // READ: 纯函数 static unsigned long long fibonacci(int i) { // TODO: 为缓存设置正确的初始值 - static unsigned long long cache[96], cached; + static unsigned long long cache[96]{0, 1}; + static int cached = 2; + // TODO: 设置正确的循环条件 - for (; false; ++cached) { + for (; cached<=i; ++cached) { cache[cached] = cache[cached - 1] + cache[cached - 2]; } return cache[i]; diff --git a/exercises/08_pointer/main.cpp b/exercises/08_pointer/main.cpp index ba37173f5..622a2fd87 100644 --- a/exercises/08_pointer/main.cpp +++ b/exercises/08_pointer/main.cpp @@ -4,6 +4,17 @@ bool is_fibonacci(int *ptr, int len, int stride) { ASSERT(len >= 3, "`len` should be at least 3"); // TODO: 编写代码判断从 ptr 开始,每 stride 个元素取 1 个元素,组成长度为 n 的数列是否满足 + for (int i = 0; i <= len - 3; i++) + { + int a = ptr[i * stride]; + int b = ptr[(i+1)*stride]; + int c = ptr[(i+2)*stride]; + if (a + b != c) + { + return false; + } + + } // arr[i + 2] = arr[i] + arr[i + 1] return true; } diff --git a/exercises/09_enum&union/main.cpp b/exercises/09_enum&union/main.cpp index 3f2cec768..5779dd314 100644 --- a/exercises/09_enum&union/main.cpp +++ b/exercises/09_enum&union/main.cpp @@ -37,6 +37,7 @@ ColorEnum convert_by_pun(Color c) { TypePun pun; // TODO: 补全类型双关转换 + pun.c = c; return pun.e; } diff --git a/exercises/10_trivial/main.cpp b/exercises/10_trivial/main.cpp index 6ba23e48e..173ebbdd6 100644 --- a/exercises/10_trivial/main.cpp +++ b/exercises/10_trivial/main.cpp @@ -9,8 +9,9 @@ struct FibonacciCache { // TODO: 实现正确的缓存优化斐波那契计算 static unsigned long long fibonacci(FibonacciCache &cache, int i) { - for (; false; ++cached) { - cache[cached] = cache[cached - 1] + cache[cached - 2]; + int cached = cache.cached; + for (cached; cached<=i; ++cached) { + cache.cache[cached] = cache.cache[cached - 1] + cache.cache[cached - 2]; } return cache.cache[i]; } @@ -20,6 +21,9 @@ int main(int argc, char **argv) { // NOTICE: C/C++ 中,读取未初始化的变量(包括结构体变量)是未定义行为 // READ: 初始化的各种写法 FibonacciCache fib; + fib.cache[0] = 0; + fib.cache[1] = 1; + fib.cached = 2; ASSERT(fibonacci(fib, 10) == 55, "fibonacci(10) should be 55"); std::cout << "fibonacci(10) = " << fibonacci(fib, 10) << std::endl; return 0; diff --git a/exercises/11_method/main.cpp b/exercises/11_method/main.cpp index 0e08e0a36..57d04651d 100644 --- a/exercises/11_method/main.cpp +++ b/exercises/11_method/main.cpp @@ -6,7 +6,7 @@ struct Fibonacci { // TODO: 实现正确的缓存优化斐波那契计算 unsigned long long get(int i) { - for (; false; ++cached) { + for (; cached<=i; ++cached) { cache[cached] = cache[cached - 1] + cache[cached - 2]; } return cache[i]; @@ -16,6 +16,9 @@ struct Fibonacci { int main(int argc, char **argv) { // TODO: 初始化缓存结构体,使计算正确 Fibonacci fib; + fib.cache[0] = 0; + fib.cache[1] = 1; + fib.cached = 2; ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; return 0; diff --git a/exercises/12_method_const/main.cpp b/exercises/12_method_const/main.cpp index 5521be4da..65c079be1 100644 --- a/exercises/12_method_const/main.cpp +++ b/exercises/12_method_const/main.cpp @@ -5,7 +5,8 @@ struct Fibonacci { int numbers[11]; // TODO: 修改方法签名和实现,使测试通过 - int get(int i) { + int get(int i) const { + return numbers[i]; } }; diff --git a/exercises/13_class/main.cpp b/exercises/13_class/main.cpp index 9afa98c5b..b6dc868d1 100644 --- a/exercises/13_class/main.cpp +++ b/exercises/13_class/main.cpp @@ -15,10 +15,16 @@ class Fibonacci { public: // TODO: 实现构造器 // Fibonacci() + Fibonacci(const size_t inputCache[16], int cached) { + for (int i = 0; i < 16; ++i) { + cache[i] = inputCache[i]; + } + this->cached = cached; + } // TODO: 实现正确的缓存优化斐波那契计算 size_t get(int i) { - for (; false; ++cached) { + for (; cached<=i; ++cached) { cache[cached] = cache[cached - 1] + cache[cached - 2]; } return cache[i]; @@ -28,7 +34,8 @@ class Fibonacci { int main(int argc, char **argv) { // 现在类型拥有无参构造器,声明时会直接调用。 // 这个写法不再是未定义行为了。 - Fibonacci fib; + size_t cache[16]{0, 1}; + Fibonacci fib(cache, 2); ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; return 0; diff --git a/exercises/14_class_destruct/main.cpp b/exercises/14_class_destruct/main.cpp index 42150e8ca..f00bcb789 100644 --- a/exercises/14_class_destruct/main.cpp +++ b/exercises/14_class_destruct/main.cpp @@ -11,20 +11,29 @@ class DynFibonacci { public: // TODO: 实现动态设置容量的构造器 - DynFibonacci(int capacity): cache(new ?), cached(?) {} + DynFibonacci(int capacity) { + cache = new size_t[capacity]; + cache[0] = 0; + cache[1] = 1; + cached = 2; + } // TODO: 实现析构器,释放缓存空间 ~DynFibonacci(); // TODO: 实现正确的缓存优化斐波那契计算 size_t get(int i) { - for (; false; ++cached) { + for (; cached <= i; ++cached) { cache[cached] = cache[cached - 1] + cache[cached - 2]; } return cache[i]; } }; +DynFibonacci ::~DynFibonacci() { + delete[] cache; +} + int main(int argc, char **argv) { DynFibonacci fib(12); ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); diff --git a/exercises/15_class_clone/main.cpp b/exercises/15_class_clone/main.cpp index f74b70391..38737265a 100644 --- a/exercises/15_class_clone/main.cpp +++ b/exercises/15_class_clone/main.cpp @@ -10,17 +10,26 @@ class DynFibonacci { public: // TODO: 实现动态设置容量的构造器 - DynFibonacci(int capacity): cache(new ?), cached(?) {} + DynFibonacci(int capacity) : cache(new size_t[capacity]), cached(2) { + cache[0] = 0; + cache[1] = 1; + } // TODO: 实现复制构造器 - DynFibonacci(DynFibonacci const &) = delete; + DynFibonacci(DynFibonacci const &c) { + this->cache = new size_t[c.cached]; + this->cached = c.cached; + for (int i = 0; i < c.cached; i++) { + this->cache[i] = c.cache[i]; + } + } // TODO: 实现析构器,释放缓存空间 ~DynFibonacci(); // TODO: 实现正确的缓存优化斐波那契计算 size_t get(int i) { - for (; false; ++cached) { + for (; cached <= i; ++cached) { cache[cached] = cache[cached - 1] + cache[cached - 2]; } return cache[i]; @@ -38,6 +47,11 @@ class DynFibonacci { } }; + +DynFibonacci::~DynFibonacci() { + delete[] cache; +} + int main(int argc, char **argv) { DynFibonacci fib(12); ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); diff --git a/exercises/16_class_move/main.cpp b/exercises/16_class_move/main.cpp index 8d2c421da..2269a80b8 100644 --- a/exercises/16_class_move/main.cpp +++ b/exercises/16_class_move/main.cpp @@ -15,21 +15,45 @@ class DynFibonacci { public: // TODO: 实现动态设置容量的构造器 - DynFibonacci(int capacity): cache(new ?), cached(?) {} + DynFibonacci(int capacity): cache(new size_t[capacity]), cached(2) + { + cache[0] = 0; + cache[1] = 1; + } // TODO: 实现移动构造器 - DynFibonacci(DynFibonacci &&) noexcept = delete; + DynFibonacci(DynFibonacci &&c) noexcept : cache(c.cache), cached(c.cached) + { + c.cache =nullptr; + c.cached = 0; + } // TODO: 实现移动赋值 // NOTICE: ⚠ 注意移动到自身问题 ⚠ - DynFibonacci &operator=(DynFibonacci &&) noexcept = delete; + DynFibonacci& operator=(DynFibonacci&& c) noexcept + { + if (this != &c) + { + delete[] cache; + cache = c.cache; + cached = c.cached; + + c.cache = nullptr; + c.cached = 0; + } + return *this; + } // TODO: 实现析构器,释放缓存空间 - ~DynFibonacci(); + ~DynFibonacci() + { + delete[]cache; + cache = nullptr; + } // TODO: 实现正确的缓存优化斐波那契计算 size_t operator[](int i) { - for (; false; ++cached) { + for (; cached<=i; ++cached) { cache[cached] = cache[cached - 1] + cache[cached - 2]; } return cache[i]; diff --git a/exercises/17_class_derive/main.cpp b/exercises/17_class_derive/main.cpp index 819ae72fc..93014cfa6 100644 --- a/exercises/17_class_derive/main.cpp +++ b/exercises/17_class_derive/main.cpp @@ -50,9 +50,9 @@ int main(int argc, char **argv) { B b = B(3); // TODO: 补全三个类型的大小 - static_assert(sizeof(X) == ?, "There is an int in X"); - static_assert(sizeof(A) == ?, "There is an int in A"); - static_assert(sizeof(B) == ?, "B is an A with an X"); + static_assert(sizeof(X) == sizeof(int), "There is an int in X"); + static_assert(sizeof(A) == sizeof(int), "There is an int in A"); + static_assert(sizeof(B) == 2*sizeof(int), "B is an A with an X"); i = 0; std::cout << std::endl diff --git a/exercises/18_class_virtual/main.cpp b/exercises/18_class_virtual/main.cpp index ac6382413..344709e4c 100644 --- a/exercises/18_class_virtual/main.cpp +++ b/exercises/18_class_virtual/main.cpp @@ -42,38 +42,38 @@ int main(int argc, char **argv) { C c; D d; - ASSERT(a.virtual_name() == '?', MSG); - ASSERT(b.virtual_name() == '?', MSG); - ASSERT(c.virtual_name() == '?', MSG); - ASSERT(d.virtual_name() == '?', MSG); - ASSERT(a.direct_name() == '?', MSG); - ASSERT(b.direct_name() == '?', MSG); - ASSERT(c.direct_name() == '?', MSG); - ASSERT(d.direct_name() == '?', MSG); + ASSERT(a.virtual_name() == 'A', MSG); + ASSERT(b.virtual_name() == 'B', MSG); + ASSERT(c.virtual_name() == 'C', MSG); + ASSERT(d.virtual_name() == 'C', MSG); + ASSERT(a.direct_name() == 'A', MSG); + ASSERT(b.direct_name() == 'B', MSG); + ASSERT(c.direct_name() == 'C', MSG); + ASSERT(d.direct_name() == 'D', MSG); A &rab = b; B &rbc = c; C &rcd = d; - ASSERT(rab.virtual_name() == '?', MSG); - ASSERT(rbc.virtual_name() == '?', MSG); - ASSERT(rcd.virtual_name() == '?', MSG); - ASSERT(rab.direct_name() == '?', MSG); - ASSERT(rbc.direct_name() == '?', MSG); - ASSERT(rcd.direct_name() == '?', MSG); + ASSERT(rab.virtual_name() == 'B', MSG); + ASSERT(rbc.virtual_name() == 'C', MSG); + ASSERT(rcd.virtual_name() == 'C', MSG); + ASSERT(rab.direct_name() == 'A', MSG); + ASSERT(rbc.direct_name() == 'B', MSG); + ASSERT(rcd.direct_name() == 'C', MSG); A &rac = c; B &rbd = d; - ASSERT(rac.virtual_name() == '?', MSG); - ASSERT(rbd.virtual_name() == '?', MSG); - ASSERT(rac.direct_name() == '?', MSG); - ASSERT(rbd.direct_name() == '?', MSG); + ASSERT(rac.virtual_name() == 'C', MSG); + ASSERT(rbd.virtual_name() == 'C', MSG); + ASSERT(rac.direct_name() == 'A', MSG); + ASSERT(rbd.direct_name() == 'B', MSG); A &rad = d; - ASSERT(rad.virtual_name() == '?', MSG); - ASSERT(rad.direct_name() == '?', MSG); + ASSERT(rad.virtual_name() == 'C', MSG); + ASSERT(rad.direct_name() == 'A', MSG); return 0; } diff --git a/exercises/19_class_virtual_destruct/main.cpp b/exercises/19_class_virtual_destruct/main.cpp index cdd54f74f..8d66e8edf 100644 --- a/exercises/19_class_virtual_destruct/main.cpp +++ b/exercises/19_class_virtual_destruct/main.cpp @@ -3,14 +3,15 @@ // READ: 静态字段 // READ: 虚析构函数 + struct A { // TODO: 正确初始化静态字段 - static int num_a = 0; + static int num_a; A() { ++num_a; } - ~A() { + virtual ~A() { --num_a; } @@ -20,12 +21,12 @@ struct A { }; struct B final : public A { // TODO: 正确初始化静态字段 - static int num_b = 0; + static int num_b; B() { ++num_b; } - ~B() { + ~B() override { --num_b; } @@ -34,13 +35,16 @@ struct B final : public A { } }; +int A::num_a = 0; +int B::num_b = 0; + int main(int argc, char **argv) { auto a = new A; auto b = new B; - ASSERT(A::num_a == ?, "Fill in the correct value for A::num_a"); - ASSERT(B::num_b == ?, "Fill in the correct value for B::num_b"); - ASSERT(a->name() == '?', "Fill in the correct value for a->name()"); - ASSERT(b->name() == '?', "Fill in the correct value for b->name()"); + ASSERT(A::num_a == 2, "Fill in the correct value for A::num_a"); + ASSERT(B::num_b == 1, "Fill in the correct value for B::num_b"); + ASSERT(a->name() == 'A', "Fill in the correct value for a->name()"); + ASSERT(b->name() == 'B', "Fill in the correct value for b->name()"); delete a; delete b; @@ -48,13 +52,14 @@ int main(int argc, char **argv) { ASSERT(B::num_b == 0, "Every B was destroyed"); A *ab = new B;// 派生类指针可以随意转换为基类指针 - ASSERT(A::num_a == ?, "Fill in the correct value for A::num_a"); - ASSERT(B::num_b == ?, "Fill in the correct value for B::num_b"); - ASSERT(ab->name() == '?', "Fill in the correct value for ab->name()"); + ASSERT(A::num_a == 1, "Fill in the correct value for A::num_a"); + ASSERT(B::num_b == 1, "Fill in the correct value for B::num_b"); + ASSERT(ab->name() == 'B', "Fill in the correct value for ab->name()"); // TODO: 基类指针无法随意转换为派生类指针,补全正确的转换语句 - B &bb = *ab; - ASSERT(bb.name() == '?', "Fill in the correct value for bb->name()"); + //B &bb = *ab; + B &bb = dynamic_cast(*ab); + ASSERT(bb.name() == 'B', "Fill in the correct value for bb->name()"); // TODO: ---- 以下代码不要修改,通过改正类定义解决编译问题 ---- delete ab;// 通过指针可以删除指向的对象,即使是多态对象 diff --git a/exercises/20_function_template/main.cpp b/exercises/20_function_template/main.cpp index cb6d978d3..7196e8930 100644 --- a/exercises/20_function_template/main.cpp +++ b/exercises/20_function_template/main.cpp @@ -2,7 +2,8 @@ // READ: 函数模板 // TODO: 将这个函数模板化 -int plus(int a, int b) { +template +T plus(T a, T b) { return a + b; } @@ -14,7 +15,7 @@ int main(int argc, char **argv) { ASSERT(plus(1.25f, 2.5f) == 3.75f, "Plus two float"); ASSERT(plus(1.25, 2.5) == 3.75, "Plus two double"); // TODO: 修改判断条件使测试通过 - ASSERT(plus(0.1, 0.2) == 0.3, "How to make this pass?"); + ASSERT(std::abs(plus(0.1, 0.2) - 0.3) < 1e-9, "How to make this pass?"); return 0; } diff --git a/exercises/21_runtime_datatype/main.cpp b/exercises/21_runtime_datatype/main.cpp index 9c4bf376a..153ab5b2d 100644 --- a/exercises/21_runtime_datatype/main.cpp +++ b/exercises/21_runtime_datatype/main.cpp @@ -18,13 +18,22 @@ struct TaggedUnion { }; // TODO: 将这个函数模板化用于 sigmoid_dyn -float sigmoid(float x) { +template +T sigmoid(T x) { return 1 / (1 + std::exp(-x)); } TaggedUnion sigmoid_dyn(TaggedUnion x) { TaggedUnion ans{x.type}; // TODO: 根据 type 调用 sigmoid + if (DataType::Float == x.type) + { + ans.f = sigmoid(x.f); + } + else + { + ans.d = sigmoid(x.d); + } return ans; } diff --git a/exercises/22_class_template/main.cpp b/exercises/22_class_template/main.cpp index d4985d904..890d7cb7c 100644 --- a/exercises/22_class_template/main.cpp +++ b/exercises/22_class_template/main.cpp @@ -1,8 +1,12 @@ #include "../exercise.h" #include // READ: 类模板 +#include + + +template +// Tensor4D(const unsigned int[4], const T *) -> Tensor4D; -template struct Tensor4D { unsigned int shape[4]; T *data; @@ -10,6 +14,10 @@ struct Tensor4D { Tensor4D(unsigned int const shape_[4], T const *data_) { unsigned int size = 1; // TODO: 填入正确的 shape 并计算 size + for (int i = 0; i < 4; ++i) { + shape[i] = shape_[i]; + size *= shape[i]; + } data = new T[size]; std::memcpy(data, data_, size * sizeof(T)); } @@ -28,6 +36,36 @@ struct Tensor4D { // 则 `this` 与 `others` 相加时,3 个形状为 `[1, 2, 1, 4]` 的子张量各自与 `others` 对应项相加。 Tensor4D &operator+=(Tensor4D const &others) { // TODO: 实现单向广播的加法 + unsigned int total = 1; + for (int i = 0; i < 4; ++i) { + // others维度要么等于当前维度,要么为1(广播) + assert((others.shape[i] == shape[i]) || (others.shape[i] == 1)); + total *= shape[i]; + } + + for (unsigned int i = 0; i < total; ++i) { + // 将一维索引i解码成4维索引 + unsigned int index[4]; + unsigned int stride = total; + unsigned int offset = i; + for (int d = 0; d < 4; ++d) { + stride /= shape[d]; + index[d] = offset / stride; + offset %= stride; + } + + // 计算others中对应的广播后索引 + unsigned int other_index = 0; + unsigned int other_stride = 1; + for (int d = 3; d >= 0; --d) { + unsigned int idx = (others.shape[d] == 1) ? 0 : index[d]; + other_index += idx * other_stride; + other_stride *= others.shape[d]; + } + + data[i] += others.data[other_index]; + } + return *this; } }; diff --git a/exercises/23_template_const/main.cpp b/exercises/23_template_const/main.cpp index e0105e168..f2f49c3d1 100644 --- a/exercises/23_template_const/main.cpp +++ b/exercises/23_template_const/main.cpp @@ -11,6 +11,10 @@ struct Tensor { Tensor(unsigned int const shape_[N]) { unsigned int size = 1; // TODO: 填入正确的 shape 并计算 size + for (unsigned int i = 0; i < N; ++i) { + shape[i] = shape_[i]; + size *= shape[i]; + } data = new T[size]; std::memset(data, 0, size * sizeof(T)); } @@ -20,7 +24,13 @@ struct Tensor { // 为了保持简单,禁止复制和移动 Tensor(Tensor const &) = delete; - Tensor(Tensor &&) noexcept = delete; + //Tensor(Tensor&&) noexcept = delete; + Tensor(Tensor &&other) noexcept { + std::memcpy(shape, other.shape, sizeof(shape)); + data = other.data; + other.data = nullptr;// 转移所有权 + } + T &operator[](unsigned int const indices[N]) { return data[data_index(indices)]; @@ -32,10 +42,18 @@ struct Tensor { private: unsigned int data_index(unsigned int const indices[N]) const { unsigned int index = 0; + unsigned int stride[N]; + stride[N - 1] = 1; + for (int i = N - 2; i >= 0; --i) { + stride[i] = stride[i + 1] * shape[i + 1]; + } + + // 再正序遍历,用 stride 乘索引累加 for (unsigned int i = 0; i < N; ++i) { ASSERT(indices[i] < shape[i], "Invalid index"); - // TODO: 计算 index + index += indices[i] * stride[i]; } + return index; } }; diff --git a/exercises/24_std_array/main.cpp b/exercises/24_std_array/main.cpp index c29718d9d..8ac430609 100644 --- a/exercises/24_std_array/main.cpp +++ b/exercises/24_std_array/main.cpp @@ -8,21 +8,22 @@ int main(int argc, char **argv) { { std::array arr{{1, 2, 3, 4, 5}}; - ASSERT(arr.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(arr) == ?, "Fill in the correct value."); + ASSERT(arr.size() == 5, "Fill in the correct value."); + ASSERT(sizeof(arr) == 20, "Fill in the correct value."); int ans[]{1, 2, 3, 4, 5}; - ASSERT(std::memcmp(arr.?, ans, ?) == 0, "Fill in the correct values."); + ASSERT(std::memcmp(arr.data(), ans, sizeof(ans)) == 0, "Fill in the correct values."); } { std::array arr; - ASSERT(arr.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(arr) == ?, "Fill in the correct value."); + ASSERT(arr.size() == 8, "Fill in the correct value."); + ASSERT(sizeof(arr) == 64, "Fill in the correct value."); } { std::array arr{"Hello, InfiniTensor!"}; - ASSERT(arr.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(arr) == ?, "Fill in the correct value."); - ASSERT(std::strcmp(arr.?, "Hello, InfiniTensor!") == 0, "Fill in the correct value."); + ASSERT(arr.size() == 21, "Fill in the correct value."); + //std::cout << sizeof(char) << std::endl; + ASSERT(sizeof(arr) == 21, "Fill in the correct value."); + ASSERT(std::strcmp(arr.data(), "Hello, InfiniTensor!") == 0, "Fill in the correct value."); } return 0; } diff --git a/exercises/25_std_vector/main.cpp b/exercises/25_std_vector/main.cpp index f9e41bb78..3872b36d5 100644 --- a/exercises/25_std_vector/main.cpp +++ b/exercises/25_std_vector/main.cpp @@ -5,84 +5,84 @@ // READ: std::vector // TODO: 将下列 `?` 替换为正确的代码 -int main(int argc, char **argv) { +int main(int argc, char** argv) { { - std::vector vec{1, 2, 3, 4, 5}; - ASSERT(vec.size() == ?, "Fill in the correct value."); + std::vector vec{ 1, 2, 3, 4, 5 }; + ASSERT(vec.size() == 5, "Fill in the correct value."); // THINK: `std::vector` 的大小是什么意思?与什么有关? - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); - int ans[]{1, 2, 3, 4, 5}; - ASSERT(std::memcmp(vec.?, ans, sizeof(ans)) == 0, "Fill in the correct values."); + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); + int ans[]{ 1, 2, 3, 4, 5 }; + ASSERT(std::memcmp(vec.data(), ans, sizeof(ans)) == 0, "Fill in the correct values."); } { - std::vector vec{1, 2, 3, 4, 5}; + std::vector vec{ 1, 2, 3, 4, 5 }; { - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); - double ans[]{1, 2, 3, 4, 5}; - ASSERT(std::memcmp(vec.?, ans, sizeof(ans)) == 0, "Fill in the correct values."); + ASSERT(vec.size() == 5, "Fill in the correct value."); + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); + double ans[]{ 1, 2, 3, 4, 5 }; + ASSERT(std::memcmp(vec.data(), ans, sizeof(ans)) == 0, "Fill in the correct values."); } { vec.push_back(6); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); + ASSERT(vec.size() == 6, "Fill in the correct value."); + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); vec.pop_back(); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); + ASSERT(vec.size() == 5, "Fill in the correct value."); + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); } { vec[4] = 6; - ASSERT(vec[0] == ?, "Fill in the correct value."); - ASSERT(vec[1] == ?, "Fill in the correct value."); - ASSERT(vec[2] == ?, "Fill in the correct value."); - ASSERT(vec[3] == ?, "Fill in the correct value."); - ASSERT(vec[4] == ?, "Fill in the correct value."); + ASSERT(vec[0] == 1, "Fill in the correct value."); + ASSERT(vec[1] == 2, "Fill in the correct value."); + ASSERT(vec[2] == 3, "Fill in the correct value."); + ASSERT(vec[3] == 4, "Fill in the correct value."); + ASSERT(vec[4] == 6, "Fill in the correct value."); } { // THINK: `std::vector` 插入删除的时间复杂度是什么? - vec.insert(?, 1.5); + vec.insert(vec.begin() + 1, 1.5); ASSERT((vec == std::vector{1, 1.5, 2, 3, 4, 6}), "Make this assertion pass."); - vec.erase(?); + vec.erase(vec.begin() + 3); ASSERT((vec == std::vector{1, 1.5, 2, 4, 6}), "Make this assertion pass."); } { vec.shrink_to_fit(); - ASSERT(vec.capacity() == ?, "Fill in the correct value."); + ASSERT(vec.capacity() == 5, "Fill in the correct value."); vec.clear(); ASSERT(vec.empty(), "`vec` is empty now."); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(vec.capacity() == ?, "Fill in the correct value."); + ASSERT(vec.size() == 0, "Fill in the correct value."); + ASSERT(vec.capacity() == 5, "Fill in the correct value."); } } { - std::vector vec(?, ?); // TODO: 调用正确的构造函数 + std::vector vec(48, 'z');// TODO: 调用正确的构造函数 ASSERT(vec[0] == 'z', "Make this assertion pass."); ASSERT(vec[47] == 'z', "Make this assertion pass."); ASSERT(vec.size() == 48, "Make this assertion pass."); - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); { auto capacity = vec.capacity(); vec.resize(16); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(vec.capacity() == ?, "Fill in a correct identifier."); + ASSERT(vec.size() == 16, "Fill in the correct value."); + ASSERT(vec.capacity() == capacity, "Fill in a correct identifier."); } { vec.reserve(256); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(vec.capacity() == ?, "Fill in the correct value."); + ASSERT(vec.size() == 16, "Fill in the correct value."); + ASSERT(vec.capacity() == 256, "Fill in the correct value."); } { vec.push_back('a'); vec.push_back('b'); vec.push_back('c'); vec.push_back('d'); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(vec.capacity() == ?, "Fill in the correct value."); - ASSERT(vec[15] == ?, "Fill in the correct value."); - ASSERT(vec[?] == 'a', "Fill in the correct value."); - ASSERT(vec[?] == 'b', "Fill in the correct value."); - ASSERT(vec[?] == 'c', "Fill in the correct value."); - ASSERT(vec[?] == 'd', "Fill in the correct value."); + ASSERT(vec.size() == 20, "Fill in the correct value."); + ASSERT(vec.capacity() == 256, "Fill in the correct value."); + ASSERT(vec[15] == 'z', "Fill in the correct value."); + ASSERT(vec[16] == 'a', "Fill in the correct value."); + ASSERT(vec[17] == 'b', "Fill in the correct value."); + ASSERT(vec[18] == 'c', "Fill in the correct value."); + ASSERT(vec[19] == 'd', "Fill in the correct value."); } } diff --git a/exercises/26_std_vector_bool/main.cpp b/exercises/26_std_vector_bool/main.cpp index b4ab4f9c4..972a80b08 100644 --- a/exercises/26_std_vector_bool/main.cpp +++ b/exercises/26_std_vector_bool/main.cpp @@ -6,29 +6,29 @@ // TODO: 将下列 `?` 替换为正确的代码 int main(int argc, char **argv) { - std::vector vec(?, ?);// TODO: 正确调用构造函数 + std::vector vec(100, true);// TODO: 正确调用构造函数 ASSERT(vec[0], "Make this assertion pass."); ASSERT(vec[99], "Make this assertion pass."); ASSERT(vec.size() == 100, "Make this assertion pass."); // NOTICE: 平台相关!注意 CI:Ubuntu 上的值。 std::cout << "sizeof(std::vector) = " << sizeof(std::vector) << std::endl; - ASSERT(sizeof(vec) == ?, "Fill in the correct value."); + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); { vec[20] = false; - ASSERT(?vec[20], "Fill in `vec[20]` or `!vec[20]`."); + ASSERT(!vec[20], "Fill in `vec[20]` or `!vec[20]`."); } { vec.push_back(false); - ASSERT(vec.size() == ?, "Fill in the correct value."); - ASSERT(?vec[100], "Fill in `vec[100]` or `!vec[100]`."); + ASSERT(vec.size() == 101, "Fill in the correct value."); + ASSERT(!vec[100], "Fill in `vec[100]` or `!vec[100]`."); } { auto ref = vec[30]; - ASSERT(?ref, "Fill in `ref` or `!ref`"); + ASSERT(ref, "Fill in `ref` or `!ref`"); ref = false; - ASSERT(?ref, "Fill in `ref` or `!ref`"); + ASSERT(!ref, "Fill in `ref` or `!ref`"); // THINK: WHAT and WHY? - ASSERT(?vec[30], "Fill in `vec[30]` or `!vec[30]`."); + ASSERT(!vec[30], "Fill in `vec[30]` or `!vec[30]`."); } return 0; } diff --git a/exercises/27_strides/main.cpp b/exercises/27_strides/main.cpp index baceaf2a9..13feaf5ae 100644 --- a/exercises/27_strides/main.cpp +++ b/exercises/27_strides/main.cpp @@ -18,6 +18,13 @@ std::vector strides(std::vector const &shape) { // TODO: 完成函数体,根据张量形状计算张量连续存储时的步长。 // READ: 逆向迭代器 std::vector::rbegin // 使用逆向迭代器可能可以简化代码 + if (shape.empty()) return strides; + + strides.back() = 1;// 最后一维步长为1 + for (int i = (int) shape.size() - 2; i >= 0; --i) { + strides[i] = shape[i + 1] * strides[i + 1]; + } + return strides; } diff --git a/exercises/28_std_string/main.cpp b/exercises/28_std_string/main.cpp index d8b276274..5fcfdf31d 100644 --- a/exercises/28_std_string/main.cpp +++ b/exercises/28_std_string/main.cpp @@ -10,9 +10,9 @@ int main(int argc, char **argv) { auto world = "world"; // READ: `decltype` 表达式 // READ: `std::is_same_v` 元编程判别 - ASSERT((std::is_same_v), "Fill in the missing type."); - ASSERT((std::is_same_v), "Fill in the missing type."); + ASSERT((std::is_same_v), "Fill in the missing type."); + ASSERT((std::is_same_v), "Fill in the missing type."); // TODO: 将 `?` 替换为正确的字符串 - ASSERT(hello + ", " + world + '!' == "?", "Fill in the missing string."); + ASSERT(hello + ", " + world + '!' == "Hello, world!", "Fill in the missing string."); return 0; } diff --git a/exercises/29_std_map/main.cpp b/exercises/29_std_map/main.cpp index fcccca347..68122a5fc 100644 --- a/exercises/29_std_map/main.cpp +++ b/exercises/29_std_map/main.cpp @@ -7,11 +7,13 @@ template bool key_exists(std::map const &map, k const &key) { // TODO: 实现函数 + return map.find(key) != map.end(); } template void set(std::map &map, k key, v value) { // TODO: 实现函数 + map[key] = value; } // ---- 不要修改以下代码 ---- diff --git a/exercises/30_std_unique_ptr/main.cpp b/exercises/30_std_unique_ptr/main.cpp index 9b98b5794..d9489ac7b 100644 --- a/exercises/30_std_unique_ptr/main.cpp +++ b/exercises/30_std_unique_ptr/main.cpp @@ -44,17 +44,19 @@ int main(int argc, char **argv) { forward(drop(reset(forward(forward(reset(nullptr)))))); problems[1] = std::move(RECORDS); + drop(drop(reset(drop(reset(reset(nullptr)))))); problems[2] = std::move(RECORDS); + // ---- 不要修改以上代码 ---- std::vector answers[]{ {"fd"}, // TODO: 分析 problems[1] 中资源的生命周期,将记录填入 `std::vector` // NOTICE: 此题结果依赖对象析构逻辑,平台相关,提交时以 CI 实际运行平台为准 - {"", "", "", "", "", "", "", ""}, - {"", "", "", "", "", "", "", ""}, + {"ffr", "d"}, + {"r", "d", "d"}, }; // ---- 不要修改以下代码 ---- diff --git a/exercises/31_std_shared_ptr/main.cpp b/exercises/31_std_shared_ptr/main.cpp index febbbcc6f..d35134457 100644 --- a/exercises/31_std_shared_ptr/main.cpp +++ b/exercises/31_std_shared_ptr/main.cpp @@ -10,36 +10,36 @@ int main(int argc, char **argv) { std::shared_ptr ptrs[]{shared, shared, shared}; std::weak_ptr observer = shared; - ASSERT(observer.use_count() == ?, ""); + ASSERT(observer.use_count() == 4, ""); ptrs[0].reset(); - ASSERT(observer.use_count() == ?, ""); + ASSERT(observer.use_count() == 3, ""); ptrs[1] = nullptr; - ASSERT(observer.use_count() == ?, ""); + ASSERT(observer.use_count() == 2, ""); ptrs[2] = std::make_shared(*shared); - ASSERT(observer.use_count() == ?, ""); + ASSERT(observer.use_count() == 1, ""); ptrs[0] = shared; ptrs[1] = shared; ptrs[2] = std::move(shared); - ASSERT(observer.use_count() == ?, ""); + ASSERT(observer.use_count() == 3, ""); std::ignore = std::move(ptrs[0]); ptrs[1] = std::move(ptrs[1]); ptrs[1] = std::move(ptrs[2]); - ASSERT(observer.use_count() == ?, ""); + ASSERT(observer.use_count() == 2, ""); shared = observer.lock(); - ASSERT(observer.use_count() == ?, ""); + ASSERT(observer.use_count() == 3, ""); shared = nullptr; for (auto &ptr : ptrs) ptr = nullptr; - ASSERT(observer.use_count() == ?, ""); + ASSERT(observer.use_count() == 0, ""); shared = observer.lock(); - ASSERT(observer.use_count() == ?, ""); + ASSERT(observer.use_count() == 0, ""); return 0; } diff --git a/exercises/32_std_transform/main.cpp b/exercises/32_std_transform/main.cpp index f4dc25a5c..ad386804c 100644 --- a/exercises/32_std_transform/main.cpp +++ b/exercises/32_std_transform/main.cpp @@ -6,10 +6,16 @@ // READ: `std::transform` // READ: `std::vector::begin` + + int main(int argc, char **argv) { std::vector val{8, 13, 21, 34, 55}; // TODO: 调用 `std::transform`,将 `v` 中的每个元素乘以 2,并转换为字符串,存入 `ans` // std::vector ans + std::vector ans(val.size()); + std::transform(val.begin(), val.end(), ans.begin(), [](int x) { + return std::to_string(x * 2); + }); ASSERT(ans.size() == val.size(), "ans size should be equal to val size"); ASSERT(ans[0] == "16", "ans[0] should be 16"); ASSERT(ans[1] == "26", "ans[1] should be 26"); diff --git a/exercises/33_std_accumulate/main.cpp b/exercises/33_std_accumulate/main.cpp index 6326929d5..53e9af330 100644 --- a/exercises/33_std_accumulate/main.cpp +++ b/exercises/33_std_accumulate/main.cpp @@ -12,6 +12,8 @@ int main(int argc, char **argv) { // - 连续存储; // 的张量占用的字节数 // int size = + int size = sizeof(DataType) * std::accumulate( + shape, shape + sizeof(shape) / sizeof(int), 1, std::multiplies()); ASSERT(size == 602112, "4x1x3x224x224 = 602112"); return 0; } diff --git a/exercises/exercises/00_hello_world/main.cpp b/exercises/exercises/00_hello_world/main.cpp new file mode 100644 index 000000000..eb1a725a7 --- /dev/null +++ b/exercises/exercises/00_hello_world/main.cpp @@ -0,0 +1,11 @@ +#include "../exercise.h" + +// READ: std streams +// READ: 流修饰符 +// READ: format in cxx20 + +int main(int argc, char **argv) { + // TODO: 在控制台输出 "Hello, InfiniTensor!" 并换行 + std::cout<< "Hello, InfiniTensor!" << std::endl; + return 0; +} diff --git a/exercises/exercises/01_variable&add/main.cpp b/exercises/exercises/01_variable&add/main.cpp new file mode 100644 index 000000000..fb05c57f3 --- /dev/null +++ b/exercises/exercises/01_variable&add/main.cpp @@ -0,0 +1,11 @@ +#include "../exercise.h" + +// READ: 运算符 + +int main(int argc, char **argv) { + // TODO: 补全变量定义并打印加法运算 + // x ? + int x = 10; + std::cout << x << " + " << x << " = " << x + x << std::endl; + return 0; +} diff --git a/exercises/exercises/02_function/main.cpp b/exercises/exercises/02_function/main.cpp new file mode 100644 index 000000000..603a9ec04 --- /dev/null +++ b/exercises/exercises/02_function/main.cpp @@ -0,0 +1,21 @@ +#include "../exercise.h" + +// READ: 声明 +// NOTICE: cppreference 中的示例中指出了复杂声明的解读法,建议认真阅读。 +// NOTICE: 补充由内而外读法的机翻解释 + +// TODO: 在这里声明函数 +int add(int a, int b); + +int main(int argc, char **argv) { + ASSERT(add(123, 456) == 123 + 456, "add(123, 456) should be 123 + 456"); + + auto x = 1, y = 2; + std::cout << x << " + " << y << " = " << add(x, y) << std::endl; + return 0; +} + +int add(int a, int b) { + // TODO: 补全函数定义,但不要移动代码行 + return a+b; +} diff --git a/exercises/exercises/03_argument¶meter/main.cpp b/exercises/exercises/03_argument¶meter/main.cpp new file mode 100644 index 000000000..76136eae6 --- /dev/null +++ b/exercises/exercises/03_argument¶meter/main.cpp @@ -0,0 +1,26 @@ +#include "../exercise.h" + +// READ: +// THINK: 参数都有哪些传递方式?如何选择传递方式? + +void func(int); + +// TODO: 为下列 ASSERT 填写正确的值 +int main(int argc, char **argv) { + auto arg = 99; + ASSERT(arg == 99, "arg should be ?"); + std::cout << "befor func call: " << arg << std::endl; + func(arg); + ASSERT(arg == 99, "arg should be ?"); + std::cout << "after func call: " << arg << std::endl; + return 0; +} + +// TODO: 为下列 ASSERT 填写正确的值 +void func(int param) { + ASSERT(param == 99, "param should be ?"); + std::cout << "befor add: " << param << std::endl; + param += 1; + ASSERT(param == 100, "param should be ?"); + std::cout << "after add: " << param << std::endl; +} diff --git a/exercises/exercises/04_static/main.cpp b/exercises/exercises/04_static/main.cpp new file mode 100644 index 000000000..64f417694 --- /dev/null +++ b/exercises/exercises/04_static/main.cpp @@ -0,0 +1,19 @@ +#include "../exercise.h" + +// READ: `static` 关键字 +// THINK: 这个函数的两个 `static` 各自的作用是什么? +static int func(int param) { + static int static_ = param; + // std::cout << "static_ = " << static_ << std::endl; + return static_++; +} + +int main(int argc, char **argv) { + // TODO: 将下列 `?` 替换为正确的数字 + ASSERT(func(5) == 5, "static variable value incorrect"); + ASSERT(func(4) == 6, "static variable value incorrect"); + ASSERT(func(3) == 7, "static variable value incorrect"); + ASSERT(func(2) == 8, "static variable value incorrect"); + ASSERT(func(1) == 9, "static variable value incorrect"); + return 0; +} diff --git a/exercises/exercises/05_constexpr/main.cpp b/exercises/exercises/05_constexpr/main.cpp new file mode 100644 index 000000000..2f6a5451a --- /dev/null +++ b/exercises/exercises/05_constexpr/main.cpp @@ -0,0 +1,28 @@ +#include "../exercise.h" + +constexpr unsigned long long fibonacci(int i) { + switch (i) { + case 0: + return 0; + case 1: + return 1; + default: + return fibonacci(i - 1) + fibonacci(i - 2); + } +} + +int main(int argc, char **argv) { + constexpr auto FIB20 = fibonacci(20); + ASSERT(FIB20 == 6765, "fibonacci(20) should be 6765"); + std::cout << "fibonacci(20) = " << FIB20 << std::endl; + + // TODO: 观察错误信息,修改一处,使代码编译运行 + // PS: 编译运行,但是不一定能算出结果…… + constexpr auto ANS_N = 9; + //constexpr auto ANS = fibonacci(ANS_N); + auto ANS = fibonacci(ANS_N); + + std::cout << "fibonacci(" << ANS_N << ") = " << ANS << std::endl; + + return 0; +} diff --git a/exercises/exercises/06_array/main.cpp b/exercises/exercises/06_array/main.cpp new file mode 100644 index 000000000..bbce324cf --- /dev/null +++ b/exercises/exercises/06_array/main.cpp @@ -0,0 +1,26 @@ +#include "../exercise.h" + +// READ: 数组 + +unsigned long long arr[90]{0, 1}; +unsigned long long fibonacci(int i) { + switch (i) { + case 0: + return 0; + case 1: + return 1; + default: + // TODO: 补全三目表达式缺失的部分 + return arr[i] ? arr[i] : (arr[i] = fibonacci(i - 1) + fibonacci(i - 2)); + } +} + +int main(int argc, char **argv) { + // TODO: 为此 ASSERT 填写正确的值 + ASSERT(sizeof(arr) == 720, "sizeof array is size of all its elements"); + // ---- 不要修改以下代码 ---- + ASSERT(fibonacci(2) == 1, "fibonacci(2) should be 1"); + ASSERT(fibonacci(20) == 6765, "fibonacci(20) should be 6765"); + ASSERT(fibonacci(80) == 23416728348467685, "fibonacci(80) should be 23416728348467685"); + return 0; +} diff --git a/exercises/exercises/07_loop/main.cpp b/exercises/exercises/07_loop/main.cpp new file mode 100644 index 000000000..ed485308b --- /dev/null +++ b/exercises/exercises/07_loop/main.cpp @@ -0,0 +1,30 @@ +#include "../exercise.h" + +// TODO: 改正函数实现,实现正确的缓存优化斐波那契计算 +// THINk: 这个函数是一个纯函数(pure function)吗? +// READ: 纯函数 +static unsigned long long fibonacci(int i) { + // TODO: 为缓存设置正确的初始值 + static unsigned long long cache[96]{0, 1}; + static int cached = 2; + + // TODO: 设置正确的循环条件 + for (; cached<=i; ++cached) { + cache[cached] = cache[cached - 1] + cache[cached - 2]; + } + return cache[i]; +} + +// ---- 不要修改以下代码 ---- +int main(int argc, char **argv) { + ASSERT(fibonacci(0) == 0, "fibonacci(0) should be 0"); + ASSERT(fibonacci(1) == 1, "fibonacci(1) should be 1"); + ASSERT(fibonacci(2) == 1, "fibonacci(2) should be 1"); + ASSERT(fibonacci(3) == 2, "fibonacci(3) should be 2"); + ASSERT(fibonacci(10) == 55, "fibonacci(10) should be 55"); + + auto fib90 = fibonacci(90); + std::cout << "fibonacci(90) = " << fib90 << std::endl; + ASSERT(fib90 == 2880067194370816120, "fibonacci(90) should be 2880067194370816120"); + return 0; +} diff --git a/exercises/exercises/08_pointer/main.cpp b/exercises/exercises/08_pointer/main.cpp new file mode 100644 index 000000000..622a2fd87 --- /dev/null +++ b/exercises/exercises/08_pointer/main.cpp @@ -0,0 +1,39 @@ +#include "../exercise.h" + +// READ: 数组向指针退化 +bool is_fibonacci(int *ptr, int len, int stride) { + ASSERT(len >= 3, "`len` should be at least 3"); + // TODO: 编写代码判断从 ptr 开始,每 stride 个元素取 1 个元素,组成长度为 n 的数列是否满足 + for (int i = 0; i <= len - 3; i++) + { + int a = ptr[i * stride]; + int b = ptr[(i+1)*stride]; + int c = ptr[(i+2)*stride]; + if (a + b != c) + { + return false; + } + + } + // arr[i + 2] = arr[i] + arr[i + 1] + return true; +} + +// ---- 不要修改以下代码 ---- +int main(int argc, char **argv) { + int arr0[]{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55}, + arr1[]{0, 1, 2, 3, 4, 5, 6}, + arr2[]{99, 98, 4, 1, 7, 2, 11, 3, 18, 5, 29, 8, 47, 13, 76, 21, 123, 34, 199, 55, 322, 0, 0}; + // clang-format off + ASSERT( is_fibonacci(arr0 , sizeof(arr0) / sizeof(*arr0) , 1), "arr0 is Fibonacci" ); + ASSERT( is_fibonacci(arr0 + 2, sizeof(arr0) / sizeof(*arr0) - 4, 1), "part of arr0 is Fibonacci" ); + ASSERT(!is_fibonacci(arr1 , sizeof(arr1) / sizeof(*arr1) , 1), "arr1 is not Fibonacci"); + ASSERT( is_fibonacci(arr1 + 1, 3 , 1), "part of arr1 is Fibonacci" ); + ASSERT(!is_fibonacci(arr2 , sizeof(arr2) / sizeof(*arr2) , 1), "arr2 is not Fibonacci"); + ASSERT( is_fibonacci(arr2 + 2, 10 , 2), "part of arr2 is Fibonacci" ); + ASSERT( is_fibonacci(arr2 + 3, 9 , 2), "part of arr2 is Fibonacci" ); + ASSERT(!is_fibonacci(arr2 + 3, 10 , 2), "guard check" ); + ASSERT(!is_fibonacci(arr2 + 1, 10 , 2), "guard check" ); + // clang-format on + return 0; +} diff --git a/exercises/exercises/09_enum&union/main.cpp b/exercises/exercises/09_enum&union/main.cpp new file mode 100644 index 000000000..5779dd314 --- /dev/null +++ b/exercises/exercises/09_enum&union/main.cpp @@ -0,0 +1,51 @@ +#include "../exercise.h" + +// READ: 枚举类型 + +// `enum` 是 C 的兼容类型,本质上其对应类型的常量。 +// 在 `enum` 中定义标识符等价于定义 constexpr 常量, +// 这些标识符不需要前缀,可以直接引用。 +// 因此 `enum` 定义会污染命名空间。 +enum ColorEnum : unsigned char { + COLOR_RED = 31, + COLOR_GREEN, + COLOR_YELLOW, + COLOR_BLUE, +}; + +// 有作用域枚举型是 C++ 引入的类型安全枚举。 +// 其内部标识符需要带前缀引用,如 `Color::Red`。 +// 作用域枚举型可以避免命名空间污染,并提供类型安全保证。 +enum class Color : int { + Red = COLOR_RED, + Green, + Yellow, + Blue, +}; + +ColorEnum convert_by_pun(Color c) { + // READ: + // `union` 表示在同一内存位置存储的不同类型的值。 + // 其常见用法是实现类型双关转换,即将一种类型的值转换为另一种无关类型的值。 + // 但这种写法实际上仅在 C 语言良定义,在 C++ 中是未定义行为。 + // 这是比较少见的 C++ 不与 C 保持兼容的特性。 + // READ: 类型双关 + union TypePun { + ColorEnum e; + Color c; + }; + + TypePun pun; + // TODO: 补全类型双关转换 + pun.c = c; + + return pun.e; +} + +int main(int argc, char **argv) { + ASSERT(convert_by_pun(Color::Red) == COLOR_RED, "Type punning conversion"); + ASSERT(convert_by_pun(Color::Green) == COLOR_GREEN, "Type punning conversion"); + ASSERT(convert_by_pun(Color::Yellow) == COLOR_YELLOW, "Type punning conversion"); + ASSERT(convert_by_pun(Color::Blue) == COLOR_BLUE, "Type punning conversion"); + return 0; +} diff --git a/exercises/exercises/10_trivial/main.cpp b/exercises/exercises/10_trivial/main.cpp new file mode 100644 index 000000000..173ebbdd6 --- /dev/null +++ b/exercises/exercises/10_trivial/main.cpp @@ -0,0 +1,30 @@ +#include "../exercise.h" + +// READ: Trivial type + +struct FibonacciCache { + unsigned long long cache[16]; + int cached; +}; + +// TODO: 实现正确的缓存优化斐波那契计算 +static unsigned long long fibonacci(FibonacciCache &cache, int i) { + int cached = cache.cached; + for (cached; cached<=i; ++cached) { + cache.cache[cached] = cache.cache[cached - 1] + cache.cache[cached - 2]; + } + return cache.cache[i]; +} + +int main(int argc, char **argv) { + // TODO: 初始化缓存结构体,使计算正确 + // NOTICE: C/C++ 中,读取未初始化的变量(包括结构体变量)是未定义行为 + // READ: 初始化的各种写法 + FibonacciCache fib; + fib.cache[0] = 0; + fib.cache[1] = 1; + fib.cached = 2; + ASSERT(fibonacci(fib, 10) == 55, "fibonacci(10) should be 55"); + std::cout << "fibonacci(10) = " << fibonacci(fib, 10) << std::endl; + return 0; +} diff --git a/exercises/exercises/11_method/main.cpp b/exercises/exercises/11_method/main.cpp new file mode 100644 index 000000000..57d04651d --- /dev/null +++ b/exercises/exercises/11_method/main.cpp @@ -0,0 +1,25 @@ +#include "../exercise.h" + +struct Fibonacci { + unsigned long long cache[128]; + int cached; + + // TODO: 实现正确的缓存优化斐波那契计算 + unsigned long long get(int i) { + for (; cached<=i; ++cached) { + cache[cached] = cache[cached - 1] + cache[cached - 2]; + } + return cache[i]; + } +}; + +int main(int argc, char **argv) { + // TODO: 初始化缓存结构体,使计算正确 + Fibonacci fib; + fib.cache[0] = 0; + fib.cache[1] = 1; + fib.cached = 2; + ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); + std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; + return 0; +} diff --git a/exercises/exercises/12_method_const/main.cpp b/exercises/exercises/12_method_const/main.cpp new file mode 100644 index 000000000..65c079be1 --- /dev/null +++ b/exercises/exercises/12_method_const/main.cpp @@ -0,0 +1,18 @@ +#include "../exercise.h" + +// READ: 有 cv 限定符的成员函数 + +struct Fibonacci { + int numbers[11]; + // TODO: 修改方法签名和实现,使测试通过 + int get(int i) const { + return numbers[i]; + } +}; + +int main(int argc, char **argv) { + Fibonacci constexpr FIB{{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55}}; + ASSERT(FIB.get(10) == 55, "fibonacci(10) should be 55"); + std::cout << "fibonacci(10) = " << FIB.get(10) << std::endl; + return 0; +} diff --git a/exercises/exercises/13_class/main.cpp b/exercises/exercises/13_class/main.cpp new file mode 100644 index 000000000..b6dc868d1 --- /dev/null +++ b/exercises/exercises/13_class/main.cpp @@ -0,0 +1,42 @@ +#include "../exercise.h" + +// C++ 中,`class` 和 `struct` 之间的**唯一区别**是 +// `class` 默认访问控制符是 `private`, +// `struct` 默认访问控制符是 `public`。 +// READ: 访问说明符 + +// 这个 class 中的字段被 private 修饰,只能在 class 内部访问。 +// 因此必须提供构造器来初始化字段。 +// READ: 构造器 +class Fibonacci { + size_t cache[16]; + int cached; + +public: + // TODO: 实现构造器 + // Fibonacci() + Fibonacci(const size_t inputCache[16], int cached) { + for (int i = 0; i < 16; ++i) { + cache[i] = inputCache[i]; + } + this->cached = cached; + } + + // TODO: 实现正确的缓存优化斐波那契计算 + size_t get(int i) { + for (; cached<=i; ++cached) { + cache[cached] = cache[cached - 1] + cache[cached - 2]; + } + return cache[i]; + } +}; + +int main(int argc, char **argv) { + // 现在类型拥有无参构造器,声明时会直接调用。 + // 这个写法不再是未定义行为了。 + size_t cache[16]{0, 1}; + Fibonacci fib(cache, 2); + ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); + std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; + return 0; +} diff --git a/exercises/exercises/14_class_destruct/main.cpp b/exercises/exercises/14_class_destruct/main.cpp new file mode 100644 index 000000000..f00bcb789 --- /dev/null +++ b/exercises/exercises/14_class_destruct/main.cpp @@ -0,0 +1,42 @@ +#include "../exercise.h" + +// READ: 析构函数 +// READ: RAII + +/// @brief 任意缓存容量的斐波那契类型。 +/// @details 可以在构造时传入缓存容量,因此需要动态分配缓存空间。 +class DynFibonacci { + size_t *cache; + int cached; + +public: + // TODO: 实现动态设置容量的构造器 + DynFibonacci(int capacity) { + cache = new size_t[capacity]; + cache[0] = 0; + cache[1] = 1; + cached = 2; + } + + // TODO: 实现析构器,释放缓存空间 + ~DynFibonacci(); + + // TODO: 实现正确的缓存优化斐波那契计算 + size_t get(int i) { + for (; cached <= i; ++cached) { + cache[cached] = cache[cached - 1] + cache[cached - 2]; + } + return cache[i]; + } +}; + +DynFibonacci ::~DynFibonacci() { + delete[] cache; +} + +int main(int argc, char **argv) { + DynFibonacci fib(12); + ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); + std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; + return 0; +} diff --git a/exercises/exercises/15_class_clone/main.cpp b/exercises/exercises/15_class_clone/main.cpp new file mode 100644 index 000000000..38737265a --- /dev/null +++ b/exercises/exercises/15_class_clone/main.cpp @@ -0,0 +1,61 @@ +#include "../exercise.h" + +// READ: 复制构造函数 +// READ: 函数定义(显式弃置) + + +class DynFibonacci { + size_t *cache; + int cached; + +public: + // TODO: 实现动态设置容量的构造器 + DynFibonacci(int capacity) : cache(new size_t[capacity]), cached(2) { + cache[0] = 0; + cache[1] = 1; + } + + // TODO: 实现复制构造器 + DynFibonacci(DynFibonacci const &c) { + this->cache = new size_t[c.cached]; + this->cached = c.cached; + for (int i = 0; i < c.cached; i++) { + this->cache[i] = c.cache[i]; + } + } + + // TODO: 实现析构器,释放缓存空间 + ~DynFibonacci(); + + // TODO: 实现正确的缓存优化斐波那契计算 + size_t get(int i) { + for (; cached <= i; ++cached) { + cache[cached] = cache[cached - 1] + cache[cached - 2]; + } + return cache[i]; + } + + // NOTICE: 不要修改这个方法 + // NOTICE: 名字相同参数也相同,但 const 修饰不同的方法是一对重载方法,可以同时存在 + // 本质上,方法是隐藏了 this 参数的函数 + // const 修饰作用在 this 上,因此它们实际上参数不同 + size_t get(int i) const { + if (i <= cached) { + return cache[i]; + } + ASSERT(false, "i out of range"); + } +}; + + +DynFibonacci::~DynFibonacci() { + delete[] cache; +} + +int main(int argc, char **argv) { + DynFibonacci fib(12); + ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); + DynFibonacci const fib_ = fib; + ASSERT(fib_.get(10) == fib.get(10), "Object cloned"); + return 0; +} diff --git a/exercises/exercises/16_class_move/main.cpp b/exercises/exercises/16_class_move/main.cpp new file mode 100644 index 000000000..2269a80b8 --- /dev/null +++ b/exercises/exercises/16_class_move/main.cpp @@ -0,0 +1,90 @@ +#include "../exercise.h" + +// READ: 左值右值(概念) +// READ: 左值右值(细节) +// READ: 关于移动语义 +// READ: 如果实现移动构造 + +// READ: 移动构造函数 +// READ: 移动赋值 +// READ: 运算符重载 + +class DynFibonacci { + size_t *cache; + int cached; + +public: + // TODO: 实现动态设置容量的构造器 + DynFibonacci(int capacity): cache(new size_t[capacity]), cached(2) + { + cache[0] = 0; + cache[1] = 1; + } + + // TODO: 实现移动构造器 + DynFibonacci(DynFibonacci &&c) noexcept : cache(c.cache), cached(c.cached) + { + c.cache =nullptr; + c.cached = 0; + } + + // TODO: 实现移动赋值 + // NOTICE: ⚠ 注意移动到自身问题 ⚠ + DynFibonacci& operator=(DynFibonacci&& c) noexcept + { + if (this != &c) + { + delete[] cache; + cache = c.cache; + cached = c.cached; + + c.cache = nullptr; + c.cached = 0; + } + return *this; + } + + // TODO: 实现析构器,释放缓存空间 + ~DynFibonacci() + { + delete[]cache; + cache = nullptr; + } + + // TODO: 实现正确的缓存优化斐波那契计算 + size_t operator[](int i) { + for (; cached<=i; ++cached) { + cache[cached] = cache[cached - 1] + cache[cached - 2]; + } + return cache[i]; + } + + // NOTICE: 不要修改这个方法 + size_t operator[](int i) const { + ASSERT(i <= cached, "i out of range"); + return cache[i]; + } + + // NOTICE: 不要修改这个方法 + bool is_alive() const { + return cache; + } +}; + +int main(int argc, char **argv) { + DynFibonacci fib(12); + ASSERT(fib[10] == 55, "fibonacci(10) should be 55"); + + DynFibonacci const fib_ = std::move(fib); + ASSERT(!fib.is_alive(), "Object moved"); + ASSERT(fib_[10] == 55, "fibonacci(10) should be 55"); + + DynFibonacci fib0(6); + DynFibonacci fib1(12); + + fib0 = std::move(fib1); + fib0 = std::move(fib0); + ASSERT(fib0[10] == 55, "fibonacci(10) should be 55"); + + return 0; +} diff --git a/exercises/exercises/17_class_derive/main.cpp b/exercises/exercises/17_class_derive/main.cpp new file mode 100644 index 000000000..93014cfa6 --- /dev/null +++ b/exercises/exercises/17_class_derive/main.cpp @@ -0,0 +1,77 @@ +#include "../exercise.h" + +// READ: 派生类 + +static int i = 0; + +struct X { + int x; + + X(int x_) : x(x_) { + std::cout << ++i << ". " << "X(" << x << ')' << std::endl; + } + X(X const &other) : x(other.x) { + std::cout << ++i << ". " << "X(X const &) : x(" << x << ')' << std::endl; + } + ~X() { + std::cout << ++i << ". " << "~X(" << x << ')' << std::endl; + } +}; +struct A { + int a; + + A(int a_) : a(a_) { + std::cout << ++i << ". " << "A(" << a << ')' << std::endl; + } + A(A const &other) : a(other.a) { + std::cout << ++i << ". " << "A(A const &) : a(" << a << ')' << std::endl; + } + ~A() { + std::cout << ++i << ". " << "~A(" << a << ')' << std::endl; + } +}; +struct B : public A { + X x; + + B(int b) : A(1), x(b) { + std::cout << ++i << ". " << "B(" << a << ", X(" << x.x << "))" << std::endl; + } + B(B const &other) : A(other.a), x(other.x) { + std::cout << ++i << ". " << "B(B const &) : A(" << a << "), x(X(" << x.x << "))" << std::endl; + } + ~B() { + std::cout << ++i << ". " << "~B(" << a << ", X(" << x.x << "))" << std::endl; + } +}; + +int main(int argc, char **argv) { + X x = X(1); + A a = A(2); + B b = B(3); + + // TODO: 补全三个类型的大小 + static_assert(sizeof(X) == sizeof(int), "There is an int in X"); + static_assert(sizeof(A) == sizeof(int), "There is an int in A"); + static_assert(sizeof(B) == 2*sizeof(int), "B is an A with an X"); + + i = 0; + std::cout << std::endl + << "-------------------------" << std::endl + << std::endl; + + // 这是不可能的,A 无法提供 B 增加的成员变量的值 + // B ba = A(4); + + // 这也是不可能的,因为 A 是 B 的一部分,就好像不可以把套娃的外层放进内层里。 + A ab = B(5);// 然而这个代码可以编译和运行! + // THINK: 观察打印出的信息,推测把大象放进冰箱分几步? + // THINK: 这样的代码是“安全”的吗? + // NOTICE: 真实场景中不太可能出现这样的代码 + + i = 0; + std::cout << std::endl + << "-------------------------" << std::endl + << std::endl; + + return 0; +} diff --git a/exercises/exercises/18_class_virtual/main.cpp b/exercises/exercises/18_class_virtual/main.cpp new file mode 100644 index 000000000..344709e4c --- /dev/null +++ b/exercises/exercises/18_class_virtual/main.cpp @@ -0,0 +1,82 @@ +#include "../exercise.h" + +// READ: 虚函数 + +struct A { + virtual char virtual_name() const { + return 'A'; + } + char direct_name() const { + return 'A'; + } +}; +struct B : public A { + // READ: override + char virtual_name() const override { + return 'B'; + } + char direct_name() const { + return 'B'; + } +}; +struct C : public B { + // READ: final + char virtual_name() const final { + return 'C'; + } + char direct_name() const { + return 'C'; + } +}; +struct D : public C { + char direct_name() const { + return 'D'; + } +}; + +int main(int argc, char **argv) { + constexpr auto MSG = "Replace '?' with its correct name."; + + A a; + B b; + C c; + D d; + + ASSERT(a.virtual_name() == 'A', MSG); + ASSERT(b.virtual_name() == 'B', MSG); + ASSERT(c.virtual_name() == 'C', MSG); + ASSERT(d.virtual_name() == 'C', MSG); + ASSERT(a.direct_name() == 'A', MSG); + ASSERT(b.direct_name() == 'B', MSG); + ASSERT(c.direct_name() == 'C', MSG); + ASSERT(d.direct_name() == 'D', MSG); + + A &rab = b; + B &rbc = c; + C &rcd = d; + + ASSERT(rab.virtual_name() == 'B', MSG); + ASSERT(rbc.virtual_name() == 'C', MSG); + ASSERT(rcd.virtual_name() == 'C', MSG); + ASSERT(rab.direct_name() == 'A', MSG); + ASSERT(rbc.direct_name() == 'B', MSG); + ASSERT(rcd.direct_name() == 'C', MSG); + + A &rac = c; + B &rbd = d; + + ASSERT(rac.virtual_name() == 'C', MSG); + ASSERT(rbd.virtual_name() == 'C', MSG); + ASSERT(rac.direct_name() == 'A', MSG); + ASSERT(rbd.direct_name() == 'B', MSG); + + A &rad = d; + + ASSERT(rad.virtual_name() == 'C', MSG); + ASSERT(rad.direct_name() == 'A', MSG); + + return 0; +} + +// READ: 扩展阅读-纯虚、抽象 +// READ: 扩展阅读-虚继承 diff --git a/exercises/exercises/19_class_virtual_destruct/main.cpp b/exercises/exercises/19_class_virtual_destruct/main.cpp new file mode 100644 index 000000000..8d66e8edf --- /dev/null +++ b/exercises/exercises/19_class_virtual_destruct/main.cpp @@ -0,0 +1,70 @@ +#include "../exercise.h" + +// READ: 静态字段 +// READ: 虚析构函数 + + +struct A { + // TODO: 正确初始化静态字段 + static int num_a; + + A() { + ++num_a; + } + virtual ~A() { + --num_a; + } + + virtual char name() const { + return 'A'; + } +}; +struct B final : public A { + // TODO: 正确初始化静态字段 + static int num_b; + + B() { + ++num_b; + } + ~B() override { + --num_b; + } + + char name() const final { + return 'B'; + } +}; + +int A::num_a = 0; +int B::num_b = 0; + +int main(int argc, char **argv) { + auto a = new A; + auto b = new B; + ASSERT(A::num_a == 2, "Fill in the correct value for A::num_a"); + ASSERT(B::num_b == 1, "Fill in the correct value for B::num_b"); + ASSERT(a->name() == 'A', "Fill in the correct value for a->name()"); + ASSERT(b->name() == 'B', "Fill in the correct value for b->name()"); + + delete a; + delete b; + ASSERT(A::num_a == 0, "Every A was destroyed"); + ASSERT(B::num_b == 0, "Every B was destroyed"); + + A *ab = new B;// 派生类指针可以随意转换为基类指针 + ASSERT(A::num_a == 1, "Fill in the correct value for A::num_a"); + ASSERT(B::num_b == 1, "Fill in the correct value for B::num_b"); + ASSERT(ab->name() == 'B', "Fill in the correct value for ab->name()"); + + // TODO: 基类指针无法随意转换为派生类指针,补全正确的转换语句 + //B &bb = *ab; + B &bb = dynamic_cast(*ab); + ASSERT(bb.name() == 'B', "Fill in the correct value for bb->name()"); + + // TODO: ---- 以下代码不要修改,通过改正类定义解决编译问题 ---- + delete ab;// 通过指针可以删除指向的对象,即使是多态对象 + ASSERT(A::num_a == 0, "Every A was destroyed"); + ASSERT(B::num_b == 0, "Every B was destroyed"); + + return 0; +} diff --git a/exercises/exercises/20_function_template/main.cpp b/exercises/exercises/20_function_template/main.cpp new file mode 100644 index 000000000..7196e8930 --- /dev/null +++ b/exercises/exercises/20_function_template/main.cpp @@ -0,0 +1,21 @@ +#include "../exercise.h" + +// READ: 函数模板 +// TODO: 将这个函数模板化 +template +T plus(T a, T b) { + return a + b; +} + +int main(int argc, char **argv) { + ASSERT(plus(1, 2) == 3, "Plus two int"); + ASSERT(plus(1u, 2u) == 3u, "Plus two unsigned int"); + + // THINK: 浮点数何时可以判断 ==?何时必须判断差值? + ASSERT(plus(1.25f, 2.5f) == 3.75f, "Plus two float"); + ASSERT(plus(1.25, 2.5) == 3.75, "Plus two double"); + // TODO: 修改判断条件使测试通过 + ASSERT(std::abs(plus(0.1, 0.2) - 0.3) < 1e-9, "How to make this pass?"); + + return 0; +} diff --git a/exercises/exercises/21_runtime_datatype/main.cpp b/exercises/exercises/21_runtime_datatype/main.cpp new file mode 100644 index 000000000..153ab5b2d --- /dev/null +++ b/exercises/exercises/21_runtime_datatype/main.cpp @@ -0,0 +1,54 @@ +#include "../exercise.h" +#include + +enum class DataType { + Float, + Double, +}; + +/// @brief Tagged union 即标签化联合体,是联合体的一种常见应用。 +/// Rust enum 在实现上就是标签化联合体。 +struct TaggedUnion { + DataType type; + // NOTICE: struct/union 可以相互任意嵌套。 + union { + float f; + double d; + }; +}; + +// TODO: 将这个函数模板化用于 sigmoid_dyn +template +T sigmoid(T x) { + return 1 / (1 + std::exp(-x)); +} + +TaggedUnion sigmoid_dyn(TaggedUnion x) { + TaggedUnion ans{x.type}; + // TODO: 根据 type 调用 sigmoid + if (DataType::Float == x.type) + { + ans.f = sigmoid(x.f); + } + else + { + ans.d = sigmoid(x.d); + } + return ans; +} + +// ---- 不要修改以下代码 ---- +int main(int argc, char **argv) { + TaggedUnion xf{DataType::Float}; + xf.f = 5.f; + auto yf = sigmoid_dyn(xf); + ASSERT(yf.type == DataType::Float, "type mismatch"); + ASSERT(yf.f == 1 / (1 + std::exp(-5.f)), "sigmoid float"); + + TaggedUnion xd{DataType::Double}; + xd.d = 5.0; + auto yd = sigmoid_dyn(xd); + ASSERT(yd.type == DataType::Double, "type mismatch"); + ASSERT(yd.d == 1 / (1 + std::exp(-5.0)), "sigmoid double"); + return 0; +} diff --git a/exercises/exercises/22_class_template/main.cpp b/exercises/exercises/22_class_template/main.cpp new file mode 100644 index 000000000..890d7cb7c --- /dev/null +++ b/exercises/exercises/22_class_template/main.cpp @@ -0,0 +1,147 @@ +#include "../exercise.h" +#include +// READ: 类模板 +#include + + +template +// Tensor4D(const unsigned int[4], const T *) -> Tensor4D; + +struct Tensor4D { + unsigned int shape[4]; + T *data; + + Tensor4D(unsigned int const shape_[4], T const *data_) { + unsigned int size = 1; + // TODO: 填入正确的 shape 并计算 size + for (int i = 0; i < 4; ++i) { + shape[i] = shape_[i]; + size *= shape[i]; + } + data = new T[size]; + std::memcpy(data, data_, size * sizeof(T)); + } + ~Tensor4D() { + delete[] data; + } + + // 为了保持简单,禁止复制和移动 + Tensor4D(Tensor4D const &) = delete; + Tensor4D(Tensor4D &&) noexcept = delete; + + // 这个加法需要支持“单向广播”。 + // 具体来说,`others` 可以具有与 `this` 不同的形状,形状不同的维度长度必须为 1。 + // `others` 长度为 1 但 `this` 长度不为 1 的维度将发生广播计算。 + // 例如,`this` 形状为 `[1, 2, 3, 4]`,`others` 形状为 `[1, 2, 1, 4]`, + // 则 `this` 与 `others` 相加时,3 个形状为 `[1, 2, 1, 4]` 的子张量各自与 `others` 对应项相加。 + Tensor4D &operator+=(Tensor4D const &others) { + // TODO: 实现单向广播的加法 + unsigned int total = 1; + for (int i = 0; i < 4; ++i) { + // others维度要么等于当前维度,要么为1(广播) + assert((others.shape[i] == shape[i]) || (others.shape[i] == 1)); + total *= shape[i]; + } + + for (unsigned int i = 0; i < total; ++i) { + // 将一维索引i解码成4维索引 + unsigned int index[4]; + unsigned int stride = total; + unsigned int offset = i; + for (int d = 0; d < 4; ++d) { + stride /= shape[d]; + index[d] = offset / stride; + offset %= stride; + } + + // 计算others中对应的广播后索引 + unsigned int other_index = 0; + unsigned int other_stride = 1; + for (int d = 3; d >= 0; --d) { + unsigned int idx = (others.shape[d] == 1) ? 0 : index[d]; + other_index += idx * other_stride; + other_stride *= others.shape[d]; + } + + data[i] += others.data[other_index]; + } + + return *this; + } +}; + +// ---- 不要修改以下代码 ---- +int main(int argc, char **argv) { + { + unsigned int shape[]{1, 2, 3, 4}; + // clang-format off + int data[]{ + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + + 13, 14, 15, 16, + 17, 18, 19, 20, + 21, 22, 23, 24}; + // clang-format on + auto t0 = Tensor4D(shape, data); + auto t1 = Tensor4D(shape, data); + t0 += t1; + for (auto i = 0u; i < sizeof(data) / sizeof(*data); ++i) { + ASSERT(t0.data[i] == data[i] * 2, "Tensor doubled by plus its self."); + } + } + { + unsigned int s0[]{1, 2, 3, 4}; + // clang-format off + float d0[]{ + 1, 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, + + 4, 4, 4, 4, + 5, 5, 5, 5, + 6, 6, 6, 6}; + // clang-format on + unsigned int s1[]{1, 2, 3, 1}; + // clang-format off + float d1[]{ + 6, + 5, + 4, + + 3, + 2, + 1}; + // clang-format on + + auto t0 = Tensor4D(s0, d0); + auto t1 = Tensor4D(s1, d1); + t0 += t1; + for (auto i = 0u; i < sizeof(d0) / sizeof(*d0); ++i) { + ASSERT(t0.data[i] == 7.f, "Every element of t0 should be 7 after adding t1 to it."); + } + } + { + unsigned int s0[]{1, 2, 3, 4}; + // clang-format off + double d0[]{ + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + + 13, 14, 15, 16, + 17, 18, 19, 20, + 21, 22, 23, 24}; + // clang-format on + unsigned int s1[]{1, 1, 1, 1}; + double d1[]{1}; + + auto t0 = Tensor4D(s0, d0); + auto t1 = Tensor4D(s1, d1); + t0 += t1; + for (auto i = 0u; i < sizeof(d0) / sizeof(*d0); ++i) { + ASSERT(t0.data[i] == d0[i] + 1, "Every element of t0 should be incremented by 1 after adding t1 to it."); + } + } +} diff --git a/exercises/exercises/23_template_const/main.cpp b/exercises/exercises/23_template_const/main.cpp new file mode 100644 index 000000000..f2f49c3d1 --- /dev/null +++ b/exercises/exercises/23_template_const/main.cpp @@ -0,0 +1,92 @@ +#include "../exercise.h" +#include + +// READ: 模板非类型实参 + +template +struct Tensor { + unsigned int shape[N]; + T *data; + + Tensor(unsigned int const shape_[N]) { + unsigned int size = 1; + // TODO: 填入正确的 shape 并计算 size + for (unsigned int i = 0; i < N; ++i) { + shape[i] = shape_[i]; + size *= shape[i]; + } + data = new T[size]; + std::memset(data, 0, size * sizeof(T)); + } + ~Tensor() { + delete[] data; + } + + // 为了保持简单,禁止复制和移动 + Tensor(Tensor const &) = delete; + //Tensor(Tensor&&) noexcept = delete; + Tensor(Tensor &&other) noexcept { + std::memcpy(shape, other.shape, sizeof(shape)); + data = other.data; + other.data = nullptr;// 转移所有权 + } + + + T &operator[](unsigned int const indices[N]) { + return data[data_index(indices)]; + } + T const &operator[](unsigned int const indices[N]) const { + return data[data_index(indices)]; + } + +private: + unsigned int data_index(unsigned int const indices[N]) const { + unsigned int index = 0; + unsigned int stride[N]; + stride[N - 1] = 1; + for (int i = N - 2; i >= 0; --i) { + stride[i] = stride[i + 1] * shape[i + 1]; + } + + // 再正序遍历,用 stride 乘索引累加 + for (unsigned int i = 0; i < N; ++i) { + ASSERT(indices[i] < shape[i], "Invalid index"); + index += indices[i] * stride[i]; + } + + return index; + } +}; + +// ---- 不要修改以下代码 ---- +int main(int argc, char **argv) { + { + unsigned int shape[]{2, 3, 4, 5}; + auto tensor = Tensor<4, int>(shape); + + unsigned int i0[]{0, 0, 0, 0}; + tensor[i0] = 1; + ASSERT(tensor[i0] == 1, "tensor[i0] should be 1"); + ASSERT(tensor.data[0] == 1, "tensor[i0] should be 1"); + + unsigned int i1[]{1, 2, 3, 4}; + tensor[i1] = 2; + ASSERT(tensor[i1] == 2, "tensor[i1] should be 2"); + ASSERT(tensor.data[119] == 2, "tensor[i1] should be 2"); + } + { + unsigned int shape[]{7, 8, 128}; + auto tensor = Tensor<3, float>(shape); + + unsigned int i0[]{0, 0, 0}; + tensor[i0] = 1.f; + ASSERT(tensor[i0] == 1.f, "tensor[i0] should be 1"); + ASSERT(tensor.data[0] == 1.f, "tensor[i0] should be 1"); + + unsigned int i1[]{3, 4, 99}; + tensor[i1] = 2.f; + ASSERT(tensor[i1] == 2.f, "tensor[i1] should be 2"); + ASSERT(tensor.data[3683] == 2.f, "tensor[i1] should be 2"); + } + return 0; +} diff --git a/exercises/exercises/24_std_array/main.cpp b/exercises/exercises/24_std_array/main.cpp new file mode 100644 index 000000000..8ac430609 --- /dev/null +++ b/exercises/exercises/24_std_array/main.cpp @@ -0,0 +1,29 @@ +#include "../exercise.h" +#include +#include + +// READ: std::array + +// TODO: 将下列 `?` 替换为正确的代码 +int main(int argc, char **argv) { + { + std::array arr{{1, 2, 3, 4, 5}}; + ASSERT(arr.size() == 5, "Fill in the correct value."); + ASSERT(sizeof(arr) == 20, "Fill in the correct value."); + int ans[]{1, 2, 3, 4, 5}; + ASSERT(std::memcmp(arr.data(), ans, sizeof(ans)) == 0, "Fill in the correct values."); + } + { + std::array arr; + ASSERT(arr.size() == 8, "Fill in the correct value."); + ASSERT(sizeof(arr) == 64, "Fill in the correct value."); + } + { + std::array arr{"Hello, InfiniTensor!"}; + ASSERT(arr.size() == 21, "Fill in the correct value."); + //std::cout << sizeof(char) << std::endl; + ASSERT(sizeof(arr) == 21, "Fill in the correct value."); + ASSERT(std::strcmp(arr.data(), "Hello, InfiniTensor!") == 0, "Fill in the correct value."); + } + return 0; +} diff --git a/exercises/exercises/25_std_vector/main.cpp b/exercises/exercises/25_std_vector/main.cpp new file mode 100644 index 000000000..3872b36d5 --- /dev/null +++ b/exercises/exercises/25_std_vector/main.cpp @@ -0,0 +1,90 @@ +#include "../exercise.h" +#include +#include + +// READ: std::vector + +// TODO: 将下列 `?` 替换为正确的代码 +int main(int argc, char** argv) { + { + std::vector vec{ 1, 2, 3, 4, 5 }; + ASSERT(vec.size() == 5, "Fill in the correct value."); + // THINK: `std::vector` 的大小是什么意思?与什么有关? + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); + int ans[]{ 1, 2, 3, 4, 5 }; + ASSERT(std::memcmp(vec.data(), ans, sizeof(ans)) == 0, "Fill in the correct values."); + } + { + std::vector vec{ 1, 2, 3, 4, 5 }; + { + ASSERT(vec.size() == 5, "Fill in the correct value."); + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); + double ans[]{ 1, 2, 3, 4, 5 }; + ASSERT(std::memcmp(vec.data(), ans, sizeof(ans)) == 0, "Fill in the correct values."); + } + { + vec.push_back(6); + ASSERT(vec.size() == 6, "Fill in the correct value."); + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); + vec.pop_back(); + ASSERT(vec.size() == 5, "Fill in the correct value."); + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); + } + { + vec[4] = 6; + ASSERT(vec[0] == 1, "Fill in the correct value."); + ASSERT(vec[1] == 2, "Fill in the correct value."); + ASSERT(vec[2] == 3, "Fill in the correct value."); + ASSERT(vec[3] == 4, "Fill in the correct value."); + ASSERT(vec[4] == 6, "Fill in the correct value."); + } + { + // THINK: `std::vector` 插入删除的时间复杂度是什么? + vec.insert(vec.begin() + 1, 1.5); + ASSERT((vec == std::vector{1, 1.5, 2, 3, 4, 6}), "Make this assertion pass."); + vec.erase(vec.begin() + 3); + ASSERT((vec == std::vector{1, 1.5, 2, 4, 6}), "Make this assertion pass."); + } + { + vec.shrink_to_fit(); + ASSERT(vec.capacity() == 5, "Fill in the correct value."); + vec.clear(); + ASSERT(vec.empty(), "`vec` is empty now."); + ASSERT(vec.size() == 0, "Fill in the correct value."); + ASSERT(vec.capacity() == 5, "Fill in the correct value."); + } + } + { + std::vector vec(48, 'z');// TODO: 调用正确的构造函数 + ASSERT(vec[0] == 'z', "Make this assertion pass."); + ASSERT(vec[47] == 'z', "Make this assertion pass."); + ASSERT(vec.size() == 48, "Make this assertion pass."); + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); + { + auto capacity = vec.capacity(); + vec.resize(16); + ASSERT(vec.size() == 16, "Fill in the correct value."); + ASSERT(vec.capacity() == capacity, "Fill in a correct identifier."); + } + { + vec.reserve(256); + ASSERT(vec.size() == 16, "Fill in the correct value."); + ASSERT(vec.capacity() == 256, "Fill in the correct value."); + } + { + vec.push_back('a'); + vec.push_back('b'); + vec.push_back('c'); + vec.push_back('d'); + ASSERT(vec.size() == 20, "Fill in the correct value."); + ASSERT(vec.capacity() == 256, "Fill in the correct value."); + ASSERT(vec[15] == 'z', "Fill in the correct value."); + ASSERT(vec[16] == 'a', "Fill in the correct value."); + ASSERT(vec[17] == 'b', "Fill in the correct value."); + ASSERT(vec[18] == 'c', "Fill in the correct value."); + ASSERT(vec[19] == 'd', "Fill in the correct value."); + } + } + + return 0; +} diff --git a/exercises/exercises/26_std_vector_bool/main.cpp b/exercises/exercises/26_std_vector_bool/main.cpp new file mode 100644 index 000000000..972a80b08 --- /dev/null +++ b/exercises/exercises/26_std_vector_bool/main.cpp @@ -0,0 +1,34 @@ +#include "../exercise.h" +#include + +// READ: std::vector +// READ: 模板特化 + +// TODO: 将下列 `?` 替换为正确的代码 +int main(int argc, char **argv) { + std::vector vec(100, true);// TODO: 正确调用构造函数 + ASSERT(vec[0], "Make this assertion pass."); + ASSERT(vec[99], "Make this assertion pass."); + ASSERT(vec.size() == 100, "Make this assertion pass."); + // NOTICE: 平台相关!注意 CI:Ubuntu 上的值。 + std::cout << "sizeof(std::vector) = " << sizeof(std::vector) << std::endl; + ASSERT(sizeof(vec) == sizeof(std::vector), "Fill in the correct value."); + { + vec[20] = false; + ASSERT(!vec[20], "Fill in `vec[20]` or `!vec[20]`."); + } + { + vec.push_back(false); + ASSERT(vec.size() == 101, "Fill in the correct value."); + ASSERT(!vec[100], "Fill in `vec[100]` or `!vec[100]`."); + } + { + auto ref = vec[30]; + ASSERT(ref, "Fill in `ref` or `!ref`"); + ref = false; + ASSERT(!ref, "Fill in `ref` or `!ref`"); + // THINK: WHAT and WHY? + ASSERT(!vec[30], "Fill in `vec[30]` or `!vec[30]`."); + } + return 0; +} diff --git a/exercises/exercises/27_strides/main.cpp b/exercises/exercises/27_strides/main.cpp new file mode 100644 index 000000000..13feaf5ae --- /dev/null +++ b/exercises/exercises/27_strides/main.cpp @@ -0,0 +1,38 @@ +#include "../exercise.h" +#include + +// 张量即多维数组。连续存储张量即逻辑结构与存储结构一致的张量。 +// 通常来说,形状为 [d0, d1, ..., dn] 的张量,第 n 维是 dn 个连续的元素,第 n-1 维是 dn-1 个连续的 dn 个元素,以此类推。 +// 张量的步长或跨度指的是张量每个维度上坐标 +1 时,数据指针跨过的范围。 +// 因此,一个连续张量,其第 n 维的步长为 1,第 n-1 维的步长为 dn,第 n-2 维的步长为 dn*dn-1,以此类推。 +// 例如,一个 2x3x4 张量,其步长为 [12, 4, 1]。 + +// READ: 类型别名 +using udim = unsigned int; + +/// @brief 计算连续存储张量的步长 +/// @param shape 张量的形状 +/// @return 张量每维度的访问步长 +std::vector strides(std::vector const &shape) { + std::vector strides(shape.size()); + // TODO: 完成函数体,根据张量形状计算张量连续存储时的步长。 + // READ: 逆向迭代器 std::vector::rbegin + // 使用逆向迭代器可能可以简化代码 + if (shape.empty()) return strides; + + strides.back() = 1;// 最后一维步长为1 + for (int i = (int) shape.size() - 2; i >= 0; --i) { + strides[i] = shape[i + 1] * strides[i + 1]; + } + + return strides; +} + +// ---- 不要修改以下代码 ---- +int main(int argc, char **argv) { + ASSERT((strides({2, 3, 4}) == std::vector{12, 4, 1}), "Make this assertion pass."); + ASSERT((strides({3, 4, 5}) == std::vector{20, 5, 1}), "Make this assertion pass."); + ASSERT((strides({1, 3, 224, 224}) == std::vector{150528, 50176, 224, 1}), "Make this assertion pass."); + ASSERT((strides({7, 1, 1, 1, 5}) == std::vector{5, 5, 5, 5, 1}), "Make this assertion pass."); + return 0; +} diff --git a/exercises/exercises/28_std_string/main.cpp b/exercises/exercises/28_std_string/main.cpp new file mode 100644 index 000000000..5fcfdf31d --- /dev/null +++ b/exercises/exercises/28_std_string/main.cpp @@ -0,0 +1,18 @@ +#include "../exercise.h" +#include + +// READ: 字符串 + +int main(int argc, char **argv) { + // READ: 字符串字面量 + using namespace std::string_literals; + auto hello = "Hello"s; + auto world = "world"; + // READ: `decltype` 表达式 + // READ: `std::is_same_v` 元编程判别 + ASSERT((std::is_same_v), "Fill in the missing type."); + ASSERT((std::is_same_v), "Fill in the missing type."); + // TODO: 将 `?` 替换为正确的字符串 + ASSERT(hello + ", " + world + '!' == "Hello, world!", "Fill in the missing string."); + return 0; +} diff --git a/exercises/exercises/29_std_map/main.cpp b/exercises/exercises/29_std_map/main.cpp new file mode 100644 index 000000000..68122a5fc --- /dev/null +++ b/exercises/exercises/29_std_map/main.cpp @@ -0,0 +1,39 @@ +#include "../exercise.h" +#include + +// READ: `std::map` +// READ: `std::unordered_map` + +template +bool key_exists(std::map const &map, k const &key) { + // TODO: 实现函数 + return map.find(key) != map.end(); +} + +template +void set(std::map &map, k key, v value) { + // TODO: 实现函数 + map[key] = value; +} + +// ---- 不要修改以下代码 ---- +int main(int argc, char **argv) { + using namespace std::string_literals; + + std::map secrets; + + set(secrets, "hello"s, "world"s); + ASSERT(key_exists(secrets, "hello"s), "\"hello\" shoud be in `secrets`"); + ASSERT(!key_exists(secrets, "foo"s), "\"foo\" shoud not be in `secrets`"); + + set(secrets, "foo"s, "bar"s); + set(secrets, "Infini"s, "Tensor"s); + ASSERT(secrets["hello"] == "world", "hello -> world"); + ASSERT(secrets["foo"] == "bar", "foo -> bar"); + ASSERT(secrets["Infini"] == "Tensor", "Infini -> Tensor"); + + set(secrets, "hello"s, "developer"s); + ASSERT(secrets["hello"] == "developer", "hello -> developer"); + + return 0; +} diff --git a/exercises/exercises/30_std_unique_ptr/main.cpp b/exercises/exercises/30_std_unique_ptr/main.cpp new file mode 100644 index 000000000..d9489ac7b --- /dev/null +++ b/exercises/exercises/30_std_unique_ptr/main.cpp @@ -0,0 +1,72 @@ +#include "../exercise.h" +#include +#include +#include +#include + +// READ: `std::unique_ptr` + +std::vector RECORDS; + +class Resource { + std::string _records; + +public: + void record(char record) { + _records.push_back(record); + } + + ~Resource() { + RECORDS.push_back(_records); + } +}; + +using Unique = std::unique_ptr; +Unique reset(Unique ptr) { + if (ptr) ptr->record('r'); + return std::make_unique(); +} +Unique drop(Unique ptr) { + if (ptr) ptr->record('d'); + return nullptr; +} +Unique forward(Unique ptr) { + if (ptr) ptr->record('f'); + return ptr; +} + +int main(int argc, char **argv) { + std::vector problems[3]; + + drop(forward(reset(nullptr))); + problems[0] = std::move(RECORDS); + + forward(drop(reset(forward(forward(reset(nullptr)))))); + problems[1] = std::move(RECORDS); + + + drop(drop(reset(drop(reset(reset(nullptr)))))); + problems[2] = std::move(RECORDS); + + + // ---- 不要修改以上代码 ---- + + std::vector answers[]{ + {"fd"}, + // TODO: 分析 problems[1] 中资源的生命周期,将记录填入 `std::vector` + // NOTICE: 此题结果依赖对象析构逻辑,平台相关,提交时以 CI 实际运行平台为准 + {"ffr", "d"}, + {"r", "d", "d"}, + }; + + // ---- 不要修改以下代码 ---- + + for (auto i = 0; i < 3; ++i) { + ASSERT(problems[i].size() == answers[i].size(), "wrong size"); + for (auto j = 0; j < problems[i].size(); ++j) { + ASSERT(std::strcmp(problems[i][j].c_str(), answers[i][j]) == 0, "wrong location"); + } + } + + return 0; +} diff --git a/exercises/exercises/31_std_shared_ptr/main.cpp b/exercises/exercises/31_std_shared_ptr/main.cpp new file mode 100644 index 000000000..d35134457 --- /dev/null +++ b/exercises/exercises/31_std_shared_ptr/main.cpp @@ -0,0 +1,45 @@ +#include "../exercise.h" +#include + +// READ: `std::shared_ptr` +// READ: `std::weak_ptr` + +// TODO: 将下列 `?` 替换为正确的值 +int main(int argc, char **argv) { + auto shared = std::make_shared(10); + std::shared_ptr ptrs[]{shared, shared, shared}; + + std::weak_ptr observer = shared; + ASSERT(observer.use_count() == 4, ""); + + ptrs[0].reset(); + ASSERT(observer.use_count() == 3, ""); + + ptrs[1] = nullptr; + ASSERT(observer.use_count() == 2, ""); + + ptrs[2] = std::make_shared(*shared); + ASSERT(observer.use_count() == 1, ""); + + ptrs[0] = shared; + ptrs[1] = shared; + ptrs[2] = std::move(shared); + ASSERT(observer.use_count() == 3, ""); + + std::ignore = std::move(ptrs[0]); + ptrs[1] = std::move(ptrs[1]); + ptrs[1] = std::move(ptrs[2]); + ASSERT(observer.use_count() == 2, ""); + + shared = observer.lock(); + ASSERT(observer.use_count() == 3, ""); + + shared = nullptr; + for (auto &ptr : ptrs) ptr = nullptr; + ASSERT(observer.use_count() == 0, ""); + + shared = observer.lock(); + ASSERT(observer.use_count() == 0, ""); + + return 0; +} diff --git a/exercises/exercises/32_std_transform/main.cpp b/exercises/exercises/32_std_transform/main.cpp new file mode 100644 index 000000000..ad386804c --- /dev/null +++ b/exercises/exercises/32_std_transform/main.cpp @@ -0,0 +1,26 @@ +#include "../exercise.h" +#include +#include +#include + +// READ: `std::transform` +// READ: `std::vector::begin` + + + +int main(int argc, char **argv) { + std::vector val{8, 13, 21, 34, 55}; + // TODO: 调用 `std::transform`,将 `v` 中的每个元素乘以 2,并转换为字符串,存入 `ans` + // std::vector ans + std::vector ans(val.size()); + std::transform(val.begin(), val.end(), ans.begin(), [](int x) { + return std::to_string(x * 2); + }); + ASSERT(ans.size() == val.size(), "ans size should be equal to val size"); + ASSERT(ans[0] == "16", "ans[0] should be 16"); + ASSERT(ans[1] == "26", "ans[1] should be 26"); + ASSERT(ans[2] == "42", "ans[2] should be 42"); + ASSERT(ans[3] == "68", "ans[3] should be 68"); + ASSERT(ans[4] == "110", "ans[4] should be 110"); + return 0; +} diff --git a/exercises/exercises/33_std_accumulate/main.cpp b/exercises/exercises/33_std_accumulate/main.cpp new file mode 100644 index 000000000..53e9af330 --- /dev/null +++ b/exercises/exercises/33_std_accumulate/main.cpp @@ -0,0 +1,19 @@ +#include "../exercise.h" +#include + +// READ: `std::accumulate` + +int main(int argc, char **argv) { + using DataType = float; + int shape[]{1, 3, 224, 224}; + // TODO: 调用 `std::accumulate` 计算: + // - 数据类型为 float; + // - 形状为 shape; + // - 连续存储; + // 的张量占用的字节数 + // int size = + int size = sizeof(DataType) * std::accumulate( + shape, shape + sizeof(shape) / sizeof(int), 1, std::multiplies()); + ASSERT(size == 602112, "4x1x3x224x224 = 602112"); + return 0; +} diff --git a/exercises/exercises/exercise.h b/exercises/exercises/exercise.h new file mode 100644 index 000000000..38f87dc46 --- /dev/null +++ b/exercises/exercises/exercise.h @@ -0,0 +1,19 @@ +#ifndef __EXERCISE_H__ +#define __EXERCISE_H__ + +#include + +#define ASSERT(COND, MSG) \ + if (!(COND)) { \ + std::cerr << "\x1b[31mAssertion failed at line #" << __LINE__ << ": \x1b[0m" << std::endl \ + << std::endl \ + << #COND << std::endl \ + << std::endl \ + << "\x1b[34mMessage:\x1b[0m" << std::endl \ + << std::endl \ + << MSG << std::endl \ + << std::endl; \ + exit(1); \ + } + +#endif// __EXERCISE_H__ diff --git a/exercises/exercises/xmake.lua b/exercises/exercises/xmake.lua new file mode 100644 index 000000000..0c3b11bfd --- /dev/null +++ b/exercises/exercises/xmake.lua @@ -0,0 +1,143 @@ +add_rules("mode.debug", "mode.release") +set_encodings("utf-8") +set_warnings("all") +set_kind("binary") +set_languages("cxx17") + +-- 格式化输出 +target("exercise00") + add_files("00_hello_world/main.cpp") + +-- 变量与运算符 +target("exercise01") + add_files("01_variable&add/main.cpp") + +-- 函数、声明和定义 +target("exercise02") + add_files("02_function/main.cpp") + +-- 形参实参 +target("exercise03") + add_files("03_argument¶meter/main.cpp") + +-- static 关键字 +target("exercise04") + add_files("04_static/main.cpp") + +-- constexpr 编译时常量和运算 +target("exercise05") + add_files("05_constexpr/main.cpp") + +-- 数组 +target("exercise06") + add_files("06_array/main.cpp") + +-- 循环 +target("exercise07") + add_files("07_loop/main.cpp") + +-- 指针 +target("exercise08") + add_files("08_pointer/main.cpp") + +-- 枚举/联合体 +target("exercise09") + add_files("09_enum&union/main.cpp") + +-- “普通”类型 +target("exercise10") + add_files("10_trivial/main.cpp") + +-- 方法 +target("exercise11") + add_files("11_method/main.cpp") + +-- const 修饰方法 +target("exercise12") + add_files("12_method_const/main.cpp") + +-- 类 +target("exercise13") + add_files("13_class/main.cpp") + +-- 析构器 +target("exercise14") + add_files("14_class_destruct/main.cpp") + +-- 复制构造函数 +target("exercise15") + add_files("15_class_clone/main.cpp") + +-- 移动语义 +target("exercise16") + add_files("16_class_move/main.cpp") + +-- 派生 +target("exercise17") + add_files("17_class_derive/main.cpp") + +-- 虚函数 +target("exercise18") + add_files("18_class_virtual/main.cpp") + +-- 虚析构函数 +target("exercise19") + add_files("19_class_virtual_destruct/main.cpp") + +-- 函数模板 +target("exercise20") + add_files("20_function_template/main.cpp") + +-- 习题:用于编译器的运行时类型 +target("exercise21") + add_files("21_runtime_datatype/main.cpp") + +-- 类模板 +target("exercise22") + add_files("22_class_template/main.cpp") + +-- 模板非类型实参 +target("exercise23") + add_files("23_template_const/main.cpp") + +-- std::array +target("exercise24") + add_files("24_std_array/main.cpp") + +-- std::vector +target("exercise25") + add_files("25_std_vector/main.cpp") + +-- std::vector +target("exercise26") + add_files("26_std_vector_bool/main.cpp") + +-- 习题:步长计算 +target("exercise27") + add_files("27_strides/main.cpp") + +-- std::string +target("exercise28") + add_files("28_std_string/main.cpp") + +-- std::map +target("exercise29") + add_files("29_std_map/main.cpp") + +-- std::transform +target("exercise30") + add_files("30_std_unique_ptr/main.cpp") + +-- std::accumulate +target("exercise31") + add_files("31_std_shared_ptr/main.cpp") + +-- std::transform +target("exercise32") + add_files("32_std_transform/main.cpp") + +-- std::accumulate +target("exercise33") + add_files("33_std_accumulate/main.cpp") + +-- TODO: lambda; deque; forward_list; fs; thread; mutex;