diff --git a/.clang-format b/.clang-format index f6cb8ad..35cc166 100644 --- a/.clang-format +++ b/.clang-format @@ -1 +1,2 @@ BasedOnStyle: Google +ColumnLimit: 240 diff --git a/CMakeLists.txt b/CMakeLists.txt index e201039..37bc882 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,3 +56,7 @@ target_link_libraries(tetris PRIVATE ncursesw) ##### snake ##### add_executable(snake snake.cpp Snake.h Snake.cpp Map.h Map.cpp vec2.h vec2.cpp) target_link_libraries(snake PRIVATE ncursesw) + +##### calculator ##### +add_executable(calculator calculator.cpp) +target_link_libraries(calculator) diff --git a/README.md b/README.md index b88a0a3..9f61e4a 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,10 @@ > 我们在代码中以`///`开头的注释为知识性注释,`//`开头的注释为针对某一段代码的注释 +> 我们用`💡`标记重要思想 + +> 我们用`🏷️`标记代码中的知识点示例 + --- 代码列表: @@ -40,6 +44,8 @@ * [Map.cpp](Map.cpp) * [vec2.h](vec2.h) * [vec2.cpp](vec2.cpp) +4. Calculator + * [calculator.cpp](calculator.cpp) --- diff --git a/Snake.h b/Snake.h index 8015234..a5fb7ac 100644 --- a/Snake.h +++ b/Snake.h @@ -9,9 +9,9 @@ class Map; enum SnakeStepResult { SNAKE_STEP_NORMAL, // = 0 - SNAKE_STEP_COLLISION, // = last + 1 = 2 - SNAKE_STEP_GOT_FOOD, // = last + 1 = 3 - SNAKE_STEP_TOO_LONG, // = last + 1 = 4 + SNAKE_STEP_COLLISION, // = last + 1 = 1 + SNAKE_STEP_GOT_FOOD, // = last + 1 = 2 + SNAKE_STEP_TOO_LONG, // = last + 1 = 3 }; class Snake { diff --git a/calculator.cpp b/calculator.cpp new file mode 100644 index 0000000..0a669e9 --- /dev/null +++ b/calculator.cpp @@ -0,0 +1,220 @@ +#include +#include + +void TakeBlank(const std::string &expr, size_t &pos) { + for (; pos < expr.size(); ++pos) { + if (std::isblank(expr[pos])) { + continue; + } else { + return; + } + } +} + +bool TakeNumber(const std::string &expr, double &opera, size_t &pos) { + size_t begin = pos; + size_t len{}; + for (; pos < expr.size(); ++pos) { + if (std::isdigit(expr[pos]) || expr[pos] == '.' || (len == 0 && expr[pos] == '-')) { + ++len; + continue; + } else { + break; + } + } + if (!len) { + return false; + } + opera = std::stod(expr.substr(begin, len)); + return true; +} + +bool TakeOp(const std::string &expr, char &op, size_t &pos) { + if (expr.size() <= pos) { + return false; + } + switch (expr[pos]) { + case '+': + case '-': + case '*': + case '/': + case '(': + case ')': + op = expr[pos]; + ++pos; + return true; + default: + return false; + } +} + +bool TestOp(const std::string &expr, char op, size_t pos) { + if (expr.size() <= pos) { + return false; + } + return expr[pos] == op; +} + +void ReportError(const std::string &expr, size_t pos, const std::string &message) { + std::cerr << expr << std::endl; + std::cerr << std::string(pos, ' ') << "^ " << message; +} + +int GetPri(char op) { + switch (op) { + case '+': + case '-': + return 1; + case '*': + case '/': + return 2; + default: + std::cerr << "ERROR: invalid operator" << std::endl; + std::exit(1); + } +} + +void CalTop(std::stack &opera_stack, std::stack &op_stack) { + if (opera_stack.size() < 2) { + std::cerr << "ERROR: invalid cal top" << std::endl; + std::exit(1); + } + + if (op_stack.size() != opera_stack.size() - 1) { + std::cerr << "ERROR: invalid op stack" << std::endl; + std::exit(1); + } + + char op = op_stack.top(); + op_stack.pop(); + double opera[2]{}; + opera[1] = opera_stack.top(); + opera_stack.pop(); + opera[0] = opera_stack.top(); + opera_stack.pop(); + double tmp_res{}; + + switch (op) { + case '+': + tmp_res = opera[0] + opera[1]; + break; + case '-': + tmp_res = opera[0] - opera[1]; + break; + case '*': + tmp_res = opera[0] * opera[1]; + break; + case '/': + tmp_res = opera[0] / opera[1]; + break; + default: + std::cerr << "ERROR: invalid operator" << std::endl; + std::exit(1); + } + opera_stack.push(tmp_res); +} + +void Cal(std::stack &opera_stack, std::stack &op_stack) { + for (; 2 <= opera_stack.size();) { + CalTop(opera_stack, op_stack); + } +} + +double EvalExpr(const std::string &expr, size_t &pos) { + size_t pos_start = pos; + std::stack opera_stack; + std::stack op_stack; + for (;;) { + char op{}; + double opera{}; + + TakeBlank(expr, pos); + if (pos == expr.size()) { + Cal(opera_stack, op_stack); + if (opera_stack.size() == 1 && op_stack.empty()) { + std::cout << "EXPR: " << expr << " RES: " << opera_stack.top() << std::endl; + return opera_stack.top(); + } else { + // ReportError(expr, pos, "ERROR: invalid expr"); + // std::exit(1); + + // let follow-up report error + } + } + if (TestOp(expr, ')', pos)) { + Cal(opera_stack, op_stack); + if (opera_stack.size() == 1 && op_stack.empty()) { + std::cout << "SUB EXPR: " << expr.substr(pos_start, pos - pos_start) << " RES: " << opera_stack.top() << std::endl; + return opera_stack.top(); + } else { + // ReportError(expr, pos, "ERROR: invalid expr"); + // std::exit(1); + + // let follow-up report error + } + } + + if (opera_stack.size() == op_stack.size()) { + if (TestOp(expr, '(', pos)) { + // take ( + TakeOp(expr, op, pos); + + // eval () + double tmp_result = EvalExpr(expr, pos); + opera_stack.push(tmp_result); + + // take ) + if (!TestOp(expr, ')', pos)) { + ReportError(expr, pos, "ERROR: expect )"); + std::exit(1); + } + TakeOp(expr, op, pos); + continue; + } + + if (TakeNumber(expr, opera, pos)) { + opera_stack.push(opera); + continue; + } else { + ReportError(expr, pos, "ERROR: expect number"); + std::exit(1); + } + } + + if (TakeOp(expr, op, pos)) { + if (!op_stack.empty() && GetPri(op) <= GetPri(op_stack.top())) { + CalTop(opera_stack, op_stack); + } + op_stack.push(op); + continue; + } else { + ReportError(expr, pos, "ERROR: expect operator"); + std::exit(1); + } + } +} + +int main() { + std::cout << "Calculator!" << std::endl; + std::cout << "Expression example: " << std::endl; + std::cout << " 1 + 1" << std::endl; + std::cout << " 2 * (2 + 2)" << std::endl; + std::cout << "Please enter expression:" << std::endl; + // handle line expr loop + for (;;) { + std::string expr; + std::getline(std::cin, expr); + + if (expr.empty()) { + break; + } + if (expr[0] == '#') { + continue; + } + + // eval expr + size_t pos{}; + EvalExpr(expr, pos); + } + return 0; +} diff --git a/hello-world.cpp b/hello-world.cpp index 181ea63..8176120 100644 --- a/hello-world.cpp +++ b/hello-world.cpp @@ -2,6 +2,9 @@ /// C++基础知识: /// 1. 编译的四个过程 https://blog.csdn.net/qq_24654009/article/details/129170760#compilation-process /// 1. 预处理 +/// 2. 编译 +/// 3. 汇编 +/// 4. 链接 /// 2. 编译单元 ///============================================================================= /// 理解或思想: @@ -9,7 +12,7 @@ /// 👉 编译单元:即编译的基本单位;可以认为一个cpp文件即对应一个编译单元 ///============================================================================= /// 思想!: -/// 👉 对待C++:💡要像对待一门口语 +/// 👉 对待C++:💡要像对待一门口语,写代码即写文章 /// 👉 编译:将一种语言转化为另一种语言;即💡翻译 /// 👉 编码原则:💡先说服自己,再说服机器 ///============================================================================= @@ -28,7 +31,8 @@ /// 1. 预处理;文本替换 /// 2. 编译(区别于整个编译过程);将源码转换为汇编代码 /// 3. 汇编;将汇编代码转换为机器码 -/// 4. 链接;将机器码链接为可执行程序 +/// 4. 链接;将机器码链接为可执行程序(或动态库), +/// 链接会组合若干机器码(如动态库)为一个可执行程序(或动态库) /// 我们可以在hello-world目标后打开各个文件 diff --git a/snake.cpp b/snake.cpp index d7844ee..617e512 100644 --- a/snake.cpp +++ b/snake.cpp @@ -1,3 +1,60 @@ +///============================================================================= +/// C++基础知识: +/// 1. 多源代码(编译单元)构建 +/// 1. (全局)变量在所有编译单元中唯一 +/// 2. (全局)函数在所有编译单元中唯一 +/// 3. 类型定义在编译单元内唯一,因为类型定义不占内存,仅说明类型的内存结构 +/// 2. 声明(普通变量与函数) +/// 1. 声明全局变量 +/// 1. 声明函数 +/// 4. 定义(普通变量与函数) +/// 1. 定义全局变量 +/// 2. 定义函数 +/// 3. 定义局部变量 +/// 4. 定义局部静态变量(与全局变量相同,仅可见性差异) +/// 5. 定义类(class) +/// 1. 定义成员变量 +/// 2. 声明类静态变量(与全局变量相同) +/// 3. 声明类静态函数(与全局函数相同) +/// 4. 声明成员函数 +/// 5. 定义类静态变量(全局) +/// 6. 定义类静态函数(全局) +/// 6. 定义类成员函数 +/// 6. 成员访问 +/// 1. 成员变量访问 +/// 2. 静态成员变量访问 +/// 7. 函数调用 +/// 1. 成员函数调用 +/// 2. 静态成员函数调用 +/// 8. 构造函数 +/// 1. 默认构造函数 +/// 2. 带参构造函数 +/// 3. 复制构造函数 +/// 4. 移动构造函数 +/// 9. 析构函数 +/// 11. 初始化列表 +/// 1. 成员变量的默认初始化 +/// 2. 在构造函数上创建初始化列表 +/// 12. 类中变量与函数的可见性 +/// 1. private +/// 2. protected +/// 3. public +/// 13. 定义枚举类型 +///============================================================================= +/// 理解或思想: +/// 👉 静态成员变量:💡与全局变量无异 +/// 👉 静态成员函数:💡与普通函数无异 +/// 👉 成员函数: +/// 💡隐含一个this参数 +/// 💡底层实现上与普通函数无异 +/// 👉 C++的默认行为: +/// 如果我们没有定义构造函数,则C++会尝试为我们创建默认构造函数 +/// 如果我们没有定义析构函数,则C++会为我们创建默认析构函数 +///============================================================================= +/// 思想!: +/// 👉 RAII:💡有始有终 +///============================================================================= + // 引入ncurses库,包括类型和函数: // * WINDOW - 窗口类型 // * newwin - 创建窗口 diff --git a/tetris.cpp b/tetris.cpp index 577a5ee..f2d4bf2 100644 --- a/tetris.cpp +++ b/tetris.cpp @@ -20,7 +20,8 @@ /// 6. 表达式 /// 1. 运算符 /// 2. 访问变量 -/// 3. 调用函数 +/// 3. 访问成员变量 +/// 4. 调用函数 /// 7. 逻辑语句 /// 1. 条件语句 if switch(case default break) /// 2. 循环语句 for while do..while -- continue break