# 程式設計Ⅱ【筆記Ⅱ】 * [name=Ivy Lin] * [筆記Ⅰ](https://hackmd.io/lvLLSXxoTo6hLxpEyBIJbQ) * [筆記ⅠⅠⅠ](https://hackmd.io/JpCPopTTTLyZ91yi279ewA) * [//筆記Ⅳ](https://hackmd.io/Ts0lnTMpQgGPGw7cvc3X-A) # 第四週 <*2023.10.3*> ### ***Binary Tree*** ### **遍歷樹的規則** * *pre-order* > 中 - 左 - 右 * *in-order* > 左 - 中 - 右 * *post-order* > 右 - 中 - 左 ### 從樹=>各種排列 > 從一棵樹得知它的各種order排列(使用遞迴) ### 實作 ```clike= #include <stdio.h> #include <stdlib.h> typedef struct _Node { int num; struct _Node *left; struct _Node *right; } Node; Node *new_node(int number) { Node *p = (Node *)malloc(sizeof(Node)); p->num = number; p->left = NULL; p->right = NULL; return p; } void in_order(Node *root) { if (root) { in_order(root->left); printf(" %d ", root->num); in_order(root->right); } } void pre_order(Node *root) { if (root) { printf(" %d ", root->num); pre_order(root->left); pre_order(root->right); } } void post_order(Node *root) { if (root) { post_order(root->right); printf(" %d ", root->num); post_order(root->left); } } int main() { Node *root; root = new_node(2); root->left = new_node(7); root->right = new_node(5); root->left->left = new_node(3); root->left->right = new_node(6); printf("in_order tree =>"); in_order(root); printf("\n"); printf("pre_order tree =>"); pre_order(root); printf("\n"); printf("post_order tree =>"); post_order(root); printf("\n"); return 0; } ``` ### 利用其中兩種解法得出樹的結構 > 利用 pre-order來決定root,尋找inorder中的root點並將區域切成左右兩塊,遞迴施作即可找到樹的結構 ```clike= #include <stdio.h> #include <stdlib.h> #define max 100 // int cur; typedef struct _Node { int num; struct _Node *left; struct _Node *right; } Node; int pre_seq[max]; int in_seq[max]; Node *new_node(int number) { Node *p = (Node *)malloc(sizeof(Node)); p->num = number; p->left = NULL; p->right = NULL; return p; } void post_order(Node *root) { if (root) { post_order(root->left); post_order(root->right); printf(" %d ", root->num); } } int find_in_seq(int start, int end, int num) { for (int i = start; i <= end; i++) { if (in_seq[i] == num) return i; } return -1; } // 要知道inorder的哪個位置 Node *build_tree(int start, int end) { // 找到目前的節點本人在哪 static int cur = 0; if (start > end) return NULL; Node *p = new_node(pre_seq[cur]); cur++; if (start < end) { int inde = find_in_seq(start, end, p->num); p->left = build_tree(start, inde - 1); // tree p->right = build_tree(inde + 1, end); } // tree return p; } int main() { Node *root; int n; scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &in_seq[i]); for (int i = 0; i < n; i++) scanf("%d", &pre_seq[i]); root = build_tree(0, n - 1); // post_order(root); return 0; } ``` ### 砍樹 > 將每個節點都free掉 > 需要從葉子節點開始free(要不然會少free) //delete的函式 ```clike= void delete(Node *p) { if (p != NULL) { delete (p->left); delete (p->right); free(p); } } ``` ### 尋找最大值 >先從左邊最大和自己比較 >再找尋右邊最大值 >最終回傳total max ```clike= int find_max(Node *p) { if (p != NULL) { int left_max = find_max(p->left); if (left_max < p->num) left_max = p->num; int right_max = find_max(p->right); return (left_max > right_max) ? left_max : right_max; } else return -1; } ``` ### 畫出一棵樹 > 使用 GVEdit 程式就可以把上面的 binary tree 描述檔,轉成圖片 程式需要的格式=> ```clike= digraph T { 2 -> 7; 2 -> 5; 7 -> 3; 7 -> 6; 6 -> 8; 6 -> 11; 5 -> 9; 9 -> 4; } ``` 實作程式碼 => ```clike= void printTree(FILE *fout, Node *tree) { if (tree!=NULL) { if (tree->left!=NULL) fprintf(fout, "%d -> %d;\n", tree->data, tree->left->data); if (tree->right!=NULL) fprintf(fout, "%d -> %d;\n", tree->data, tree->right->data); printTree(fout, tree->left); printTree(fout, tree->right); } } void writeGV(Node *tree) { FILE *fout = fopen("tree.gv", "w"); fprintf(fout, "digraph T {\n"); printTree(fout, tree); fprintf(fout, "}\n"); } ``` # 第5週 <*2023.10.10*> > 上機 # 第6週 <*2023.10.17*> ## ***分析 Binary Expression 以及建構 Syntax Tree*** ### ***Expression 運算(bool)*** > 利用優先運算子讀取數學運算式,讓電腦可以計算 > 作法: > 自行定義一個型態,對照輸入的字串遞迴計算 ```clike= // bool states #include <stdio.h> #include <stdlib.h> #include <ctype> #include <string.h> typedef enum { VA, VB, VC, VD, OP_AND, OP_OR, END } TokenSet; // 將每個變數取名字!(可以直接寫變數名稱當作值來使用) char sym[] = "ABCD&|"; char expr[256]; TokenSet getnext(int reset) { static int idx; TokenSet ret = END; if (reset) { idx = 0; return END; } // isspace => 判斷現在的這格是不是空白字元 while (expr[idx] != '\0' && isspace(expr[idx])) idx++; if (expr[idx] == '\0') return END; else { // 另一種寫法:(size_t)i<strlen(sym) // 將i強制轉為(size_t)的型式=>無符號整數(通常用於表達內存和索引) int l = strlen(sym); for (int i = 0; i < l - 1; i++) if (sym[i] == expr[idx]) ret = i; idx++; return ret; } } void evaluate(int A, int B, int C, int D) { TokenSet tok; int ev1, ev2; tok = getnext(0); switch (tok) { case VA: return A; case VB: return B; case VC: return C; case VD: return D; case OP_AND: ev1 = evaluate(A, B, C, D); ev2 = evaluate(A, B, C, D); return ev1 && ev2; // break; case OP_OR: ev1 = evaluate(A, B, C, D); ev2 = evaluate(A, B, C, D); return ev1 || ev2; case END: tok = getnext(1); return evaluate(A, B, C, D); } return 0; } int main() { fgets(expr, sizeof(expr), stdin); int len = strlen(expr); if (len > 0 && expr[len - 1] == '\n') { --len; expr[len] = '\n'; } for (int i = 0; i < 16; i++) { printf("%d%d%d%d:%d\n", (i & 8) >> 3, (i & 4) >> 2, (i & 2) >> 1, (i & 1)); evaluate((i & 8) >> 3, (i & 4) >> 2, (i & 2) >> 1, i % 1); } return 0; } ``` ### ***Expression 運算(十進位)*** ```clike= //程設一的範例 #include <stdio.h> #include <ctype.h> int calculate(void); int main(void) { printf("=%d\n", calculate()); return 0; } int calculate(void) { int c; int ans; int op1, op2; c = getchar(); if (isspace(c)) { ans = calculate(); } else if (c == '+') { printf("("); op1 = calculate(); printf("+"); op2 = calculate(); printf(")"); ans = op1 + op2; } else if (c == '-') { printf("("); op1 = calculate(); printf("-"); op2 = calculate(); printf(")"); ans = op1 - op2; } else if (c == '*') { printf("("); op1 = calculate(); printf("*"); op2 = calculate(); printf(")"); ans = op1 * op2; } else if (isdigit(c)) { ungetc(c, stdin); scanf("%d", &ans); printf("%d", ans); } return ans; } ``` ### ***建立 Syntax Tree*** ```clike= //範例程式碼 # include <string.h> # include <stdlib.h> # include <ctype.h> # define MAXEXPR 256 char expr[MAXEXPR]; int pos; typedef enum {VAR_A, VAR_B, VAR_C, VAR_D, OP_AND, OP_OR, END } TokenSet; char sym[]="ABCD&|()"; typedef struct _Node { TokenSet data; struct _Node *left, *right; } BTNode; BTNode* EXPR(); BTNode* FACTOR(); /* create a node without any child.*/ BTNode* makeNode(char c){ int i; BTNode *node = (BTNode*) malloc(sizeof(BTNode)); for (i = 0; (unsigned int)i < strlen(sym); i++) if (c==sym[i]) node->data = i; node->left = NULL; node->right = NULL; return node; } /* clean a tree.*/ void freeTree(BTNode *root){ if (root!=NULL) { freeTree(root->left); freeTree(root->right); free(root); } } /* print a tree by pre-order. */ void printPrefix(BTNode *root){ if (root != NULL) { printf("%c",sym[root->data]); printPrefix(root->left); printPrefix(root->right); } } /* FACTOR = VAR | (EXPR) */ BTNode* FACTOR(){ char c; BTNode *node = NULL; if (pos>=0) { c = expr[pos--]; if (c>= 'A' && c<='D'){ // apply the rule FACTOR = VAR // make a new node for VAR node = makeNode(c); } else if (c==')') { // apply the rule FACTOR = (EXPR) // get the node pointer from recusive call of EXPR() node = EXPR(); if(expr[pos--]!= '(') { // the left parenthesis is needed printf("Error: not matching parenthesis!\n"); freeTree(node); } } } return node; } /* parse an infix expression and generate a syntax tree. EXPR = FACTOR| EXPR OP FACTOR */ BTNode* EXPR(){ char c; BTNode *node = NULL, *right=NULL; if (pos>=0) { // if the expression has length > 1. // get the pointer to the right child from calling the function FACTOR() right = FACTOR(); if (pos>0) { c = expr[pos]; if (c=='&' || c=='|'){ // apply the rule EXPR = EXPR OP FACTOR // make a new node for the OP node = makeNode(c); // set the node's right child as ... node->right = right; pos--; // this step is important // set the node's left child from recursive call of EXPR() node->left = EXPR(); } else node = right; // apply the rule EXPR = FACTOR } else node = right; // apply the rule EXPR = FACTOR } return node; } int main(void) { size_t len; fgets(expr, sizeof(expr), stdin); len = strlen(expr); if (len > 0 && expr[len-1] == '\n') { --len; expr[len] = '\0'; } pos = strlen(expr) - 1; BTNode *root = EXPR(); printPrefix(root); freeTree(root); return 0; } ``` > 利用樹狀結構,遍歷樹的方式來計算*prefix expression*的字串 ```clike= //程式碼本人+註解們 // FACTOR | EXPR 語法規則 /* FACTOE = VAR 或 (EXPR) EXPR = FACTOR 或 EXPR OP FACTOR */ /* 演算法本人 1)先使用EXPR的與法 => 從最底層取出一個FACTOR 2)如果有一個OP在此FACTOR中 => FACTOR是OP的右小孩 => 遞迴其他的部分並讓其成為OP的左小孩 */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> // Enum typedef enum { VAR_A, VAR_B, VAR_C, VAR_D, OP_AND, OP_OR, END } TokenSet; // Structure typedef struct _Node { TokenSet data; struct _Node *left, *right; } BTNode; // Globol char expr[256]; int pos; char sym[] = "ABCD&|()"; BTNode *EXPR(); BTNode *FACTOR(); // Make tree BTNode *makeNode(char c) { int i; BTNode *node = (BTNode *)malloc(sizeof(BTNode)); for (i = 0; (unsigned int)i < strlen(sym); i++) if (c == sym[i]) node->data = i; node->left = NULL; node->right = NULL; return node; } // Free tree void freeTree(BTNode *root) { if (root != NULL) { freeTree(root->left); freeTree(root->right); free(root); } } // Print tree => Count ans void printPrefix(BTNode *root) { if (root != NULL) { printf("%c", sym[root->data]); printPrefix(root->left); printPrefix(root->right); } } int evalPrefix(BTNode *root, int A, int B, int C, int D) { int ev1, ev2; if (root != NULL) { printf("%c", sym[root->data]); if (root->data == OP_AND) { ev1 = evalPrefix(root->left, A, B, C, D); ev2 = evalPrefix(root->right, A, B, C, D); return ev1 && ev2; } else if (root->data == OP_OR) { ev1 = evalPrefix(root->left, A, B, C, D); ev2 = evalPrefix(root->right, A, B, C, D); return ev1 || ev2; } else { switch (root->data) { case VAR_A: return A; case VAR_B: return B; case VAR_C: return C; case VAR_D: return D; } } } } // Function1 FACTOR BTNode *FACTOR() { char c; BTNode *node = NULL; if (pos >= 0) { c = expr[pos--]; if (c >= 'A' && c <= 'D') { // apply the rule FACTOR = VAR // make a new node for VAR node = makeNode(c); } else if (c == ')') { // apply the rule FACTOR = (EXPR) // get the node pointer from recusive call of EXPR() node = EXPR(); if (expr[pos--] != '(') { // the left parenthesis is needed printf("Error: not matching parenthesis!\n"); freeTree(node); } } } return node; } // Function2 EXPR BTNode *EXPR() { char c; BTNode *node = NULL, *right = NULL; if (pos >= 0) { // if the expression has length > 1. // get the pointer to the right child from calling the function FACTOR() right = FACTOR(); if (pos > 0) { c = expr[pos]; if (c == '&' || c == '|') { // apply the rule EXPR = EXPR OP FACTOR // make a new node for the OP node = makeNode(c); // set the node's right child as ... node->right = right; pos--; // this step is important // set the node's left child from recursive call of EXPR() node->left = EXPR(); } else node = right; // apply the rule EXPR = FACTOR } else node = right; // apply the rule EXPR = FACTOR } return node; } // Main int main(void) { size_t len; fgets(expr, sizeof(expr), stdin); len = strlen(expr); if (len > 0 && expr[len - 1] == '\n') { --len; expr[len] = '\0'; } pos = strlen(expr) - 1; BTNode *root = EXPR(); printPrefix(root); printf("%d\n", evalPrefix(root, 0, 1, 1, 1)); freeTree(root); return 0; } ``` # 第7週 <*2023.10.24*> * ***Project*** > [Wikipedia](http://rosettacode.org/wiki/Arithmetic_Evaluator/C) 1. 目的: 寫出一個計算機 2. 實作: * 用Token記住元素(Operator、Int 等等) * 將對應的字串紀錄在陣列中 * 寫出函數來取出Token 3. 程式碼 * C語言的標頭檔 > ***<引用防護>*** * 像是以下使用#ifndef #endif 等等,為了避免重覆引用和定義(有些編譯器不允許符號定義兩次) * 基本格式(參考google開源) ```clike= #ifndef 標頭檔的命名_H_ #define 標頭檔的命名_H_ ... #endif //標頭檔的命名_H_ ``` > ***<定義函數宣告>*** * 基本的宣告以及如何使用函數 * #include 引用路徑 / 順序 > [標頭檔介紹參考資料](https://tw-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/headers.html) > [計算機參考程式碼](https://github.com/htchen/calculator_tree/blob/master/main_trial3.c) * 運算規則 1. statement := END | expr END 2. expr := term expr_tail 3. expr_tail := ADDSUB(加或減) term expr_tail | NIL(空字串) 4. term := facter term_tail 5. term_tail := MULDIV factor term_tail | NIL(空字串) 6. factor := INT | ADDSUB(加或減) INT | ADDSUB(加或減) ID | ID ASSIGN expr | ID | LPAREN expr RPAREN * ***標頭檔本人 lex.h*** ```clike= //使用標頭檔慣用的保護程式架構(ifndef-define-endif) //定義globol => TokenSet //定義基本函數宣告(前面加上extern代表函數在其他檔案中定義) #ifndef __LEX__ #define __LEX__ #define MAXLEN 256 typedef enum { UNKNOWN, END, INT, ID, ADDSUB, MULDIV, ASSIGN, LPAREN, RPAREN } TokenSet; extern int match(TokenSet token); extern void advance(void); extern char *getLexeme(void); #endif // __LEX__ ``` * ***檔案1 lex.c*** ```clike= #include <stdio.h> #include <ctype.h> #include <string.h> //引用自行定義的標頭檔 #include "lex.h" //定義此檔案會使用的globol static TokenSet getToken(void); static TokenSet lookahead = UNKNOWN; static char lexeme[MAXLEN]; //主函式 TokenSet getToken(void) { int i; char c; while ((c = fgetc(stdin)) == ' ' || c == '\t') ; // ©¿²¤ªÅ¥Õ¦r¤¸ if (isdigit(c)) { lexeme[0] = c; c = fgetc(stdin); i = 1; while (isdigit(c) && i < MAXLEN) { lexeme[i] = c; ++i; c = fgetc(stdin); } ungetc(c, stdin); lexeme[i] = '\0'; return INT; } else if (c == '+' || c == '-') { lexeme[0] = c; lexeme[1] = '\0'; return ADDSUB; } else if (c == '*' || c == '/') { lexeme[0] = c; lexeme[1] = '\0'; return MULDIV; } else if (c == '\n') { lexeme[0] = '\0'; return END; } else if (c == '=') { strcpy(lexeme, "="); return ASSIGN; } else if (c == '(') { strcpy(lexeme, "("); return LPAREN; } else if (c == ')') { strcpy(lexeme, ")"); return RPAREN; } else if (isalpha(c) || c == '_') { lexeme[0] = c; c = fgetc(stdin); i = 1; while (isalpha(c) || isdigit(c) || c == '_') { lexeme[i] = c; ++i; c = fgetc(stdin); } ungetc(c, stdin); lexeme[i] = '\0'; return ID; } else { return UNKNOWN; } } //len.h中宣告過的函式 => 直接定義函數內容 void advance(void) { lookahead = getToken(); } int match(TokenSet token) { if (lookahead == UNKNOWN) advance(); return token == lookahead; } char *getLexeme(void) { return lexeme; } ``` * ***main函式 main.c*** ### Syntax Tree * 改建成Syntax Tree的版本 * lex.c / lex.h和原本相同 * [完整程式碼參考](https://github.com/htchen/calculator_tree) ```clike= #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> //引用標頭檔 #include "lex.h" //宣告檔案會使用到的函式 void error(ErrorType errorNum); //宣告會使用到的struct typedef struct { char name[MAXLEN]; int val; } Symbol; typedef struct _Node { char lexeme[MAXLEN]; TokenSet token; int val; struct _Node *left, *right; } BTNode; typedef enum { MISPAREN, NOTNUMID, NOTFOUND, RUNOUT, NAN } ErrorType; //global #define TBLSIZE 65535 Symbol table[TBLSIZE]; int sbcount = 0; void statement(void); BTNode *expr(void); BTNode *term(void); BTNode *factor(void); int getval(void); int setval(char *, int); /* create a node without any child */ BTNode *makeNode(TokenSet tok, const char *lexe) { BTNode *node = (BTNode *)malloc(sizeof(BTNode)); strcpy(node->lexeme, lexe); node->token = tok; node->val = 0; node->left = NULL; node->right = NULL; return node; } /* clean a tree */ void freeTree(BTNode *root) { if (root != NULL) { freeTree(root->left); freeTree(root->right); free(root); } } /* print a tree by pre-order */ void printPrefix(BTNode *root) { if (root != NULL) { printf("%s ", root->lexeme); printPrefix(root->left); printPrefix(root->right); } } /* traverse the syntax tree by pre-order and evaluate the underlying expression */ int evaluateTree(BTNode *root) { int retval = 0, lv, rv; if (root != NULL) { switch (root->token) { case ID: case INT: retval = root->val; break; case ASSIGN: case ADDSUB: case MULDIV: lv = evaluateTree(root->left); rv = evaluateTree(root->right); if (strcmp(root->lexeme, "+") == 0) retval = lv + rv; else if (strcmp(root->lexeme, "-") == 0) retval = lv - rv; else if (strcmp(root->lexeme, "*") == 0) retval = lv * rv; else if (strcmp(root->lexeme, "/") == 0) { if (rv == 0) error(NAN); else retval = lv / rv; } else if (strcmp(root->lexeme, "=") == 0) retval = setval(root->left->lexeme, rv); break; default: retval = 0; } } return retval; } int getval(void) { int i, retval, found; if (match(INT)) { retval = atoi(getLexeme()); } else if (match(ID)) { i = 0; found = 0; retval = 0; while (i < sbcount && !found) { if (strcmp(getLexeme(), table[i].name) == 0) { retval = table[i].val; found = 1; break; } else i++; } if (!found) { if (sbcount < TBLSIZE) { strcpy(table[sbcount].name, getLexeme()); table[sbcount].val = 0; sbcount++; } else error(RUNOUT); } } return retval; } int setval(char *str, int val) { int i, retval = 0; i = 0; while (i < sbcount) { if (strcmp(str, table[i].name) == 0) { table[i].val = val; retval = val; break; } else i++; } return retval; } // expr := term expr_tail // expr_tail := ADDSUB term expr_tail | NIL BTNode *expr(void) { BTNode *retp, *left; retp = left = term(); while (match(ADDSUB)) { // tail recursion => while retp = makeNode(ADDSUB, getLexeme()); advance(); retp->right = term(); retp->left = left; left = retp; } return retp; } // term := factor term_tail // term_tail := MULDIV factor term_tail | NIL BTNode *term(void) { BTNode *retp, *left; retp = left = factor(); while (match(MULDIV)) { // tail recursion => while retp = makeNode(MULDIV, getLexeme()); advance(); retp->right = factor(); retp->left = left; left = retp; } return retp; } BTNode *factor(void) { BTNode *retp = NULL; char tmpstr[MAXLEN]; if (match(INT)) { retp = makeNode(INT, getLexeme()); retp->val = getval(); advance(); } else if (match(ID)) { BTNode *left = makeNode(ID, getLexeme()); left->val = getval(); strcpy(tmpstr, getLexeme()); advance(); if (match(ASSIGN)) { retp = makeNode(ASSIGN, getLexeme()); advance(); retp->right = expr(); retp->left = left; } else retp = left; } else if (match(ADDSUB)) { strcpy(tmpstr, getLexeme()); advance(); if (match(ID) || match(INT)) { retp = makeNode(ADDSUB, tmpstr); if (match(ID)) retp->right = makeNode(ID, getLexeme()); else retp->right = makeNode(INT, getLexeme()); retp->right->val = getval(); retp->left = makeNode(INT, "0"); retp->left->val = 0; advance(); } else error(NOTNUMID); } else if (match(LPAREN)) { advance(); retp = expr(); if (match(RPAREN)) advance(); else error(MISPAREN); } else error(NOTNUMID); return retp; } void error(ErrorType errorNum) { switch (errorNum) { case MISPAREN: fprintf(stderr, "Mismatched parenthesis\n"); break; case NOTNUMID: fprintf(stderr, "Number or identifier expected\n"); break; case NOTFOUND: fprintf(stderr, "%s not defined\n", getLexeme()); break; case RUNOUT: fprintf(stderr, "Out of memory\n"); break; case NAN: fprintf(stderr, "Not a number\n"); } exit(0); } void statement(void) { BTNode *retp; if (match(END)) { printf(">> "); advance(); } else { retp = expr(); if (match(END)) { printf("%d\n", evaluateTree(retp)); printPrefix(retp); printf("\n"); freeTree(retp); printf(">> "); advance(); } } } int main() { printf(">> "); while (1) statement(); return 0; } ``` # 第八週 <*2023.10.30*> ## 組合語言 * ***Get assembly code*** > *in terminal* > => ***gcc -S ex1.c*** // *get assembly code of ex1.c* > => ***-gcc -S -masm=intel ex1.c*** // *x86 CPU & intel style's assembly* > => ***-gcc ex1.c*** //*compile assembly code* * ***Assembly v.s. C code*** ```clike= int b; int main(){ b=3; int a; a=5; } ``` ```asm= //assignment mov DWORD PTR b,3; mov DWORD PTR [esp+12],5 ``` * ***MOV instruction*** 語法 => *MOV destination, source *//將右側資料搬動到左側 * ***DWORD data size*** | *Type Specifier* | *Bytes addressed* | | -------- | -------- | | *BYTE* | 1 | | *WORD* | 2 | | *DWORD* | 4 | | *QWORD* |8 | * ***Indiret memory access*** *Register 'esp' holds an address of memory*