Course Introduction
課程說明 (Course Description)
本課程之內容包括:C/C++ 語言基礎語法、程式寫作技巧與觀念及實作範例。透過課堂解釋實作範例搭配作業練習各主題的語法或技巧。
The course will introduce the basics of C/C++ and the concepts and skills of programming. Each theme will be instructed with examples and hands-on labs in-class or off-class.
指定用書 (Text Books)
- S. Prata, C PRIMER PLUS (自由購買)
- 課程講義 (Lecture notes)
參考書籍 (References)
國立清華大學開放式課程OpenCourseWare(NTHU, OCW) - 計算機程式設計一(資工版)
Introduction to C++ | Electrical Engineering and Computer Science | MIT OpenCourseWare
Introduction to C and C++ | Electrical Engineering and Computer Science | MIT OpenCourseWare
教學方式 (Teaching Method)
透過講義解說各主題的實作技巧,由學生練習、測試與完成相關之課堂實作或作業。 課堂會採用實體及線上混合教學,學生可在滿足防疫規定下參加現場授課,同時於線上平台直播,同學也可同步於線上平台留言發問。 學生課後可透過 eeclass 討論區及預約時段與授課老師及助教討論。
The course will introduce each theme with lecture notes. Students will practice each theme with Homeworks and hands-on labs in-class or off-class. The course will hold both physically and online. Students can attend class physically under Taiwan CDC COVID-19 guideline. Also, the course will stream on online streaming platform(s). Students can discuss in real-time on the online streaming platform(s). After class, students can use the discussion forum in the NTHU eeclass or make a reservation with instructor or TA.
教學進度 (Syllabus)
(9/11 updated) 週次皆為暫定安排,會依實際授課情形調整。
週次 (Week) | 主題 (Theme) | |
---|---|---|
Week 2 | 程式設計 & C/C++ 語言基本概念 | Basic Concept of Programming |
& C/C++ Language | ||
資料型態 & 格式化輸出與輸入(1) | Data Type & Formatted | |
Input/Output(1) | ||
Week 3 | 資料型態 & 格式化輸出與輸入(2) | Data Type & Formatted |
Input/Output(2) | ||
Week 4 | 資料型態 & 格式化輸出與輸入(3) | Data Type & Formatted |
Input/Output(3) | ||
運算子與運算式 | Operator & expression | |
Week 5 | 分支敘述 | Branch Statement |
迴圈敘述(1) | Loop Statement(1) | |
Week 6 | 迴圈敘述(2) | Loop Statement(2) |
Week 7 | 陣列與字串(1) | Array & String(1) |
Week 8 | 陣列與字串(2) | Array & String(2) |
Week 9 | 陣列與字串(3) | Array & String(3) |
位元處裡 | Bit manipulation | |
Week 10 | 期中評量 | Midterm exam |
Week 11 | 函式 & 參考(1) | Function & Reference(1) |
Week 12 | 函式 & 參考(2) | Function & Reference(2) |
Week 13 | 函式 & 參考(3) | Function & Reference(3) |
Week 14 | 結構(1) | Structure(1) |
Week 15 | 結構(2) | Structure(2) |
檔案 | File | |
Week 16 | 記憶體管理 | Memory management |
Week 17 | 前置處理器 | Preprocessor |
例外處裡 | Exception handling | |
除錯 & 斷言 | Debugging & assertion | |
Week 18 | 期末評量 | Final exam |
成績考核 (Evaluation)
暫定,會依實際授課情形調整。 (Tentative, will change during the course)
學期成績 (Grade) = 作業/實作 (Homework/Lab), 30% + 期中考 (Midterm exam), 30% + 期末考 (Final exam), 40 % + Bonus, 5%
(11/25 update) = 作業/實作 (Homework/Lab), 50% + 期中考 (Midterm exam), 25% + Extra Homework, 15% + 期末考 (Final exam), 25% + Bonus, 5%
相關網頁(Personal Website)
課程網頁 (Course website): https://stevenokm.github.io/i2p-nthu-math/i2p1
課程資訊
- 課程上課時間:每週四 15:30 - 18:10
- 前 1 - 1.5 小時講解今日主題
- 之後時間開放同學於現場或線上即時提問
- 課程上課地點:
- 綜三館 315 電腦教室 (座位47人,可出席人數 47 人,10/7更新)
- Youtube (Link: 上課錄影 - YouTube)
- Teams (團隊代碼: 0l2nz9s)
- 本課程開放加簽,原則上限制人數為 50 人,且參加實體課程需配合防疫規定。
- 請使用電子加簽系統加簽。
- 目前開放上限為本系大一生 47 人,並另有 10 人須審核加簽名額。
- 外系同學可線上加簽系統加選課程。
- 本課程有 Teams 團隊,需在申請完學校的 Office 365 帳號後即可使用連結加入,同學可多加利用團隊進行討論。
- 申請方式: Office 365 服務 (nthu.edu.tw)
- 外校同學可提供 office 365 帳號給助教協助加入團隊。
作業
- 每週作業會公布在課程網頁上,並使用 eeclass 平台繳交作業。
- eeclass 連結: https://eeclass.nthu.edu.tw/course/5088
- 作業會使用自動化工具檢查,若有批改上有問題可與助教詢問。
- 作業抄襲一律 0 分計算,並且不計入繳交次數。
- 作業須準時繳交,遲交則得到原始成績 80% 的分數。
期中 / 期末評量
- 期中 / 期末評量採斷網上機測試。
- 期中 / 期末評量會視疫情變化調整為其他方案。
- 同學皆可參加期中 / 期末評量。
老師資訊
洪奕文 E-mail: s106062802@m106.nthu.edu.tw
Office Hours: Every Mon. 14:00 - 15:00
Office Room: R2341, 2F, EECS Building
Note: 若要來研究室詢問問題,請 一定要預約時間 ,臨時來研究室詢問 不保證 能夠當下開放詢問。 若要約 teams 詢問也可以約 Office Hour 時段詢問。
TA infos
許廷碩 E-mail: tim890727@gmail.com
TA Hours: TBA
上課實況/錄影檔
Youtube (Link: 上課錄影 - YouTube)
Bonus
課程內容如果有錯誤,可以在每頁的右上方 "Suggest an edit" 使用 Github 進行修改,修改完畢後利用 Pull Request 的方式提交。
提交時註明學號以及姓名,並且每提交一次可以增加學期分數,配分依據修改幅度而定,每人總增加分數不超過 5 分。
Visual Studio Code Tutorial
設定 VSCode 環境 (Linux)
安裝 GCC
打開 終端機 輸入以下指令:
sudo apt update
sudo apt install -y build-essential g++ gdb
g++ -v
Wrong:
Correct:
安裝 VSCode & C++ plugin
Install VSCode: https://code.visualstudio.com/docs/setup/linux
Install the C++ extension for VSCode: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
新增專案 (Hello world!)
在 終端機 輸入以下指令:
$ mkdir projects
$ cd projects
$ code .
In VSCode: Exploer -> New File
輸入 test.cpp
在文字輸入區輸入以下程式碼
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main(void)
{
vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"};
for (const string& word : msg)
{
cout << word << " ";
}
cout << endl;
return 0;
}
編譯 & 執行 Hello world!
In VSCode: Terminal -> New Terminal
In VSCode Terminal:
$ g++ test.cpp
$ ./a.out
Hello C++ World from VS Code and the C++ extension!
$
Debugger
可以設置中斷點,方便程式除錯。
launch.json
In VSCode: Exploer -> New Folder
輸入 .vscode
In VSCode: Exploer -> New File
輸入 launch.json
在文字輸入區輸入以下設定檔
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/a.out",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
編譯 & 執行 Debugger
In VSCode Terminal:
$ g++ -g test.cpp
$
插入中斷點
在要中斷的程式碼行號左邊點一下
In VSCode: Run -> Start Debugging F5
NOTE: 安裝完後可以在 File -> Oper Recent
開啟 WSL 的工作區 (後綴有 WSL:Ubuntu
)
設定 VSCode 環境 (macOS)
安裝 g++
打開 終端機 輸入以下指令:
xcode-select --install
g++ -v
Wrong:
Correct:
安裝 VSCode & C++ plugin
Install VSCode: https://code.visualstudio.com/docs/setup/mac
Install the C++ extension for VSCode: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
Note: 要加入 code
到環境變數 PATH
內。
- 打開 Command Palette (⌘ Shift P) 後輸入
shell command
,就可以找到 Shell Command: Install 'code' command in PATH
- 重新啟動 終端機
新增專案 (Hello world!)
在 終端機 輸入以下指令:
$ mkdir projects
$ cd projects
$ code .
In VSCode: Exploer -> New File
輸入 test.cpp
在文字輸入區輸入以下程式碼
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main(void)
{
vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"};
for (const string& word : msg)
{
cout << word << " ";
}
cout << endl;
return 0;
}
settings.json
In VSCode: Exploer -> New Folder
輸入 .vscode
In VSCode: Exploer -> New File
輸入 settings.json
在文字輸入區輸入以下設定檔
{
"C_Cpp.default.cppStandard": "c++17"
}
編譯 & 執行 Hello world!
In VSCode: Terminal -> New Terminal
In VSCode Terminal:
$ g++ -std=c++17 -stdlib=libc++ test.cpp
$ ./a.out
Hello C++ World from VS Code and the C++ extension!
$
Debugger
可以設置中斷點,方便程式除錯。
安裝 CodeLLDB plugin
Install the CodeLLDB extension for VSCode: https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb
launch.json
In VSCode:
選擇 .vscode
In VSCode: Exploer -> New File
輸入 launch.json
在文字輸入區輸入以下設定檔
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "lldb",
"request": "launch",
"program": "${workspaceFolder}/a.out",
"args": ["-arg1", "-arg2"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "lldb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
編譯 & 執行 Debugger
In VSCode Terminal:
$ g++ -std=c++17 -stdlib=libc++ -g test.cpp
$
插入中斷點
在要中斷的程式碼行號左邊點一下
In VSCode: Run -> Start Debugging F5
使用 VSCode server
安裝 GCC & C++ plugin
In VSCode: Terminal -> New Terminal
sudo apt update
sudo apt install -y build-essential g++ gdb
g++ -v
curl -L https://github.com/microsoft/vscode-cpptools/releases/download/1.5.1/cpptools-linux.vsix --output cpptools-linux.vsix
code-server --install-extension cpptools-linux.vsix
NOTE: 須重新整理網頁才能生效
新增專案 (Hello world!)
In VSCode: Exploer -> New Folder
輸入 projects
In VSCode: File -> Open Folder
In VSCode: Exploer -> New File
輸入 test.cpp
在文字輸入區輸入以下程式碼
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"};
for (const string& word : msg)
{
cout << word << " ";
}
cout << endl;
}
編譯 & 執行 Hello world!
In VSCode Terminal:
$ g++ test.cpp
$ ./a.out
Hello C++ World from VS Code and the C++ extension!
$
Debugger
可以設置中斷點,方便程式除錯。
launch.json
In VSCode: Exploer -> New Folder
輸入 .vscode
In VSCode: Exploer -> New File
輸入 launch.json
在文字輸入區輸入以下設定檔
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/a.out",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
編譯 & 執行 Debugger
In VSCode Terminal:
$ g++ -g test.cpp
$
插入中斷點
在要中斷的程式碼行號左邊點一下
In VSCode: Run -> Start Debugging F5
Reference:
- Get Started with C++ on Linux in Visual Studio Code
- Configure VS Code for Clang/LLVM on macOS
- Get Started with C++ and Windows Subsystem for Linux in Visual Studio Code
Visual Studio Code Tutorial (WSL/windows + mingw)
設定 VSCode 環境 (WSL)
NOTE: 安裝教學影片 WSL Tutorial
安裝 WSL & GCC
Install WSL: https://docs.microsoft.com/en-us/windows/wsl/install
啟動 WSL Ubuntu
在 WSL Ubuntu 輸入以下指令:
sudo apt update
sudo apt install -y build-essential g++ gdb
g++ -v
Wrong:
Correct:
安裝 VSCode & Remote - WSL plugin
Install VSCode: https://code.visualstudio.com/docs/setup/windows
Install the Remote - WSL extension for VSCode: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl
新增專案 (Hello world!)
重新打開 WSL Ubuntu 並輸入以下指令:
$ mkdir projects
$ cd projects
$ code .
Correct:
安裝 WSL C++ plugin
Install the C++ extension for VSCode: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
Correct:
In VSCode: Exploer -> New File
輸入 test.cpp
在文字輸入區輸入以下程式碼
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"};
for (const string& word : msg)
{
cout << word << " ";
}
cout << endl;
}
編譯 & 執行 Hello world!
In VSCode: Terminal -> New Terminal
In VSCode Terminal:
$ g++ test.cpp
$ ./a.out
Hello C++ World from VS Code and the C++ extension!
$
Debugger
可以設置中斷點,方便程式除錯。
launch.json
In VSCode: Exploer -> New Folder
輸入 .vscode
In VSCode: Exploer -> New File
輸入 launch.json
在文字輸入區輸入以下設定檔
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/a.out",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
編譯 & 執行 Debugger
In VSCode Terminal:
$ g++ -g test.cpp
$
插入中斷點
在要中斷的程式碼行號左邊點一下
In VSCode: Run -> Start Debugging F5
設定 VSCode 環境 (Windows + mingw)
Credit: CNOCycle/cpp_tutorial by E. Chen
安裝步驟
- 下載vscode-all-in-one.zip
- 解壓縮縮後,將檔案放到路徑
%USERPROFILE%
- 將
%USERPROFILE%\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin
加入到環境變數Path
- 完成
圖解說明
%USERPROFILE%
是windows的環境變數,指的是該電腦使用者的目錄。 概念同linux底下的${HOME}
。 以本台測試電腦為例,該電腦使用者為WDAGUtilityAccount
,則%USERPROFILE%
=C:\Users\WDAGUtilityAccount
。 正常請況下在檔案總管上的路徑列輸入%USERPROFILE%
後會自動跳轉。如下圖示範:
- 將檔案放到路徑
%USERPROFILE%
,如下圖示範:
- 將
%USERPROFILE%\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin
加入到環境變數Path
,如下圖示範:
文字版流程:
- 在「搜尋」中,搜尋並選取:系統 (控制台)
- 按一下進階系統設定連結。
- 按一下環境變數。在系統變數區段中,找到 PATH 環境變數並加以選取。按一下編輯。如果 PATH 環境變數不存在,請按一下新增。
- 在編輯系統變數 (或新增系統變數) 視窗中,指定 PATH 環境變數的值。按一下確定。按一下確定,將其餘的視窗全都關閉。
NOTE: %USERPROFILE%\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin
的順序是有差的,請確認是在最下方才是正確的。
若不是可以使用右側的 上移
、下移
調整順序。
測試編譯以及除錯
測試編譯
- 用
vscode
編輯器選擇開啟解壓縮檔案裡面的test
資料夾,如下圖示範:
NOTE: 如果電腦上沒有 test
資料夾,需要在 %USERPROFILE%
中新增一個資料夾,名稱可以自由取。
- 信任開啟檔案,選擇
Yes, I trust
,如下圖示範:
- 點擊左方test.cpp檔案後,在第5行左側按下去,會出現紅點,如下圖示範:
- 按下
ctrl+shift+B
按鍵,或上方terminal -> Run Build Task
,如下圖示範:
- 此時上方會出現以下畫面,選擇中間選項使用 g++ 編譯檔案,如下圖示範:
- 設置正確的話,此時下方會出現編譯成功完成的提示,如下圖示範:
測試除錯
-
完成前項
測試編譯
的所有流程 -
按下左方三角形按鈕後選擇
Run and Debug
,如下圖示範:
- 此時上方會依序出現相關提示,選擇預設的即可,如下圖示範:
- 若設定正確的話,會看到程式停留在選取的第5行,如下圖示範:
Reference:
- CNOCycle/cpp_tutorial by E. Chen
- Get Started with C++ and Windows Subsystem for Linux in Visual Studio Code
- Get Started with C++ and Mingw-w64 in Visual Studio Code
Visual Studio Code Tutorial (315 classroom)
使用 VSCode 環境
點擊兩下,打開桌面上的 Visual Studio Code
。
在文字輸入區輸入以下程式碼
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"};
for (const string& word : msg)
{
cout << word << " ";
}
cout << endl;
}
編譯 & 執行 Hello world!
In VSCode: Terminal -> New Terminal
In VSCode Terminal:
$ g++ test.cpp
$ ./a.out
Hello C++ World from VS Code and the C++ extension!
$
測試編譯以及除錯
測試編譯
- 點擊左方test.cpp檔案後,在要中斷的程式碼行號左邊點一下,會出現紅點,如下圖示範:
- 按下
ctrl+shift+B
按鍵,或上方terminal -> Run Build Task
,如下圖示範:
- 此時上方會出現以下畫面,選擇中間選項使用 g++ 編譯檔案,如下圖示範:
- 設置正確的話,此時下方會出現編譯成功完成的提示,如下圖示範:
測試除錯
-
完成前項
測試編譯
的所有流程 -
按下左方三角形按鈕後選擇
Run and Debug
,如下圖示範:
- 此時上方會依序出現相關提示,選擇預設的即可,如下圖示範:
- 若設定正確的話,會看到程式停留在選取的那一行,如下圖示範:
Lecture 1-1: Introduction to Programming & C++
從 Source Code 到執行檔
Credit: Computer Architecture by 黃婷婷老師
Text Editor
程式碼其實就是一般的純文字檔,為了能夠讓程式設計師有更好的效率撰寫程式,因此寫程式的編輯器會增加其他的功能輔助,例如自動縮排、關鍵字變色、甚至檢查語法錯誤等。
常見的程式編輯器有 Notepad++ 、 Atom 、 VS Code 等。
Compiler
是程式語言的核心工具,用來將你寫的 C/C++ 程式碼變成讓電腦能夠執行的指令。 此外還負責指令的最佳化,讓執行效率提升。
Linker
是用來將編譯好的指令連結成電腦能夠執行的執行檔。因為除了我們自己寫的程式碼之外,無可避免一定會用到許多已經編譯好的指令—— Library (函式庫)。
在現實的情況下,不可能每一東西都要靠自己創造,所以絕大多數的時候我們都會使用內建的或別人寫好的函式庫,來達到自己想要的功能。
Linker 就是用來把我們的程式碼和需要的函式庫裡的程式碼連結在一起,形成單一的可執行檔。
在 Windows 裡,執行的的副檔名為 .exe
,macOS/Linux 則是給予執行權限即可執行。
Loader
是用來將執行檔載入給電腦,並實際讓電腦執行你的執行檔。
可以想像是在桌面上點兩下 .exe
檔案, Loader 概念與這個類似。
程式執行
所有的程式都是從指令列執行。 現在常見的圖形介面程式都是從指令列程式擴充而來的。
例如在桌面上點兩下 a.exe
檔案其實是告訴系統說 “請執行 a.exe
”,系統會透過 Loader 將 a.exe
載入並且執行。
這時僅有指令列,如果要顯示圖形化介面, a.exe
需要加入其他函式庫告訴系統 “a.exe
有圖形化介面,內容分別是...”。
$ firefox
.......
.......
^C
$
C++ 語言架構
// import std::cout & std::endl
#include <iostream>
/*
start of define main function
void: no input arguments
int: return value in int type
*/
int main(void){
// print "Hello world!" on the terminal
std::cout << "Hello world!" << std::endl;
// return execute status back to the system
// `0` means the program executed successfully
return 0;
// end of define main function
}
$ ./a.out
Hello world!
$
#include <iostream>
int main(void){}
& return 0;
std::cout
& <<
std::endl
return 0;
Reference:
Lecture 1-2: Data Type & Formatted Input/Output (1)
變數 & 格式化輸出
// import std::cout & std::endl
#include <iostream>
// start of define main function
// void: no input arguments
// int: return value in int type
int main(void){
// define variable `year`, `month`, and `day`
// and assign value with `2021`, `9`, and `23`
int year = 2021, month = 9, day = 23;
// print "Hello world!" and today's date on the terminal
std::cout << "Hello world!"
<< std::endl
<< "Today is "
<< year << "/"
<< month << "/"
<< day << "."
<< std::endl;
return 0;
}
$ ./a.out
Hello world!
Today is 2021/9/23.
$
int year = 2021, month = 9, day = 23;
變數命名規則
std::cout << year;
從鍵盤讀取: std::cin
#include <iostream>
int main(void){
int input_number = 0;
std::cin >> input_number;
std::cout << "Hello world! "
<< input_number
<< std::endl;
return 0;
}
std::cin >> input_number;
數字運算
#include <iostream>
int main(void){
int a = 0, b = 0, c = 0;
std::cout << "input a:";
std::cin >> a;
std::cout << "input b:";
std::cin >> b;
c = a + b;
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a + b = " << c << std::endl;
return 0;
}
c = a + b;
Reference:
Lab 1-1: Hello World (1)
Helloworld 的範例程式碼
#include <iostream>
int main(void){
std::cout << "Hello world!" << std::endl;
return 0;
}
互動式顯示 Hello world! <your student id>
(50%)
- 輸入:學生學號
- 輸出:Hello world 文字及學生學號
- 檔名:lab1_1_<學號>.cpp (e.g. lab1_1_106062802.cpp)
程式需提示使用者輸入學號,程式需輸出 Hello world!
以及使用者輸入的學號。
Format
Your student ID: <your student id>⏎
Hello world! student <your student id>
Example
$ ./a.out
Your student ID: 106062802⏎
Hello world! student 106062802
Lab 1-2: Hello World (2)
每週工時計算 (50%)
- 輸入:一位員工的每週工作日數、該員工該週每天的工作時數
- 輸出:該員工每週的工時
- 檔名:lab1_2_<學號>.cpp (e.g. lab1_2_106062802.cpp)
程式需提示使用者輸入一位員工的每週工作日數、該員工該週每天的工作時數 (假設每天皆相同),程式需輸出該員工每週的工時。
Format
Employee workdays per week: <workdays/week>⏎
Employee working hours per workday: <working hours/workday>⏎
Employee works <working hours/week> hour(s) per week.
Example
$ ./a.out
Employee workdays per week: 6⏎
Employee working hours per workday: 12⏎
Employee works 72 hour(s) per week.
Lecture 2: Data Type & Formatted Input/Output (2)
細部拆解一個 C++ 程式碼
Debugger 逐行解釋
#include <iostream>
int main(void)
{
int a = 0, b = 0, c = 0;
std::cout << "input a:";
std::cin >> a;
std::cout << "input b:";
std::cin >> b;
c = a + b;
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a + b = " << c << std::endl;
return 0;
}
為何要寫 #include <>
?
Include 了許多已經先寫好的 function,有些是跟系統有關,我們看不到原始碼的,我們只要拿來用就好。
可以打開 /usr/include/c++/9/iostream
看原始碼。
為何 int main(void)
?
main
的定義
int main () { body }
int main (int argc, char *argv[]) { body }
The main function is called at program startup after initialization of the non-local objects with static storage duration. It is the designated entry point to a program that is executed in hosted environment (that is, with an operating system). The entry points to freestanding programs (boot loaders, OS kernels, etc) are implementation-defined.
Another example:
#include <iostream>
#include <sstream>
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0;
std::stringstream char_array_to_int;
std::cout << argc << std::endl;
char_array_to_int << argv[1] << ' ' << argv[2];
char_array_to_int >> a;
char_array_to_int >> b;
c = a + b;
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a + b = " << c << std::endl;
return 0;
}
Coding Style & 變數名稱規則
好的 coding style 很重要
Bad example:
#include <iostream>
#include <sstream>
int main(int argc, char *argv[]){int a = 0, b = 0, c = 0;std::stringstream char_array_to_int;std::cout << argc << std::endl;char_array_to_int << argv[1] << ' ' << argv[2];char_array_to_int >> a;char_array_to_int >> b;c = a + b;std::cout << "a: " << a << std::endl << "b: " << b << std::endl << "a + b = " << c << std::endl;return 0;}
intend & spacing
Bad example:
#include <iostream>
#include <sstream>
int main(int argc, char *argv[]){int a = 0, b = 0, c = 0;
std::stringstream char_array_to_int;
std::cout<<argc<<std::endl;
char_array_to_int<<argv[1]<<' '<<argv[2];
char_array_to_int>>a;
char_array_to_int>>b;
c=a+b;
std::cout<<"a: "<<a<<std::endl
<< "b: " << b << std::endl
<< "a + b = "<< c << std::endl;
return 0;
}
fomatter
In vscode:
- Windows: Shift Alt F
- macOS: Shift Option F
- Linux: Ctrl Shift I
Demo
變數名稱規則
GNU
#include <iostream>
#include <sstream>
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0;
std::stringstream char_array_to_int;
std::cout << argc << std::endl;
char_array_to_int << argv[1] << ' ' << argv[2];
char_array_to_int >> a;
char_array_to_int >> b;
c = a + b;
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a + b = " << c << std::endl;
return 0;
}
Hungarian notation
#include <iostream>
#include <sstream>
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0;
std::stringstream charArrayToInt;
std::cout << argc << std::endl;
charArrayToInt << argv[1] << ' ' << argv[2];
charArrayToInt >> a;
charArrayToInt >> b;
c = a + b;
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a + b = " << c << std::endl;
return 0;
}
變數格式 & 型態轉換
int, long, long long
Type | Size in bits | Format | Value range |
---|---|---|---|
int | 16 | signed | \(-32768\) to \(32767\) |
unsigned | \(0\) to \(65535\) | ||
long | 32 | signed | \(-2,147,483,648\) to \(2,147,483,647\) |
unsigned | \(0\) to \(4,294,967,295\) | ||
long long | 64 | signed | \(-9,223,372,036,854,775,808\) to \(9,223,372,036,854,775,807\) |
unsigned | \(0\) to \(18,446,744,073,709,551,615\) |
unsigned int & overflow
#include <iostream>
int main(void)
{
int i = 2147483647;
unsigned int j = 4294967295U;
std::cout << i << ' ' << i + 1 << ' ' << i + 2 << std::endl;
std::cout << j << ' ' << j + 1 << ' ' << j + 2 << std::endl;
return 0;
}
float, double, double double
Type | Size in bits | Format | Value range |
---|---|---|---|
float | 32 | IEEE-754 | min subnormal: \( \pm 1.401,298,4 \times 10^{-45} \) |
min normal: \( \pm 1.175,494,3 \times 10^{-38} \) | |||
max: \( \pm 3.402,823,4 \times 10^{38} \) | |||
double | 64 | IEEE-754 | min subnormal: \( \pm 4.940,656,458,412 \times 10^{-324} \) |
min normal: \( \pm 2.225,073,858,507,201,4 \times 10^{-308} \) | |||
max: \( \pm 1.797,693,134,862,315,7 \times 10^{308} \) | |||
long doulbe | 80 | x86 | min subnormal: \( \pm 3.645,199,531,882, \) |
\( 474,602,528 \times 10^{-4951} \) | |||
min normal: \( \pm 3.362,103,143,112, \) | |||
\( 093,506,263 \times 10^{-4932} \) | |||
max: \( \pm 1.189,731,495,357, \) | |||
\( 231,765,021 \times 10^{4932} \) | |||
__float128 | 128 | IEEE-754 | min subnormal: \( \pm 6.475,175,119,438,025, \) |
\( 110,924,438,958,227,646,552,5 \times 10^{-4966} \) | |||
min normal: \( \pm 3.362,103,143,112,093, \) | |||
\( 506,262,677,817,321,752,602,6 \times 10^{-4932} \) | |||
max: \( \pm 1.189,731,495,357,231, \) | |||
\( 765,085,759,326,628,007,016,2 \times 10^{4932} \) |
floating point precision
#include <iostream>
#include <iomanip>
int main(void)
{
float x = 1.23456789;
std::cout << std::setprecision(3) << x << std::endl;
std::cout << std::setprecision(4) << x << std::endl;
std::cout << std::fixed;
std::cout << std::setprecision(5) << x << std::endl;
std::cout << std::setprecision(9) << x << std::endl;
return 0;
}
cast
#include <iostream>
int main(void)
{
double f = 3.14;
unsigned int n1 = (unsigned int)f; // C-style cast
std::cout << f << std::endl;
std::cout << n1 << std::endl;
return 0;
}
char
Type | Size in bits | Format | Value range |
---|---|---|---|
character | 8 | signed | \( -128 \) to \( 127 \) |
unsigned | \( 0 \) to \( 255 \) | ||
16 | UTF-16 | \( 0 \) to \( 65535 \) | |
32 | UTF-32 | \( 0 \) to \( 1,114,111 \) (0x10ffff ) |
ASCII
Hex = Hexdecimal Value, Char = Character
Hex | Char | Hex | Char | Hex | Char | Hex | Char |
---|---|---|---|---|---|---|---|
0 | NUL (null) | 20 | SPACE | 40 | @ | 60 | ` |
1 | SOH (start of heading) | 21 | ! | 41 | A | 61 | a |
2 | STX (start of text) | 22 | " \" | 42 | B | 62 | b |
3 | ETX (end of text) | 23 | # | 43 | C | 63 | c |
4 | EOT (end of transmission) | 24 | $ | 44 | D | 64 | d |
5 | ENQ (enquiry) | 25 | % | 45 | E | 65 | e |
6 | ACK (acknowledge) | 26 | & | 46 | F | 66 | f |
7 | BEL (bell \a ) | 27 | ' \' | 47 | G | 67 | g |
8 | BS (backspace \b ) | 28 | ( | 48 | H | 68 | h |
9 | TAB (horizontal tab \t ) | 29 | ) | 49 | I | 69 | i |
A | LF (NL line feed, new line \n ) | 2A | * | 4A | J | 6A | j |
B | VT (vertical tab \v ) | 2B | + | 4B | K | 6B | k |
C | FF (NP form feed, new page \f ) | 2C | , | 4C | L | 6C | l |
D | CR (carriage return \r ) | 2D | - | 4D | M | 6D | m |
E | SO (shift out) | 2E | . | 4E | N | 6E | n |
F | SI (shift in) | 2F | / | 4F | O | 6F | o |
10 | DLE (data link escape) | 30 | 0 | 50 | P | 70 | p |
11 | DC1 (device control 1) | 31 | 1 | 51 | Q | 71 | q |
12 | DC2 (device control 2) | 32 | 2 | 52 | R | 72 | r |
13 | DC3 (device control 3) | 33 | 3 | 53 | S | 73 | s |
14 | DC4 (device control 4) | 34 | 4 | 54 | T | 74 | t |
15 | NAK (negative acknowledge) | 35 | 5 | 55 | U | 75 | u |
16 | SYN (synchronous idle) | 36 | 6 | 56 | V | 76 | v |
17 | ETB (end of trans. block) | 37 | 7 | 57 | W | 77 | w |
18 | CAN (cancel) | 38 | 8 | 58 | X | 78 | x |
19 | EM (end of medium) | 39 | 9 | 59 | Y | 79 | y |
1A | SUB (substitute) | 3A | : | 5A | Z | 7A | z |
1B | ESC (escape \e ) | 3B | ; | 5B | [ | 7B | { |
1C | FS (file separator) | 3C | < | 5C | \ \\ | 7C | | |
1D | GS (group separator) | 3D | = | 5D | ] | 7D | } |
1E | RS (record separator) | 3E | > | 5E | ^ | 7E | ~ |
1F | US (unit separator) | 3F | ? \? | 5F | _ | 7F | DEL |
#include <iostream>
int main(void)
{
char char_five = '5'; // char_five = 53
char char_diff = '5' - '0'; // char_diff = 53-48 = 5
char c = 'B' + 32; // c = 98 = 'b'
std::cout << char_five << std::endl;
std::cout << (int)char_five << std::endl;
std::cout << (int)char_diff << std::endl;
std::cout << c << std::endl;
return 0;
}
others
UTF-8, UTF-16, Big5, Shift_JIS, etc.
Reference:
- htchen/i2p-nthu by 陳煥宗老師
- Main function - cppreference.com
- 【VSCode】如何在 VSCode 上自定義 C++ 的 coding style | by Zam Huang | Medium
- Naming convention (programming) - Wikipedia
- GNU Coding Standards
- Fundamental types - cppreference.com
- setprecision - C++ Reference
- Explicit type conversion - cppreference.com
- ASCII Table
- What is a buffer in C?
Lab 2: 正實數加/乘法器
Lab 2-1: 正實數顯示 (30%)
- 輸入:正實數的整數部分、正實數的小數部分(輸入數字乘以\( \frac{1}{1,000,000} \)),正實數的小數部分不超過 6 位數。
- 輸出:該正實數,並且以
float
形式輸出。 - 檔名:lab2_1_<學號>.cpp (e.g. lab2_1_106062802.cpp)
程式需提示使用者輸入正實數的整數部分、正實數的小數部分,程式需輸出該正實數。
Format
Input real number, before decimal point: <before decimal point>⏎
Input real number, after decimal point: <after decimal point>⏎
The real number is: <output real number>
Example
$ ./a.out
Input real number, before decimal point: 123⏎
Input real number, after decimal point: 456000⏎
The real number is: 123.456
$ ./a.out
Input real number, before decimal point: 654321⏎
Input real number, after decimal point: 0⏎
The real number is: 654321
$ ./a.out
Input real number, before decimal point: 123⏎
Input real number, after decimal point: 456⏎
The real number is: 123
Reference Code:
Credit: 金昆樂 (110021111)
#include <iostream>
using namespace std;
int main()
{
float a, b;
cout << "Input real number, before decimal point: ";
cin >> a;
cout << "Input real number, after decimal point: ";
cin >> b;
cout << "The real number is: " << a + b / 1000000 << endl;
return 0;
}
Lab 2-2: 正實數加法 (30%)
- 輸入:兩正實數的整數部分、正實數的小數部分(輸入數字乘以\( \frac{1}{1,000,000} \)),正實數的小數部分不超過 6 位數。
- 輸出:兩正實數數值、兩正實數的和,並且以
double
形式搭配std::fixed
輸出。 - 檔名:lab2_2_<學號>.cpp (e.g. lab2_2_106062802.cpp)
程式需提示使用者輸入兩正實數的整數部分、正實數的小數部分,程式需輸出兩正實數數值、兩正實數的和。
Format
Input real number (a), before decimal point: <(a) before decimal point>⏎
Input real number (a), after decimal point: <(a) after decimal point>⏎
The real number (a) is: <real number (a)>
Input real number (b), before decimal point: <(b) before decimal point>⏎
Input real number (b), after decimal point: <(b) after decimal point>⏎
The real number (b) is: <real number (b)>
(a) + (b) = <real number (a) + (b)>
Example
$ ./a.out
Input real number (a), before decimal point: 1⏎
Input real number (a), after decimal point: 234560⏎
The real number (a) is: 1.234560
Input real number (b), before decimal point: 12345⏎
Input real number (b), after decimal point: 6⏎
The real number (b) is: 12345.000006
(a) + (b) = 12346.234566
Reference Code:
Credit: 金昆樂 (110021111)
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double a1, a2, b1, b2;
cout << "Input real number (a), before decimal point: ";
cin >> a1;
cout << "Input real number (a), after decimal point: ";
cin >> a2;
cout << "The real number (a) is: " << fixed << a1 + a2 / 1000000 << endl;
cout << "Input real number (b), before decimal point: ";
cin >> b1;
cout << "Input real number (b), after decimal point: ";
cin >> b2;
cout << "The real number (b) is: " << fixed << b1 + b2 / 1000000 << endl;
cout << "(a) + (b) = " << fixed << a1 + b1 + (a2 + b2) / 1000000 << endl;
return 0;
}
Lab 2-3: 正實數乘法 (20%)
- 輸入:兩正實數的整數部分、正實數的小數部分(輸入數字乘以\( \frac{1}{10,000,000,000} \)),正實數的小數部分不超過 10 位數。
- 輸出:兩正實數數值、兩正實數的積,並且以
double
形式搭配std::setprecision(15)
輸出。 - 檔名:lab2_3_<學號>.cpp (e.g. lab2_3_106062802.cpp)
程式需提示使用者輸入兩正實數的整數部分、正實數的小數部分,程式需輸出兩正實數數值、兩正實數的積。
Format
Input real number (a), before decimal point: <(a) before decimal point>⏎
Input real number (a), after decimal point: <(a) after decimal point>⏎
The real number (a) is: <real number (a)>
Input real number (b), before decimal point: <(b) before decimal point>⏎
Input real number (b), after decimal point: <(b) after decimal point>⏎
The real number (b) is: <real number (b)>
(a) * (b) = <real number (a) * (b)>
Example
$ ./a.out
Input real number (a), before decimal point: 1⏎
Input real number (a), after decimal point: 2000000000⏎
The real number (a) is: 1.2
Input real number (b), before decimal point: 12⏎
Input real number (b), after decimal point: 3000000000⏎
The real number (b) is: 12.3
(a) * (b) = 14.76
$ ./a.out
Input real number (a), before decimal point: 1⏎
Input real number (a), after decimal point: 2000000000⏎
The real number (a) is: 1.2
Input real number (b), before decimal point: 12⏎
Input real number (b), after decimal point: 3⏎
The real number (b) is: 12.0000000003
(a) * (b) = 14.40000000036
Reference Code:
Credit: 金昆樂 (110021111)
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double a1, a2, b1, b2;
cout << "Input real number (a), before decimal point: ";
cin >> a1;
cout << "Input real number (a), after decimal point: ";
cin >> a2;
cout << "The real number (a) is: " << setprecision(15)
<< a1 + a2 / 10000000000 << endl;
cout << "Input real number (b), before decimal point: ";
cin >> b1;
cout << "Input real number (b), after decimal point: ";
cin >> b2;
cout << "The real number (b) is: " << setprecision(15)
<< b1 + b2 / 10000000000 << endl;
long double c = (a1 + a2 / 10000000000) * (b1 + b2 / 10000000000);
cout << "(a) * (b) = " << setprecision(15) << c << endl;
return 0;
}
Lab 2-4: 大正實數加法 (10%)
- 輸入:兩正實數的整數部分、正實數的小數部分(輸入數字乘以\( \frac{1}{1,000,000,000,000,000,000} \)),整數及小數各不超過 18 位數字
- 輸出:兩正實數數值、兩正實數的和,整數不超過 19 位數字,小數部分固定顯示 18 位。
- 檔名:lab2_4_<學號>.cpp (e.g. lab2_4_106062802.cpp)
程式需提示使用者輸入兩正實數的整數部分、正實數的小數部分,程式需輸出兩正實數數值、兩正實數的和。
Format
Input real number (a), before decimal point: <(a) before decimal point>⏎
Input real number (a), after decimal point: <(a) after decimal point>⏎
The real number (a) is: <real number (a)>
Input real number (b), before decimal point: <(b) before decimal point>⏎
Input real number (b), after decimal point: <(b) after decimal point>⏎
The real number (b) is: <real number (b)>
(a) + (b) = <real number (a) + (b)>
Example
$ ./a.out
Input real number (a), before decimal point: 123456789012345678⏎
Input real number (a), after decimal point: 123456789012345678⏎
The real number (a) is: 123456789012345678.123456789012345678
Input real number (b), before decimal point: 987654321098765432⏎
Input real number (b), after decimal point: 987654321098765432⏎
The real number (b) is: 987654321098765432.987654321098765432
(a) + (b) = 1111111110111111111.111111110111111110
$ ./a.out
Input real number (a), before decimal point: 1⏎
Input real number (a), after decimal point: 1⏎
The real number (a) is: 1.000000000000000001
Input real number (b), before decimal point: 2⏎
Input real number (b), after decimal point: 2⏎
The real number (b) is: 2.000000000000000002
(a) + (b) = 3.000000000000000003
Reference:
Reference Code:
Credit: 金昆樂 (110021111)
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{
long long int a1 = 0, a2 = 0, b1 = 0, b2 = 0;
cout << "Input real number (a), before decimal point: ";
cin >> a1;
cout << "Input real number (a), after decimal point: ";
cin >> a2;
cout << "The real number (a) is: " << a1 << "."
<< setfill('0') << setw(18) << a2 << endl;
cout << "Input real number (b), before decimal point: ";
cin >> b1;
cout << "Input real number (b), after decimal point: ";
cin >> b2;
cout << "The real number (b) is: " << b1 << "."
<< setfill('0') << setw(18) << b2 << endl;
long long int c2 = a2 + b2, c1 = a1 + b1;
long long int N = 1000000000000000000;
if (c2 >= N)
{
c2 = c2 - N;
c1++;
}
cout << "(a) + (b) = " << c1 << '.'
<< setfill('0') << setw(18) << c2 << endl;
return 0;
}
Lab 2-5: 大正實數乘法 (10%)
- 輸入:兩正實數的整數部分、正實數的小數部分(輸入數字乘以\( \frac{1}{1,000,000,000} \)),整數及小數各不超過 9 位數字
- 輸出:兩正實數數值、兩正實數的積,整數及小數各不超過 18 位數字,小數部分固定顯示 18 位。
- 檔名:lab2_5_<學號>.cpp (e.g. lab2_5_106062802.cpp)
程式需提示使用者輸入兩正實數的整數部分、正實數的小數部分,程式需輸出兩正實數數值、兩正實數的積。
Format
Input real number (a), before decimal point: <(a) before decimal point>⏎
Input real number (a), after decimal point: <(a) after decimal point>⏎
The real number (a) is: <real number (a)>
Input real number (b), before decimal point: <(b) before decimal point>⏎
Input real number (b), after decimal point: <(b) after decimal point>⏎
The real number (b) is: <real number (b)>
(a) * (b) = <real number (a) * (b)>
Example
$ ./a.out
Input real number (a), before decimal point: 123456789⏎
Input real number (a), after decimal point: 123456789⏎
The real number (a) is: 123456789.123456789000000000
Input real number (b), before decimal point: 987654321⏎
Input real number (b), after decimal point: 987654321⏎
The real number (b) is: 987654321.987654321000000000
(a) * (b) = 121932631356500531.347203169112635269
$ ./a.out
Input real number (a), before decimal point: 1⏎
Input real number (a), after decimal point: 1⏎
The real number (a) is: 1.000000001000000000
Input real number (b), before decimal point: 2⏎
Input real number (b), after decimal point: 2⏎
The real number (b) is: 2.000000002000000000
(a) * (b) = 2.000000004000000002
Reference:
Reference Code:
Credit: 金昆樂 (110021111)
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
long long int a1 = 0, a2 = 0, b1 = 0, b2 = 0;
cout << "Input real number (a), before decimal point: ";
cin >> a1;
cout << "Input real number (a), after decimal point: ";
cin >> a2;
cout << "The real number (a) is: " << a1 << "."
<< setfill('0') << setw(9) << a2 << "000000000" << endl;
cout << "Input real number (b), before decimal point: ";
cin >> b1;
cout << "Input real number (b), after decimal point: ";
cin >> b2;
cout << "The real number (b) is: " << b1 << "."
<< setfill('0') << setw(9) << b2 << "000000000" << endl;
long long int N = 1000000000;
long long int c1 = a1 * b1 + a1 * b2 / N + a2 * b1 / N;
long long int c2 = a2 * b2 + ((a1 * b2) % N) * N + ((a2 * b1) % N) * N;
c1 += c2 / (N * N);
c2 = c2 % (N * N);
cout << "(a) * (b) = " << c1 << '.'
<< setfill('0') << setw(18) << c2 << endl;
return 0;
}
Lecture 3-1: Data Type & Formatted Input/Output (3)
Radix
- Binary
0b0
,0b1
- Octal:
00
~07
- Decimal:
0
~9
- Hexdecimal:
0x0
~0xf
example:
int d = 42;
int o = 052;
int x = 0x2a;
int X = 0X2A;
int b = 0b101010; // C++14
setfill & setw
setfill
#include <iostream>
#include <iomanip> // include IO Manipulators
int main()
{
std::cout << "default fill: [" << std::setw(10) << 42 << "]\n"
<< "setfill('*'): [" << std::setfill('*')
<< std::setw(10) << 42 << "]\n";
}
output
$ ./a.out
default fill: [ 42]
setfill('*'): [********42]
$
setw
#include <iostream>
#include <iomanip>
int main()
{
std::cout << "no setw: [" << 42 << "]\n"
<< "setw(6): [" << std::setw(6) << 42 << "]\n"
<< "setw(6), several elements: [" << 89
<< std::setw(6) << 12 << 34 << "]\n";
}
$ ./a.out
no setw: [42]
setw(6): [ 42]
setw(6), several elements: [89 1234]
$
scientific representation
format: 10-based
1.234567e-10
-1.234567e+10
std::scientific, std::fixed
#include <iostream>
#include <iomanip>
enum class cap
{
title,
middle,
end
};
void print(const char *text, double num, cap c)
{
if (c == cap::title)
std::cout << "┌──────────┬────────────┬──────────────────────────┐\n"
"│ number │ iomanip │ representation │\n"
"├──────────┼────────────┼──────────────────────────┤\n";
std::cout << std::left
<< "│ " << std::setw(8) << text << " │ fixed │ "
<< std::setw(24) << std::fixed << num << " │\n"
<< "│ " << std::setw(8) << text << " │ scientific │ "
<< std::setw(24) << std::scientific << num << " │\n"
<< "│ " << std::setw(8) << text << " │ default │ "
<< std::setw(24) << std::defaultfloat << num << " │\n";
std::cout << (c != cap::end ?
"├──────────┼────────────┼──────────────────────────┤\n"
: "└──────────┴────────────┴──────────────────────────┘\n");
}
int main()
{
print("0.0", 0.0, cap::title);
print("0.01", 0.01, cap::middle);
print("0.00001", 0.00001, cap::end);
}
$ ./a.out
┌──────────┬────────────┬──────────────────────────┐
│ number │ iomanip │ representation │
├──────────┼────────────┼──────────────────────────┤
│ 0.0 │ fixed │ 0.000000 │
│ 0.0 │ scientific │ 0.000000e+00 │
│ 0.0 │ default │ 0 │
├──────────┼────────────┼──────────────────────────┤
│ 0.01 │ fixed │ 0.010000 │
│ 0.01 │ scientific │ 1.000000e-02 │
│ 0.01 │ default │ 0.01 │
├──────────┼────────────┼──────────────────────────┤
│ 0.00001 │ fixed │ 0.000010 │
│ 0.00001 │ scientific │ 1.000000e-05 │
│ 0.00001 │ default │ 1e-05 │
└──────────┴────────────┴──────────────────────────┘
$
pow(base, exponent)
計算 $${base}^{exponment}$$
#include <iostream>
#include <cmath> // include cmath for std::pow
int main()
{
// typical usage
std::cout << "pow(2, 10) = " << std::pow(2, 10) << '\n'
<< "pow(2, 0.5) = " << std::pow(2, 0.5) << '\n'
<< "pow(-2, -3) = " << std::pow(-2, -3) << '\n';
}
$ ./a.out
pow(2, 10) = 1024
pow(2, 0.5) = 1.41421
pow(-2, -3) = -0.125
$
I/O Buffer & 控制字元(Escape Character)
I/O Buffer
當你使用 std::cout
把資料顯示到螢幕上時,其實並不會立即顯示在螢幕上,而是先送到所謂的 buffer 裡。
要等到下列幾種情況才會做 flushing the buffer 的動作,把 buffer 裡的資料沖到螢幕上:
- 當 buffer 滿的時候
- 當
\n
字元出現的時候 - 當接下來是做輸入的動作的時候 (譬如遇到
std::cin
)。 有時候為了讓資料能立刻顯示到螢幕上,可以用 fflush() 強迫把 buffer 裡的東西送出。 當你發現有時候輸入或輸出的顯示順序會亂掉,可以試著在std::cout
之後用std::flush
來確保資料不會被卡在 buffer 裡。
#include <iostream>
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = '\n';
std::cout << "a: "
<< a
//<< std::flush
<< "b: "
<< b
//<< std::flush
<< "a + b = "
<< c
<< std::endl;
return 0;
}
控制字元(Escape Character)
In ASCII: 0 ... 1F, 7F
#include <iostream>
int main(int argc, char *argv[])
{
char a = 'c', b = '\010', c = 'a';
std::cout << "a: "
<< a
<< "b: "
<< b
<< "c: "
<< c
<< std::endl;
return 0;
}
bool
#include <iostream>
int main()
{
std::cout << std::boolalpha
<< true << '\n'
<< false << '\n'
<< std::noboolalpha
<< true << '\n'
<< false << '\n';
}
$ ./a.out
true
false
1
0
$
NOTE: 會在 tarnary operator ? :
使用
Reference:
- htchen/i2p-nthu by 陳煥宗老師
- Integer literal - cppreference.com
- std::setfill - cppreference.com
- std::setw - cppreference.com
- std::fixed, std::scientific, std::hexfloat, std::defaultfloat - cppreference.com
- std::pow, std::powf, std::powl - cppreference.com
- ASCII Table
- What is a buffer in C?
- Boolean literals - cppreference.com
Lecture 3-2: Operator & expression (1)
operators & priority
increment & decrement
opeartor | syntax |
---|---|
pre-increment | ++a |
pre-decrement | --a |
post-increment | a++ |
post-decrement | a-- |
#include <iostream>
int main()
{
int n1 = 1;
int n2 = ++n1;
int n3 = ++++n1;
int n4 = n1++;
//int n5 = n1++ ++; // error
//int n6 = n1 + ++n1; // undefined behavior
std::cout << "n1 = " << n1 << '\n'
<< "n2 = " << n2 << '\n'
<< "n3 = " << n3 << '\n'
<< "n4 = " << n4 << '\n';
}
NOTE: demo with debugger
$ ./a.out
n1 = 5
n2 = 2
n3 = 4
n4 = 4
$
arithmic operators
opeartor | syntax |
---|---|
unary plus | +a |
unary minus | -a |
addition | a + b |
subtraction | a - b |
multiplication | a * b |
division | a / b |
modulo | a % b |
+a
& -a
#include <iostream>
int main()
{
char c = 0x6a;
int n1 = 1;
unsigned char n2 = 1;
unsigned int n3 = 1;
std::cout << "char: " << c << " int: " << +c << '\n'
<< "-1, where 1 is signed: " << -n1 << '\n'
<< "-1, where 1 is unsigned char: " << -n2 << '\n'
<< "-1, where 1 is unsigned int: " << -n3 << '\n';
}
$ ./a.out
char: j int: 106
-1, where 1 is signed: -1
-1, where 1 is unsigned char: -1
-1, where 1 is unsigned int: 4294967295
$
a % b
#include <iostream>
int main()
{
char c = 2;
unsigned int un = 2;
int n = -10;
std::cout << "2 * (-10), where 2 is a char = " << c * n << '\n'
<< "2 * (-10), where 2 is unsigned = " << un * n << '\n'
<< "-10 / 2.12 = " << n / 2.12 << '\n'
<< "-10 / 21 = " << n / 21 << '\n'
<< "-10 % 21 = " << n % 21 << '\n';
}
$ ./a.out
2 * (-10), where 2 is a char = -20
2 * (-10), where 2 is unsigned = 4294967276
-10 / 2.12 = -4.71698
-10 / 21 = 0
-10 % 21 = -10
$
bitwise operator
opeartor | syntax |
---|---|
bitwise NOT | ~a |
bitwise AND | a & b |
bitwise OR | a \| b |
bitwise XOR | a ^ b |
bitwise left shift | a << b |
bitwise right shift | a >> b |
~a
, a & b
, a | b
, a ^ b
Truth table:
a = 0 | a = 1 | |
---|---|---|
~a | 1 | 0 |
a & b | a = 0 | a = 1 |
---|---|---|
b = 0 | 0 | 0 |
b = 1 | 0 | 1 |
a \| b | a = 0 | a = 1 |
---|---|---|
b = 0 | 0 | 1 |
b = 1 | 1 | 1 |
a ^ b | a = 0 | a = 1 |
---|---|---|
b = 0 | 0 | 1 |
b = 1 | 1 | 0 |
#include <iostream>
#include <iomanip>
#include <bitset> // include for display binary number
int main()
{
uint16_t mask = 0x00f0;
uint32_t x0 = 0x12345678;
uint32_t x1 = x0 | mask;
uint32_t x2 = x0 & ~mask;
uint32_t x3 = x0 & mask;
uint32_t x4 = x0 ^ mask;
uint32_t x5 = ~x0;
using bin16 = std::bitset<16>; // display in 16-bit width binary number
using bin32 = std::bitset<32>; // display in 32-bit width binary number
std::cout << std::hex << std::showbase // show in hexdecemal with base number (0x)
<< "Mask: " << mask << std::setw(49) << bin16(mask) << '\n'
<< "Value: " << x0 << std::setw(42) << bin32(x0) << '\n'
<< "Setting bits: " << x1 << std::setw(35) << bin32(x1) << '\n'
<< "Clearing bits: " << x2 << std::setw(34) << bin32(x2) << '\n'
<< "Selecting bits: " << x3 << std::setw(39) << bin32(x3) << '\n'
<< "XOR-ing bits: " << x4 << std::setw(35) << bin32(x4) << '\n'
<< "Inverting bits: " << x5 << std::setw(33) << bin32(x5) << '\n';
}
$ ./a.out
Mask: 0xf0 0000000011110000
Value: 0x12345678 00010010001101000101011001111000
Setting bits: 0x123456f8 00010010001101000101011011111000
Clearing bits: 0x12345608 00010010001101000101011000001000
Selecting bits: 0x70 00000000000000000000000001110000
XOR-ing bits: 0x12345688 00010010001101000101011010001000
Inverting bits: 0xedcba987 11101101110010111010100110000111
$
a >> b
& a << b
#include <iostream>
int main()
{
std::cout << std::hex << std::showbase;
char c = 0x10;
unsigned long long ull = 0x123;
std::cout << "0x123 << 1 = " << (ull << 1) << '\n'
<< "0x123 << 63 = " << (ull << 63) << '\n' // overflow in unsigned
<< "0x10 << 10 = " << (c << 10) << '\n'; // char is promoted to int
long long ll = -1000;
std::cout << std::dec << "-1000 >> 1 = " << (ll >> 1) << '\n';
}
$ ./a.out
0x123 << 1 = 0x246
0x123 << 63 = 0x8000000000000000
0x10 << 10 = 0x4000
-1000 >> 1 = -500
$
logical operators
opeartor | syntax |
---|---|
negation | !a |
AND | a && b |
inclusive OR | a \|\| b |
Truth table:
a = false | a = true | |
---|---|---|
!a | true | false |
a && b | a = false | a = true |
---|---|---|
b = false | false | false |
b = true | false | true |
a \|\| b | a = false | a = true |
---|---|---|
b = false | false | true |
b = true | true | true |
#include <iostream>
int main()
{
bool a = true;
bool b = false;
std::cout << std::boolalpha
<< "a: " << a << std::endl
<< "b: " << b << std::endl
<< "!a: " << !a << std::endl
<< "a && b: " << a &&
b << std::endl
<< "a || b: " << a ||
b << std::endl;
return 0;
}
$ ./a.out
a: true
b: false
!a: false
a && b: false
a || b: true
$
comparison operators
opeartor | syntax |
---|---|
equal to | a == b |
not equal to | a != b |
less than | a < b |
greater than | a > b |
less than or equal to | a <= b |
greater than or equal to | a >= b |
#include <iostream>
int main()
{
std::cout << std::boolalpha;
int n = -1;
int n2 = 1;
std::cout << " -1 == 1? " << (n == n2) << '\n'
<< "Comparing two signed values:\n"
<< " -1 < 1? " << (n < n2) << '\n'
<< " -1 > 1? " << (n > n2) << '\n';
unsigned int u = 1;
std::cout << "Comparing signed and unsigned:\n"
<< " -1 < 1? " << (n < u) << '\n'
<< " -1 > 1? " << (n > u) << '\n';
}
$ ./a.out
-1 == 1? false
Comparing two signed values:
-1 < 1? true
-1 > 1? false
Comparing signed and unsigned:
-1 < 1? false
-1 > 1? true
$
assign operators
opeartor | syntax | equivalent statements |
---|---|---|
assignment | a = b | a = b |
addition assignment | a += b | temp = a + b; a = temp |
subtraction assignment | a -= b | temp = a - b; a = temp |
multiplication assignment | a *= b | temp = a * b; a = temp |
division assignment | a /= b | temp = a / b; a = temp |
modulo assignment | a %= b | temp = a % b; a = temp |
bitwise AND assignment | a &= b | temp = a & b; a = temp |
bitwise OR assignment | a \|= b | temp = a \| b; a = temp |
bitwise XOR assignment | a ^= b | temp = a ^ b; a = temp |
bitwise left shift assignment | a <<= b | temp = a << b; a = temp |
bitwise right shift assignment | a >>= b | temp = a >> b; a = temp |
conditional operators
opeartor | syntax |
---|---|
conditional operator | a ? b : c |
#include <iostream>
int main()
{
// simple rvalue example
int n = 1 > 2 ? 10 : 11; // 1>2 is false, so n = 11
// simple lvalue example
int m = 10;
(n == m ? n : m) = 7; // n == m is false, so m = 7
std::cout << "n = " << n << "\nm = " << m; //output the result
}
$ ./a.out
n = 11
m = 7
$
priority
precedence | operator |
---|---|
1 | a++ , a-- |
2 | ++a , --a |
3 | +a , -a |
!a , ~a | |
(type) | |
5 | a * b , a / b , a % b |
6 | a + b , a - b |
7 | a << b , a >> b |
9 | a < b , a <= b , a > b , a >= b |
10 | a == b , a != b |
11 | a & b |
12 | a ^ b |
13 | a \| b |
14 | a && b |
15 | a \|\| b |
16 | a ? b : c , a = b , a += b , a -= b , a *= b , a /= b , a %= b , a <<= b , a >>= b , a &= b , a ^= b , a \|= b |
// Credit: E. Chen
#include <iostream>
int main()
{
std::cout << !0 + 1 << std::endl;
std::cout << !(0 + 1) << std::endl;
int a = 6 % 10 / 2 + 1 % 2 >= 3 || !+4 - 9 <= -1;
/***
* step 1 -> int a = 6 % 10 / 2 + 1 % 2 >= 3 || ! (+4) - 9 <= (-1);
(Unary plus and minus)
* step 2 -> int a = 6 % 10 / 2 + 1 % 2 >= 3 || (0) - 9 <= (-1);
(Logical NOT)
* step 3 -> int a = (6 % 10 / 2) + (1 % 2) >= 3 || 0 - 9 <= (-1);
(Arithmetic multiplication, division and remainder)
* step 4 -> int a = (3 + 1) >= 3 || (0 - 9) <= (-1);
(Arithmetic addition and substraction)
* step 5 -> int a = (4 > 3) || (-9 <= -1);
(Relational operators)
* step 6 -> int a = (true || true);
(Logical operators)
* step 7 -> int a = true;
(assignmnet operators)
***/
std::cout << a << std::endl;
return 0;
}
Reference:
- htchen/i2p-nthu by 陳煥宗老師
- Fundamental types - cppreference.com
- Increment/decrement operators - cppreference.com
- Arithmetic operators - cppreference.com
- Logical operators - cppreference.com
- Assignment operators - cppreference.com
- Other operators - cppreference.com
- C++ Operator Precedence - cppreference.com
Lab 3: 科學記號
Lab 3-1: 科學記號顯示 (40%)
- 輸入:以 10 為底的實數。
- 實數的正負號 (sign),正負號輸入大於等於 0 為正數,小於 0 為負數。
- 實數的尾數 (mantissa),尾數部分固定 9 位數字,第一位不為
0
,數值為輸入數字乘以\( \frac{1}{100,000,000} \),。 - 實數的指數部分 (exponent),指數部分為 -126 至 +127。
- 輸出:該實數的科學記號表示,有效位數 (Significant figures - Wikipedia) 顯示固定為 9 位。
- 檔名:lab3_1_<學號>.cpp (e.g. lab3_1_106062802.cpp)
程式需提示使用者輸入實數的正負號 (sign)、實數的尾數 (mantissa,輸入數字乘以\( \frac{1}{100,000,000} \))、實數的指數部分 (exponent),輸出該實數的科學記號表示。
Format
Input real number, sign: <sign>⏎
Input real number, mantissa: <mantissa>⏎
Input real number, exponent: <exponent>⏎
The real number is: <output real number>
Example
$ ./a.out
Input real number, sign: -100⏎
Input real number, mantissa: 123456789⏎
Input real number, exponent: 10⏎
The real number is: -1.23456789e+10
Reference Code:
Credit: 黃芷葳 (110021115)
#include <iostream>
#include <iomanip>
#include <cmath>
int main(void)
{
int s = 0, exp = 0;
double sign = 0, man = 0, num = 0;
std::cout << "Input real number, sign: ";
std::cin >> s;
if (s >= 0)
{
sign = 1;
}
else //s<0
{
sign = -1;
}
std::cout << "Input real number, mantissa: ";
std::cin >> man;
std::cout << "Input real number, exponent: ";
std::cin >> exp;
num = sign * (man * (std::pow(10, exp - 8)));
std::cout << "The real number is: "
<< std::scientific << std::setprecision(8)
<< num << "\n";
return 0;
}
Lab 3-2: 科學記號比大小 (30%)
- 輸入:兩個以 10 為底的實數。
- 實數的正負號 (sign),正負號輸入大於等於 0 為正數,小於 0 為負數。
- 實數的尾數 (mantissa),尾數部分固定 9 位數字,第一位不為
0
,數值為輸入數字乘以\( \frac{1}{100,000,000} \),。 - 實數的指數部分 (exponent),指數部分為 -126 至 +127。
- 輸出:
- 兩實數的科學記號表示,有效位數 (Significant figures - Wikipedia) 顯示固定為 9 位。
- 兩實數比較結果,如果第一數較大則輸出
1
,相同則輸出0
,第一數較小則輸出-1
- 檔名:lab3_2_<學號>.cpp (e.g. lab3_2_106062802.cpp)
程式需提示使用者輸入兩個實數的正負號 (sign)、實數的尾數 (mantissa,輸入數字乘以\( \frac{1}{100,000,000} \))、實數的指數部分 (exponent),程式需輸出數字顯示比較結果。
Format
Input real number (a), sign: <sign a>⏎
Input real number (a), mantissa: <mantissa a>⏎
Input real number (a), exponent: <exponent a>⏎
Input real number (b), sign: <sign b>⏎
Input real number (b), mantissa: <mantissa b>⏎
Input real number (b), exponent: <exponent b>⏎
The real number (a) is: <output real number a>
The real number (b) is: <output real number b>
Compare result: <1, 0, or -1>
Example
$ ./a.out
Input real number (a), sign: -100⏎
Input real number (a), mantissa: 123456789⏎
Input real number (a), exponent: 10⏎
Input real number (b), sign: -10⏎
Input real number (b), mantissa: 123456788⏎
Input real number (b), exponent: 10⏎
The real number (a) is: -1.23456789e+10
The real number (b) is: -1.23456788e+10
Compare result: -1
Reference Code:
Credit: 黃芷葳 (110021115)
#include <iostream>
#include <iomanip>
#include <cmath>
int main(void)
{
int sa = 0, expa = 0, sb = 0, expb = 0;
double signa = 0, mana = 0, signb = 0, manb = 0;
double a = 0, b = 0;
std::cout << "Input real number (a), sign: ";
std::cin >> sa;
if (sa >= 0)
{
signa = 1;
}
else //sa<0
{
signa = -1;
}
std::cout << "Input real number (a), mantissa: ";
std::cin >> mana;
std::cout << "Input real number (a), exponent: ";
std::cin >> expa;
std::cout << "Input real number (b), sign: ";
std::cin >> sb;
if (sb >= 0)
{
signb = 1;
}
else //sb<0
{
signb = -1;
}
std::cout << "Input real number (b), mantissa: ";
std::cin >> manb;
std::cout << "Input real number (b), exponent: ";
std::cin >> expb;
a = signa * (mana * (std::pow(10, expa - 8)));
b = signb * (manb * (std::pow(10, expb - 8)));
std::cout << "The real number (a) is: "
<< std::scientific << std::setprecision(8)
<< a << "\n";
std::cout << "The real number (b) is: "
<< std::scientific << std::setprecision(8)
<< b << "\n";
if (a > b)
{
std::cout << "Compare result: 1"
<< "\n";
}
else if (a == b)
{
std::cout << "Compare result: 0"
<< "\n";
}
else //a<b
{
std::cout << "Compare result: -1"
<< "\n";
}
return 0;
}
Lab 3-3: IEEE-754 誤差計算 (20%)
- 輸入:以 10 為底的實數。
- 實數的正負號 (sign),正負號輸入大於等於 0 為正數,小於 0 為負數。
- 實數的尾數 (mantissa),尾數部分固定 9 位數字,第一位不為
0
,數值為輸入數字乘以\( \frac{1}{100,000,000} \),。 - 實數的指數部分 (exponent),指數部分為 -38 至 +38。
- 輸出:
- 該實數的科學記號表示,有效位數為 9 位。
- 該實數的 IEEE-754 single precision 數值,以科學記號表示,有效位數 (Significant figures - Wikipedia) 顯示固定為 9 位。
- 該實數與 IEEE-754 single precision 數值的差異絕對值,以科學記號表示,有效位數 (Significant figures - Wikipedia) 顯示固定為 9 位。
- 檔名:lab3_3_<學號>.cpp (e.g. lab3_3_106062802.cpp)
程式需提示使用者輸入實數的正負號 (sign)、實數的尾數 (mantissa,輸入數字乘以\( \frac{1}{100,000,000} \))、實數的指數部分 (exponent),輸出該實數的科學記號表示、該實數的 IEEE-754 single precision 數值、該實數與 IEEE-754 single precision 數值的差異絕對值。
Format
Input real number, sign: <sign>⏎
Input real number, mantissa: <mantissa>⏎
Input real number, exponent: <exponent>⏎
The real number is: <output real number>
The IEEE-754 single precision number is: <output float value>
The difference (absolute value): <difference in abs>
Example
$ ./a.out
Input real number, sign: -100⏎
Input real number, mantissa: 123456789⏎
Input real number, exponent: 10⏎
The real number is: -1.23456789e+10
The IEEE-754 single precision number is: -1.23456788e+10
The difference (absolute value): 1.00000000e+02
Reference Code:
#include <iostream>
#include <iomanip>
#include <cmath>
int main(void)
{
long int a = -100, b = 123456789, c = 7;
float c_exp = std::pow(10.0, (double)c);
double b_mantissa = (float)b * 1e-8;
double b_mantissa_double = (double)b * 1e-8;
a = (a >= 0 ? 1 : -1);
float d = a * b_mantissa * c_exp;
double d_real = a * b_mantissa_double * c_exp;
double mantissa_diff = d - d_real;
mantissa_diff = (mantissa_diff >= 0 ? mantissa_diff : mantissa_diff * -1); // abs()
// round mantissa difference
double mantissa_diff_round_unit = std::pow(10.0, (double)c - 8);
double mantissa_diff_quotient = std::round(mantissa_diff / mantissa_diff_round_unit);
// unit is 10^(c-8), because the significant digits are 9 digits
// and with a leading digit, the least significant digit is
// 10^-8 smaller than the leading digit. Thus, the mantissa difference
// must round to 10^(c-8).
mantissa_diff = mantissa_diff_round_unit * mantissa_diff_quotient;
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "c: " << c << std::endl
<< std::scientific << std::setprecision(8)
<< d << std::endl
<< d_real << std::endl
<< mantissa_diff << std::endl;
return 0;
}
Lab 3-4: 精確科學記號比大小 (10%)
- 輸入:兩個以 10 為底的實數。
- 實數的正負號 (sign),正負號輸入大於等於 0 為正數,小於 0 為負數。
- 實數的尾數 (mantissa),尾數部分固定 19 位數字,第一位不為
0
,數值為輸入數字乘以\( \frac{1}{1,000,000,000,000,000,000} \),。 - 實數的指數部分 (exponent),指數部分為 -126 至 +127。
- 輸出:
- 兩實數的科學記號表示,有效位數 (Significant figures - Wikipedia) 顯示固定為 19 位。
- 兩實數比較結果,如果第一數較大則輸出
1
,相同則輸出0
,第一數較小則輸出-1
- 檔名:lab3_4_<學號>.cpp (e.g. lab3_4_106062802.cpp)
程式需提示使用者輸入兩個實數的正負號 (sign)、實數的尾數 (mantissa,輸入數字乘以\( \frac{1}{1,000,000,000,000,000,000} \))、實數的指數部分 (exponent),程式需輸出數字顯示比較結果。
Format
Input real number (a), sign: <sign a>⏎
Input real number (a), mantissa: <mantissa a>⏎
Input real number (a), exponent: <exponent a>⏎
Input real number (b), sign: <sign b>⏎
Input real number (b), mantissa: <mantissa b>⏎
Input real number (b), exponent: <exponent b>⏎
The real number (a) is: <output real number a>
The real number (b) is: <output real number b>
Compare result: <1, 0, or -1>
Example
$ ./a.out
Input real number (a), sign: -100⏎
Input real number (a), mantissa: 1234567890123456789⏎
Input real number (a), exponent: 10⏎
Input real number (b), sign: -10⏎
Input real number (b), mantissa: 1234567890123456788⏎
Input real number (b), exponent: 10⏎
The real number (a) is: -1.234567890123456789e+10
The real number (b) is: -1.234567890123456788e+10
Compare result: -1
Reference Code:
TA version: using unsigned long long
to store mantissa.
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int a_sign, a_exp, b_sign, b_exp;
unsigned long long a_mantissa, b_mantissa;
cout << "Input real number (a), sign: ";
cin >> a_sign;
a_sign = (a_sign >= 0 ? 1 : -1);
cout << "Input real number (a), mantissa: ";
cin >> a_mantissa;
cout << "Input real number (a), exponent: ";
cin >> a_exp;
cout << "Input real number (b), sign: ";
cin >> b_sign;
b_sign = (b_sign >= 0 ? 1 : -1);
cout << "Input real number (b), mantissa: ";
cin >> b_mantissa;
cout << "Input real number (b), exponent: ";
cin >> b_exp;
// initialize
cout << "The real number (a) is: "
<< a_sign * (int)(a_mantissa / (long long)1e+18)
<< "."
<< setw(18) << setfill('0')
<< a_mantissa % (long long)1e+18
<< "e"
<< (a_exp >= 0 ? "+" : "-")
<< setw(2) << setfill('0')
<< (a_exp >= 0 ? a_exp : -a_exp)
<< endl;
cout << "The real number (b) is: "
<< b_sign * (int)(b_mantissa / (long long)1e+18)
<< "."
<< setw(18) << setfill('0')
<< b_mantissa % (long long)1e+18
<< "e"
<< (b_exp >= 0 ? "+" : "-")
<< setw(2) << setfill('0')
<< (b_exp >= 0 ? b_exp : -b_exp)
<< endl;
int result = (a_sign * b_sign == -1 ? (a_sign > b_sign ? 1 : -1) :
(a_exp > b_exp ? a_sign :
(a_exp < b_exp ? -a_sign :
(a_mantissa > b_mantissa ? a_sign :
(a_mantissa < b_mantissa ? -a_sign : 0)
)
)
)
);
cout << "Compare result: "
<< result
<< endl;
return 0;
}
Credit: 鄭楷庭 (110021119)
#include <iostream>
#include <iomanip>
#include <cmath>
#include <string>
int main()
{
long long sign1, num1, sign2, num2;
std::string man1, man2;
signed int expo1, expo2;
int comp = 0, i;
std::cout << "Input real number (a), sign: ";
std::cin >> sign1;
std::cout << "Input real number (a), mantissa: ";
std::cin >> man1;
if (sign1 < 0)
{
man1.insert(0, "-");
man1.insert(2, ".");
}
else
{
man1.insert(1, ".");
}
std::cout << "Input real number (a), exponent: ";
std::cin >> expo1;
std::cout << "Input real number (b), sign: ";
std::cin >> sign2;
std::cout << "Input real number (b), mantissa: ";
std::cin >> man2;
if (sign2 < 0)
{
man2.insert(0, "-");
man2.insert(2, ".");
}
else
{
man2.insert(1, ".");
}
std::cout << "Input real number (b), exponent: ";
std::cin >> expo2;
std::cout << "The real number (a) is: " << man1 << "e" << std::showpos << expo1
<< std::noshowpos << std::endl;
std::cout << "The real number (b) is: " << man2 << "e" << std::showpos << expo2
<< std::noshowpos << std::endl;
if (sign1 >= 0 && sign2 < 0)
comp = 1;
else if (sign1 < 0 && sign2 >= 0)
comp = -1;
else
{
if (sign1 >= 0)
{
if (expo1 > expo2)
comp = 1;
else if (expo1 < expo2)
comp = -1;
else
{
for (i = 0; i < man1.length(); i++)
{
if (man1[i] > man2[i])
{
comp = 1;
break;
}
else if (man1[i] > man2[i])
{
comp = -1;
break;
}
}
}
}
else
{
if (expo1 > expo2)
comp = -1;
else if (expo1 < expo2)
comp = 1;
else
{
for (i = 0; i < man1.length(); i++)
{
if (man1[i] > man2[i])
{
comp = -1;
break;
}
else if (man1[i] > man2[i])
{
comp = 1;
break;
}
}
}
}
}
std::cout << "Compare result: " << comp << std::endl;
}
Lecture 4-1: Operator & expression (2)
number format
https://en.wikipedia.org/wiki/Computer_number_format
Two's compliement
Decimal value | Two's compliement |
---|---|
\(0\) | b000 |
\(1\) | b001 |
\(2\) | b010 |
\(3\) | b011 |
\(-4\) | b100 |
\(-3\) | b101 |
\(-2\) | b110 |
\(-1\) | b111 |
IEEE-754 single precision format
IEEE-754 Floating Point Converter
Case Study: Fast inverse square root
float Q_rsqrt(float number)
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = *(long *) &y; // evil floating point bit level hacking
i = 0x5f3759df - (i >> 1); // what the fuck?
y = *(float *)&i;
y = y * (threehalfs - (x2 * y * y)); // 1st iteration
//y = y * (threehalfs - (x2 * y * y)); // 2nd iteration, this can be removed
return y;
}
example:
$$ x = 0.15625, \frac{1}{\sqrt{x}} \approx 2.52982 $$
bit representation:
0011_1110_0010_0000_0000_0000_0000_0000
Bit pattern of both x
and i
0001_1111_0001_0000_0000_0000_0000_0000
Shift right one position: (i >> 1)
0101_1111_0011_0111_0101_1001_1101_1111
The magic number 0x5F3759DF
0100_0000_0010_0111_0101_1001_1101_1111
The result of 0x5F3759DF - (i >> 1)
IEEE-754 32-bit representation:
0_01111100_01000000000000000000000
\( y = 1.25 \times 2^{-3} \)
0_00111110_00100000000000000000000
\( y = 1.125 \times 2^{-65} \)
0_10111110_01101110101100111011111
\( y = 1.432430... \times 2^{63} \)
0_10000000_01001110101100111011111
\( y = 1.307430... \times 2^{1} \approx 2.61486 \)
Reference:
- htchen/i2p-nthu by 陳煥宗老師
- Two's complement - Wikipedia
- Single-precision floating-point format - Wikipedia
- Fast inverse square root - Wikipedia
Lecture 4-2: Branch Statement
if ...
#include <iostream>
int main()
{
bool flag = true;
if(flag)
{
std::cout << "The flag is true" << std::endl;
}
std::cout << "The normal statement" << std::endl;
}
#include <iostream>
int main()
{
bool flag = true;
if(flag)
std::cout << "The flag is true" << std::endl;
std::cout << "The normal statement" << std::endl;
}
Notice: different from python
#include <iostream>
int main()
{
bool flag = true;
if(flag)
std::cout << "The flag is true" << std::endl;
std::cout << "The normal statement" << std::endl;
}
#include <iostream>
int main()
{
int input = -100;
if(input < 0)
{
std::cout << "The input is negative" << std::endl;
}
}
if ... else
#include <iostream>
int main()
{
bool flag = true;
if(flag)
{
std::cout << "The flag is true" << std::endl;
}
else // flag == false
{
std::cout << "The flag is false" << std::endl;
}
std::cout << "The normal statement" << std::endl;
}
#include <iostream>
int main()
{
int input = -100;
if(input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else // input >= 0
{
std::cout << "The input is positive" << std::endl;
}
}
if ... else if ... else
#include <iostream>
int main()
{
int input = -100;
if(input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else if(input == 0)
{
std::cout << "The input is zero" << std::endl;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
}
nested if ... else
#include <iostream>
int main()
{
int input = -100;
if(input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else
{
if(input == 0)
{
std::cout << "The input is zero" << std::endl;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
}
}
switch ... case
#include <iostream>
int main()
{
int input = -100;
switch(input)
{
case -1:
std::cout << "The input is -1" << std::endl;
//break;
case 0:
std::cout << "The input is 0" << std::endl;
//break;
case 1:
std::cout << "The input is 1" << std::endl;
//break;
default:
std::cout << "The input is others" << std::endl;
}
}
return 0
to end main
function
#include <iostream>
int main()
{
int input = -100;
if(input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else
{
// return 0;
if(input == 0)
{
std::cout << "The input is zero" << std::endl;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
}
return 0;
}
Reference:
Lab 4: 簡易科學計算機
Lab 4-1: 計算機功能選擇 (40%)
- 輸入:要執行的計算功能,分別如下:
- 加 (
+
) - 減 (
-
) - 乘 (
*
) - 除 (
/
)
- 加 (
- 輸出:所選擇的運算子字元 (i.e.
+
,-
,*
,/
) - 檔名:lab4_1_<學號>.cpp (e.g. lab4_1_106062802.cpp)
程式需提示使用者輸入需要的運算功能,輸出該運算功能的運算子字元。
Simple scientific calculator
1) plus (+)
2) minus (-)
3) multiplication (*)
4) division (/)
Please select the operator: <1-4>
You selected: <+, -, *, />
Example:
$ ./a.out
Simple scientific calculator
1) plus (+)
2) minus (-)
3) multiplication (*)
4) division (/)
Please select the operator: 3
You selected: *
Reference Code:
if ... else ...
: Credit: 許景煬 (107071017)
#include <iostream>
#include <iomanip>
int main(void)
{
int a = 0;
std::cout << "Simple scientific calculator\n"
<< "1) plus (+)\n"
<< "2) minus (-)\n"
<< "3) multiplication (*)\n"
<< "4) division (/)\n"
<< "Please select the operator: "
<< std::endl;
std::cin >> a;
if (a == 1)
{
std::cout << "You selected: "
<< "+"
<< std::endl;
}
else if (a == 2)
{
std::cout << "You selected: "
<< "-"
<< std::endl;
}
else if (a == 3)
{
std::cout << "You selected: "
<< "*"
<< std::endl;
}
else if (a == 4)
{
std::cout << "You selected: "
<< "/"
<< std::endl;
}
else
{
std::cout << "Your operator is not in here!!!!!!!"
<< std::endl;
}
}
switch ... case ...
: Credit: 陳欣妤 (110021108)
#include <iostream>
using namespace std;
int main()
{
int a;
cout << "Simple scientific calculator" << '\n'
<< "1) plus (+)" << '\n'
<< "2) minus (-)" << '\n'
<< "3) multiplication (*)" << '\n'
<< "4) division (/)" << '\n'
<< "Please select the operator: ";
cin >> a;
switch (a)
{
case 1:
cout << "You selected: +" << endl;
break;
case 2:
cout << "You selected: -" << endl;
break;
case 3:
cout << "You selected: *" << endl;
break;
case 4:
cout << "You selected: /" << endl;
break;
default:
cout << "You selected: ?" << endl;
}
return 0;
}
Lab 4-2: 計算機四則運算 (40%)
- 輸入:
- 要執行的計算功能,分別如下:
- 加 (
+
) - 減 (
-
) - 乘 (
*
) - 除 (
/
)
- 加 (
- 兩個以 10 為底的實數,以科學記號格式輸入。
- 實數的正負號 (sign),正負號輸入大於等於 0 為正數,小於 0 為負數。
- 實數的尾數 (mantissa),尾數部分固定 9 位數字,第一位不為
0
,數值為輸入數字乘以\( \frac{1}{100,000,000} \),。 - 實數的指數部分 (exponent),指數部分為 -38 至 +38。
- 第二個實數為任意非
0
的數字。
- 要執行的計算功能,分別如下:
- 輸出:所選擇的運算子字元 (i.e.
+
,-
,*
,/
)及兩實數的運算結果,以科學記號表示,有效位數為 15 位。 - 檔名:lab4_2_<學號>.cpp (e.g. lab4_2_106062802.cpp)
程式需提示使用者輸入需要的運算功能,輸出該運算功能的運算子字元。
Simple scientific calculator
1) plus (+)
2) minus (-)
3) multiplication (*)
4) division (/)
Please select the operator: <1-4>
You selected: <+, -, *, />
Input real number (a), sign: <sign a>
Input real number (a), mantissa: <mantissa a>
Input real number (a), exponent: <exponent a>
Input real number (b), sign: <sign b>
Input real number (b), mantissa: <mantissa b>
Input real number (b), exponent: <exponent b>
The real number (a) is: <output real number a>
The real number (b) is: <output real number b>
(a) <+, -, *, /> (b) = <result>
Example:
$ ./a.out
Simple scientific calculator
1) plus (+)
2) minus (-)
3) multiplication (*)
4) division (/)
Please select the operator: 3
You selected: *
Input real number (a), sign: -100
Input real number (a), mantissa: 123456789
Input real number (a), exponent: 10
Input real number (b), sign: -10
Input real number (b), mantissa: 123456788
Input real number (b), exponent: 10
The real number (a) is: -1.23456789e+10
The real number (b) is: -1.23456788e+10
(a) * (b) = 1.52415786267337e+20
Reference Code:
Credit: 張晉赫 (110021109)
#include <iomanip>
#include <iostream>
#include <math.h>
using namespace std;
int main()
{
int selection;
cout << "Simple scientific calculator \n"
<< "1) plus (+) \n"
<< "2) minus (-) \n"
<< "3) multiplication (*) \n"
<< "4) division (/) \n";
cout << "Please select the operator: ";
cin >> selection;
switch (selection)
{
case 1:
cout << "You selected: +" << endl;
break;
case 2:
cout << "You selected: -" << endl;
break;
case 3:
cout << "You selected: *" << endl;
break;
case 4:
cout << "You selected: /" << endl;
break;
}
int sign_a, exponent_a, sign_b, exponent_b;
double mantissa_a, mantissa_b;
cout << "Input real number (a), sign: ";
cin >> sign_a;
cout << "Input real number (a), mantissa: ";
cin >> mantissa_a;
cout << "Input real number (a), exponent: ";
cin >> exponent_a;
cout << "Input real number (b), sign: ";
cin >> sign_b;
cout << "Input real number (b), mantissa: ";
cin >> mantissa_b;
cout << "Input real number (b), exponent: ";
cin >> exponent_b;
mantissa_a = mantissa_a / 100000000;
mantissa_b = mantissa_b / 100000000;
double num_a = pow(10, exponent_a);
double number_a = mantissa_a * num_a;
if (sign_a >= 0)
{
cout << "The real number (a) is: " << setprecision(8) << scientific << number_a << endl;
}
else
{
number_a = -number_a;
cout << "The real number (a) is: " << setprecision(8) << scientific << number_a << endl;
}
double num_b = pow(10, exponent_b);
double number_b = mantissa_b * num_b;
if (sign_b >= 0)
{
cout << "The real number (b) is: " << setprecision(8) << scientific << number_b << endl;
}
else
{
number_b = -number_b;
cout << "The real number (b) is: " << setprecision(8) << scientific << number_b << endl;
}
switch (selection)
{
case 1:
cout << "(a) + (b) = " << setprecision(14) << number_a + number_b << endl;
break;
case 2:
cout << "(a) - (b) = " << setprecision(14) << number_a - number_b << endl;
break;
case 3:
cout << "(a) * (b) = " << setprecision(14) << number_a * number_b << endl;
break;
case 4:
cout << "(a) / (b) = " << setprecision(14) << number_a / number_b << endl;
break;
}
return 0;
}
Lab 4-3: 計算機輸入檢查及例外處理 (20%)
- 輸入:
- 要執行的計算功能,分別如下:
- 加 (
+
) - 減 (
-
) - 乘 (
*
) - 除 (
/
) - 取餘數 (
%
)
- 加 (
- 兩個以 10 為底的實數,以科學記號格式輸入。
- 實數的正負號 (sign),正負號輸入大於等於 0 為正數,小於 0 為負數。
- 實數的尾數 (mantissa),尾數部分固定 9 位數字,第一位不為
0
,數值為輸入數字乘以\( \frac{1}{100,000,000} \),。 - 實數的指數部分 (exponent),指數部分為 -38 至 +38。
- 若為取餘數運算,兩數皆須為正整數。
- 要執行的計算功能,分別如下:
- 輸出:所選擇的運算子字元 (i.e.
+
,-
,*
,/
,%
)及兩實數的運算結果,以科學記號表示,有效位數為 15 位。若輸入不合法的數字則需顯示Invalid input
並結束程式。 - 檔名:lab4_3_<學號>.cpp (e.g. lab4_3_106062802.cpp)
程式需提示使用者輸入需要的運算功能,輸出該運算功能的運算子字元。
程式需檢查使用者輸入的是否符合題目要求,不合法的輸入需在當下的輸入完成後顯示 Invalid input
並結束程式。
不合法輸出格式範例請參考以下範例。
使用者會在每次輸入時輸入任意實數,請考慮所有合法 double
型態的數字輸入。
Simple scientific calculator
1) plus (+)
2) minus (-)
3) multiplication (*)
4) division (/)
5) modulation (%)
Please select the operator: <1-5>
You selected: <+, -, *, /, %>
Input real number (a), sign: <sign a>
Input real number (a), mantissa: <mantissa a>
Input real number (a), exponent: <exponent a>
Input real number (b), sign: <sign b>
Input real number (b), mantissa: <mantissa b>
Input real number (b), exponent: <exponent b>
The real number (a) is: <output real number a>
The real number (b) is: <output real number b>
(a) <+, -, *, /, %> (b) = <result>
Example:
$ ./a.out
Simple scientific calculator
1) plus (+)
2) minus (-)
3) multiplication (*)
4) division (/)
5) modulation (%)
Please select the operator: 3
You selected: *
Input real number (a), sign: -100
Input real number (a), mantissa: 123456789
Input real number (a), exponent: 10
Input real number (b), sign: -10
Input real number (b), mantissa: 123456788
Input real number (b), exponent: 10
The real number (a) is: -1.23456789e+10
The real number (b) is: -1.23456788e+10
(a) * (b) = 1.52415786267337e+20
$ ./a.out
Simple scientific calculator
1) plus (+)
2) minus (-)
3) multiplication (*)
4) division (/)
5) modulation (%)
Please select the operator: 6
Invalid input
$ ./a.out
Simple scientific calculator
1) plus (+)
2) minus (-)
3) multiplication (*)
4) division (/)
5) modulation (%)
Please select the operator: 4
You selected: /
Input real number (a), sign: 0
Input real number (a), mantissa: 12345678912
Invalid input
$ ./a.out
Simple scientific calculator
1) plus (+)
2) minus (-)
3) multiplication (*)
4) division (/)
5) modulation (%)
Please select the operator: 4
You selected: /
Input real number (a), sign: -100
Input real number (a), mantissa: 123456789
Input real number (a), exponent: 10
Input real number (b), sign: -10
Input real number (b), mantissa: 0
Invalid input
Reference Code:
Credit: 郭羽芹 (110021104)
#include <iostream>
#include <iomanip>
using namespace std;
int main(void)
{
int x;
cout << "Simple scientific calculator" << endl;
cout << "1) plus (+)" << endl;
cout << "2) minus (-)" << endl;
cout << "3) multiplication (*)" << endl;
cout << "4) division (/)" << endl;
cout << "5) modulation (%)" << endl;
cout << "Please select the operator: ";
cin >> x;
if (x == 1)
cout << "You selected: +" << endl;
else if (x == 2)
cout << "You selected: -" << endl;
else if (x == 3)
cout << "You selected: *" << endl;
else if (x == 4)
cout << "You selected: /" << endl;
else if (x == 5)
cout << "You selected: %" << endl;
else
{
cout << "Invalid input" << endl;
return 0;
}
double s1, s2, e1, e2;
double m1, m2;
cout << "Input real number (a), sign: ";
cin >> s1;
cout << "Input real number (a), mantissa: ";
if (x == 5)
if (s1 < 1)
{
cout << "Invalid input" << endl;
return 0;
}
cin >> m1;
if (m1 >= 1000000000)
{
cout << "Invalid input" << endl;
return 0;
}
else if (m1 < 100000000)
{
cout << "Invalid input" << endl;
return 0;
}
cout << "Input real number (a), exponent: ";
cin >> e1;
if (e1 < -38 || e1 > 38)
{
cout << "Invalid input" << endl;
return 0;
}
int e11 = e1;
if (e11 != e1)
{
cout << "Invalid input" << endl;
return 0;
}
if (s1 >= 0)
s1 = 1;
else
s1 = -1;
while (m1 >= 10)
m1 /= 10;
for (int i = 0; i < e1; i++)
m1 *= 10;
m1 = m1 * s1;
if (x == 5)
{
if (e1 < 8)
{
cout << "Invalid input" << endl;
return 0;
}
}
cout << "Input real number (b), sign: ";
cin >> s2;
if (x == 5)
if (s2 < 0)
{
cout << "Invalid input" << endl;
return 0;
}
cout << "Input real number (b), mantissa: ";
cin >> m2;
if (m2 >= 1000000000)
{
cout << "Invalid input" << endl;
return 0;
}
else if (m2 < 100000000)
{
cout << "Invalid input" << endl;
return 0;
}
cout << "Input real number (b), exponent: ";
cin >> e2;
if (e2 < -38 || e2 > 38)
{
cout << "Invalid input" << endl;
return 0;
}
int e22 = e2;
if (e22 != e2)
{
cout << "Invalid input" << endl;
return 0;
}
if (s2 >= 0)
s2 = 1;
else
s2 = -1;
while (m2 >= 10)
m2 /= 10;
for (int i = 0; i < e2; i++)
m2 *= 10;
m2 = m2 * s2;
int m22 = m2;
if (x == 5)
{
if (e2 < 8)
{
cout << "Invalid input" << endl;
return 0;
}
}
cout << "The real number (a) is: " << setprecision(8) << scientific << m1 << endl;
cout << "The real number (b) is: " << setprecision(8) << scientific << m2 << endl;
if (x == 1)
cout << "(a) + (b) = " << setprecision(14) << scientific << m1 + m2 << endl;
else if (x == 2)
cout << "(a) - (b) = " << setprecision(14) << scientific << m1 - m2 << endl;
else if (x == 3)
cout << "(a) * (b) = " << setprecision(14) << scientific << m1 * m2 << endl;
else if (x == 4)
cout << "(a) / (b) = " << setprecision(14) << scientific << m1 / m2 << endl;
else
{
long long a = m1, b = m2;
cout << "(a) % (b) = " << setprecision(14) << scientific << a % b << endl;
}
return 0;
}
Credit: 林元鴻 (110021120)
#include <iostream>
#include <iomanip>
#include <cmath>
int main()
{
int a = 0, t = 0;
long long i = 0, a_mod = 0, b_mod = 0, n8 = 100000000;
double exp_a = 0, exp_b = 0, num_a = 0, num_b = 0, sign_a = 0, sign_b = 0;
std::cout << "Simple scientific calculator"
<< "\n"
<< "1) plus (+)"
<< "\n"
<< "2) minus (-)"
<< "\n"
<< "3) multiplication (*)"
<< "\n"
<< "4) division (/)"
<< "\n"
<< "5) modulation (%)"
<< "\n"
<< "Please select the operator: ";
std::cin >> a;
switch (a)
{
case 1:
std::cout << "You selected: +" << std::endl;
break;
case 2:
std::cout << "You selected: -" << std::endl;
break;
case 3:
std::cout << "You selected: *" << std::endl;
break;
case 4:
std::cout << "You selected: /" << std::endl;
break;
case 5:
std::cout << "You selected: %" << std::endl;
break;
default:
std::cout << "Invalid input" << std::endl;
return 0;
}
std::cout << "Input real number (a), sign: ";
std::cin >> sign_a;
if (a == 5 && sign_a < 0)
{
std::cout << "Invalid input" << std::endl;
return 0;
}
std::cout << "Input real number (a), mantissa: ";
std::cin >> num_a;
if (num_a >= 1000000000 || num_a < 100000000)
{
std::cout << "Invalid input" << std::endl;
return 0;
}
if (a == 5 && ((long long)num_a != num_a || num_a <= 0))
{
std::cout << "Invalid input" << std::endl;
return 0;
}
for (i = 1; i <= 10; i += 1)
{
num_a = (num_a >= 10 ? num_a / 10 : num_a);
}
if (sign_a < 0)
num_a = num_a * (-1);
std::cout << "Input real number (a), exponent: ";
std::cin >> exp_a;
while (((long)(num_a * n8)) % (long)(pow(10, t)) == 0)
{
t++;
}
t--;
if (a == 5 && exp_a < (8 - t))
{
std::cout << "Invalid input" << std::endl;
return 0;
}
if (exp_a > 38 || exp_a < -38 || (long)exp_a != exp_a)
{
std::cout << "Invalid input" << std::endl;
return 0;
}
std::cout << "Input real number (b), sign: ";
std::cin >> sign_b;
if (a == 5 && sign_b < 0)
{
std::cout << "Invalid input" << std::endl;
return 0;
}
std::cout << "Input real number (b), mantissa: ";
std::cin >> num_b;
if (num_b >= 1000000000 || num_b < 100000000)
{
std::cout << "Invalid input" << std::endl;
return 0;
}
if (a == 5 && ((long long)num_b != num_b || num_b <= 0))
{
std::cout << "Invalid input" << std::endl;
return 0;
}
for (i = 1; i <= 10; i += 1)
{
num_b = (num_b >= 10 ? num_b / 10 : num_b);
}
if (sign_b < 0)
num_b = num_b * (-1);
std::cout << "Input real number (b), exponent: ";
std::cin >> exp_b;
t = 0;
while (((long)(num_b * n8)) % (long)(pow(10, t)) == 0)
{
t++;
}
t--;
if (a == 5 && exp_b < (8 - t))
{
std::cout << "Invalid input" << std::endl;
return 0;
}
if (exp_b > 38 || exp_b < -38 || (long)exp_b != exp_b)
{
std::cout << "Invalid input" << std::endl;
return 0;
}
a_mod = num_a * pow(10, exp_a);
b_mod = num_b * pow(10, exp_b);
std::cout << "The real number (a) is: " << std::scientific << std::setprecision(8) << num_a * pow(10, exp_a) << std::endl;
std::cout << "The real number (b) is: " << std::scientific << std::setprecision(8) << num_b * pow(10, exp_b) << std::endl;
if (a == 1)
std::cout << "(a) + (b) = " << std::scientific << std::setprecision(14) << num_a * pow(10, exp_a) + num_b * pow(10, exp_b) << std::endl;
if (a == 2)
std::cout << "(a) - (b) = " << std::scientific << std::setprecision(14) << num_a * pow(10, exp_a) - num_b * pow(10, exp_b) << std::endl;
if (a == 3)
std::cout << "(a) * (b) = " << std::scientific << std::setprecision(14) << (num_a * pow(10, exp_a)) * (num_b * pow(10, exp_b)) << std::endl;
if (a == 4)
std::cout << "(a) / (b) = " << std::scientific << std::setprecision(14) << (num_a * pow(10, exp_a)) / (num_b * pow(10, exp_b)) << std::endl;
if (a == 5)
std::cout << "(a) % (b) = " << std::scientific << std::setprecision(14) << (double)(a_mod % b_mod) << std::endl;
return 0;
}
Lecture 5: Loop Statement (1)
What is Loop?
要重複執行特定指令,並在滿足特定條件下結束重複執行
example: check lager than 0
, equal to 0
, or less than 0
#include <iostream>
int main()
{
int input = 0;
std::cout << "Input a integer: ";
std::cin >> input;
if (input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else if (input == 0)
{
std::cout << "The input is zero" << std::endl;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
std::cout << "Input a integer: ";
std::cin >> input;
if (input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else if (input == 0)
{
std::cout << "The input is zero" << std::endl;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
}
for (;;) {}
#include <iostream>
int main()
{
int input = 0;
int type_counts = 3; // check input three times
for (int i = 0; i < type_counts; i++) // for loop
{
std::cout << "Input a integer (" << i << '/' << type_counts << "): ";
std::cin >> input;
if (input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else if (input == 0)
{
std::cout << "The input is zero" << std::endl;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
}
}
分析 for loop 架構
for (int i = 0; i < type_counts; i++) // for loop
{
}
int i = 0;
:宣告並初始化在 loop 內會用到的變數,一個 for loop 只會執行一次。一般會宣告只會在 for loop 內更動的變數,例如第幾次迴圈或是 array 現在使用的 index。
i < type_counts;
:for loop 的執行條件,如果條件滿足就會執行 {}
所包圍著的程式碼一次。
i++
:執行 {}
所包圍著的程式碼完後會執行的部分。一般會變動 int i = 0;
內所宣告的變數,例如下一次迴圈是第幾次回圈的變數或是 array 下一個使用的 index。
NOTE: 以上三個部分都是只能一個 statement,也就是一行以 ;
結束的程式碼。i < type_counts;
部分一定要回傳 true
or false
or 0
or 非 0
的數字。
while () {}
#include <iostream>
int main()
{
int input = 0;
int type_counts = 3; // check input three times
int i = 0; // define index variable
while (i < type_counts) // while loop, check before execute the follows
{
std::cout << "Input a integer (" << i << '/' << type_counts << "): ";
std::cin >> input;
if (input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else if (input == 0)
{
std::cout << "The input is zero" << std::endl;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
i++; // increase index variable
}
}
NOTE: 如果需要宣告在 while loop 內會調整到的變數,需要在 while loop 外宣告。
variable scope
NOTE: demo with gdb, 觀察 i
跟 today_date
。
#include <iostream>
int main()
{
int input = 0;
int type_counts = 3; // check input three times
int i = 0; // define index variable
while (i < type_counts) // while loop, check before execute the follows
{
int i = 0; // wrong example
long today_date = 20211021;
std::cout << "Today is (yyyymmdd): " << today_date << std::endl;
std::cout << "Input a integer (" << i << '/' << type_counts << "): ";
std::cin >> input;
if (input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else if (input == 0)
{
std::cout << "The input is zero" << std::endl;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
i++; // increase index variable
}
//std::cout << "Today is (yyyymmdd): " << today_date << std::endl; // compile error
}
do {} while ();
#include <iostream>
int main()
{
int input = 0;
int type_counts = 3; // check input three times
int i = 0; // define index variable
do
{
std::cout << "Input a integer (" << i << '/' << type_counts << "): ";
std::cin >> input;
if (input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else if (input == 0)
{
std::cout << "The input is zero" << std::endl;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
i++; // increase index variable
} while (i < type_counts); // do while loop, check after execute the aboves
}
infinite loop & not executed loop
infinite loop
#include <iostream>
int main()
{
int input = 0;
int type_counts = 3; // check input three times
int i = 0; // define index variable
while (i < type_counts) // while loop, check before execute the follows
{
std::cout << "Input a integer (" << i << '/' << type_counts << "): ";
std::cin >> input;
if(input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else if(input == 0)
{
std::cout << "The input is zero" << std::endl;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
// i++; // infinite loop, condition is always satisfied
}
}
not executed loop
#include <iostream>
int main()
{
int input = 0;
int type_counts = 0; // check input zero time
int i = 0; // define index variable
while (i < type_counts) // not excute, condition cannot satisify
{
std::cout << "Input a integer (" << i << '/' << type_counts << "): ";
std::cin >> input;
if (input < 0)
{
std::cout << "The input is negative" << std::endl;
}
else if (input == 0)
{
std::cout << "The input is zero" << std::endl;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
i++;
}
}
break
& continue
#include <iostream>
int main()
{
int input = 0;
int type_counts = 3; // check input three times
int i = 0; // define index variable
while (i < type_counts) // while loop, check before execute the follows
{
std::cout << "Input a integer (" << i << '/' << type_counts << "): ";
std::cin >> input;
if(input < 0)
{
std::cout << "The input is negative, continue" << std::endl;
continue;
}
else if(input == 0)
{
std::cout << "The input is zero, exit." << std::endl;
break;
}
else // input > 0
{
std::cout << "The input is positive" << std::endl;
}
i++; // increase index variable, check after break & continue
}
}
nested loop
#include <iostream>
int main()
{
while (true)
{
int height = 0;
int width = 0;
std::cout << "Input height: ";
std::cin >> height;
if (height < 0)
{
std::cout << "Invalid height: " << height << std::endl;
continue;
}
else if (height == 0)
{
std::cout << "Input 0, exit." << std::endl;
break;
}
std::cout << "Input width: ";
std::cin >> width;
if (width < 0)
{
std::cout << "Invalid width: " << width << std::endl;
continue;
}
else if (width == 0)
{
std::cout << "Input 0, exit." << std::endl;
break;
}
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
std::cout << '*' << std::flush;
}
std::cout << std::endl;
}
}
return 0;
}
examples
pyramid
#include <iostream>
int main()
{
while (true)
{
int height = 0;
std::cout << "Input height: ";
std::cin >> height;
if (height < 0)
{
std::cout << "Invalid height: " << height << std::endl;
continue;
}
else if (height == 0)
{
std::cout << "Input 0, exit." << std::endl;
break;
}
for (int i = 1; i < (height + 1); i++)
{
for (int j = 0; j < i; j++)
{
std::cout << '*' << std::flush;
}
std::cout << std::endl;
}
}
return 0;
}
9x9 multiplication table
Multiplication table - Wikipedia
#include <iostream>
#include <iomanip>
int main()
{
for (int i = 1; i < (9 + 1); i++) // index start from 1
{
for (int j = 1; j < (9 + 1); j++) // index start from 1
{
std::cout << i << '*' << j << " = "
<< std::setw(2) << i * j << ' '
<< std::flush;
}
std::cout << std::endl;
}
return 0;
}
Reference:
Lecture 6-1: Loop Statement (2)
guess number
#include <iostream>
#include <cstdlib> // for rand() function
int main()
{
int input = 0;
int type_counts = 3; // check input three times
unsigned int number = rand() % 10; // get a random number from 0 to 9
int i = 0; // define index variable
while (i < type_counts) // while loop, check before execute the follows
{
std::cout << "Input a integer (" << i << '/' << type_counts << "): ";
std::cin >> input;
if(input < number)
{
std::cout << "The input is smaller than the number" << std::endl;
}
else if(input == number)
{
std::cout << "You guessed it!" << std::endl;
return 0;
}
else // input > number
{
std::cout << "The input is larger than the number" << std::endl;
}
i++; // increase index variable, check after break & continue
}
std::cout << "The number is " << number << std::endl;
}
Fibonacci number
$$ \begin{align} F_0 &= 0 \\ F_1 &= 1 \\ F_n &= F_{n-1} + F_{n-2}, n \ge 2 \end{align}$$
Modified from Nth-Fibonacci/Approach_3_O(n)Time&_O(1)_Space.cpp at main · iamrajiv/Nth-Fibonacci (github.com)
#include <iostream>
int main()
{
int current = 1, previous = 0;
int counter = 2, n = 0;
std::cout << "fib number: ";
std::cin >> n;
while (counter <= n)
{
int nextFib = previous + current;
previous = current;
current = nextFib;
counter++;
}
std::cout << (n > 0 ? current : previous) << std::endl;
return 0;
}
Long Division
#include <iostream>
#include <cmath> // for log10() & pow()
int main()
{
unsigned int N = 0, D = 0;
std::cout << "Dividend: ";
std::cin >> N;
std::cout << "Divisor: ";
std::cin >> D;
unsigned int R = N, Q = 0;
unsigned int N_digits = (N == 0) ? 1 : (log10(N) + 1);
unsigned int D_digits = (D == 0) ? 1 : (log10(D) + 1);
unsigned int D_shifted = D * pow(10, N_digits - D_digits);
for (int i = N_digits; i >= D_digits; i--)
{
Q *= 10;
while (D_shifted <= R)
{
R = R - D_shifted;
Q++;
}
D_shifted /= 10;
}
std::cout << N << " / " << D << " = " << Q << " ... " << R << std::endl;
return 0;
}
check prime
Modified from A C++ program to check whether given number is prime or not. (github.com)
#include <iostream>
int main()
{
int n;
bool flag = 0;
std::cout << "Enter a number to check if prime or not : ";
std::cin >> n;
for (int i = 2; i <= n / 2; i++)
{
if (n % i == 0)
{
flag = 1;
break;
}
}
if (flag == 0)
{
std::cout << "\nThe number " << n << " is a prime number." << std::endl;
}
else
{
std::cout << "\nThe number " << n << " is not a prime number." << std::endl;
}
return (0);
}
Reference:
Lecture 6-2: Array & String (1)
What is Array?
將一個變數能夠儲存多個值,並且用數字進行編號 (index) 並且利用數字存取 (i.e. random access)
Fibonacci number
$$ \begin{align} F_0 &= 0 \\ F_1 &= 1 \\ F_n &= F_{n-1} + F_{n-2}, n \ge 2 \end{align}$$
Modified from fibonacci-time-analysis/fib.cpp at master · christopher-siewert/fibonacci-time-analysis (github.com)
#include <iostream>
int main()
{
int n = 0;
std::cout << "fib number: ";
std::cin >> n;
unsigned long long int f[100000]; /// array for values
f[0] = 0;
f[1] = 1;
/// Loop from 2 to n
for (int i = 2; i <= n; i++)
{
f[i] = f[i - 1] + f[i - 2]; /// Equals sum of previous 2
}
std::cout << f[n] << std::endl;
return 0;
}
character array (C String)
Credit: htchen/i2p-nthu by 陳煥宗老師
#include <iostream>
#include <iomanip>
#include <cstring>
int main(void)
{
float minutes, distance;
const float SPEED = 0.083;
int size, letters;
char name[10];
std::cout << "Hi! What's your first name? ";
std::cin >> name;
std::cout << std::endl
<< name
<< ", how many minutes does it take to walk from"
<< std::endl;
std::cout << "your dormitory to the CS building? ";
std::cin >> minutes;
size = sizeof(name);
letters = strlen(name);
distance = minutes * SPEED;
std::cout << std::endl
<< "The distance from your dormitory to the CS building"
<< std::endl;
std::cout << "is about "
<< std::setprecision(3) << distance
<< " kilometers.\n\n";
std::cout << "By the way, your first name has "
<< letters
<< " letters,\n";
std::cout << "and we have "
<< size
<< " bytes to store it in.\n";
return 0;
}
我們來看看這個程式做了哪些事情以及用到了哪些新的東西。 執行這個程式會得到類似下面的結果:
$ ./a.out
Hi! What's your first name? Stevie⏎
Stevie, how many minutes does it take to walk from
your dormitory to the CS building? 10⏎
The distance from your dormitory to the CS building
is about 0.83 kilometers.
By the way, your first name has 6 letters,
and we have 10 bytes to store it in.
程式先用 std::cout
顯示訊息,詢問使用者的名字,然後用 std::cin
來讀取使用者輸入的 "字串",把字串儲存在變數name 裡面。
再來就可以用儲存起來的字串稱呼使用者,並且問一個簡單的問題,再把計算結果顯示在螢幕上。
程式裡面用到的新東西首先是 #include <cstring>
。
回想當初程式需要 #include <iostream>
是因為要用到 std::cout
,同樣地,引入 cstring
是為了要用到其中相關的 function
,在這個例子用到的是 strlen()
這個 function
,它可以計算字串變數裡面所儲存的字串的長度。
傳入的參數是字串,傳回的整數值就是字串長度,以 Stevie
為例,長度是 6。
字串其實是靠所謂的character array
來儲存。
char name[10];
表示 name
會佔用到十個 bytes 的記憶體,等於是有十個空位,每個空位的大小是一個 byte,每個 byte 的空間可以存放一個字元。
字元陣列 character arrays 與字串
S | t | e | v | i | t | \0 |
---|
字串裡的字元必須連續地存放在記憶體中,所以剛好可以用陣列來儲存,因為陣列就是一連串的記憶體空間。字元陣列的每一格空間可以存放一個字元 (char
),當我們宣告 char name[10];
表示要保留十格空間存放十個字元,每一格可以容
納一個 char
型別的資料。當然除了 char
之外,也可用其他型別如 int
來宣告陣列,目前我們先專心探討字元陣列。為了標記整個字串究竟在哪裡算是結尾,c++ 語言使用一個特殊的字元 \0
來表示字串結尾。字元 \0
對應到的 ASCII 值是 0
。我們也可以用整數 0
來代替字元 \0
,但為了有所區別,當字元使用時最好寫成 \0
。
宣告一個字元變數和宣告一個陣列的差別可以用下圖來表示。
char ch; // 佔用一個 byte,型別為 char
char name[10]; // 佔用十個 bytes,型別為 char
宣告陣列產生一個可以容納十個字元的 array,準備用來記錄使用者輸入的字串 (使用者的名字)。
因為要保留一格給 \0
字元來標示字串結尾,所以其實真正能用來記錄的人名長度,最多只能包含九個字元。
如何把字串存入陣列中呢?在前面的例子裡用的方法是 std::cin >> name;
讀取使用者輸入的字串。
參數 name 就是要存放字串的陣列名稱,這個名稱所代表的意義是整個字串的開頭位址。
因此 std::cin
就能由 name 找到陣列開頭位址,一格一格把字元填進去,而且會自動在最後加上 \0
當作結束。
再回到範例,裡面用到 sizeof,它可以計算型別 (譬如 sizeof(float)
) 或資料 (譬如 sizeof(name)
或 sizeof("Stevie")
) 的 byte 數。
若把它用在宣告過的陣列上,它會告訴我們陣列有多少個bytes,如果是 char name[10];
就是 10 bytes。另外 #include <cstring>
是為了用 strlen()
這個 function,它是針對字串而設計的function,可以算出字串長度。
字串長度是靠 \0
來界定,所以如果把某個字串當參數傳給 strlen()
,它就會一個字元一個字元地數,直到遇到 \0'
就停下來,然後回報字串長,看看底下例子中的差異:
#include <iostream>
#include <cstring>
int main(void)
{
std::cout << "The length of string is "
<< strlen("Stevie")
<< ", but "
<< sizeof("Stevie")
<< " bytes are occupied.\n";
return 0;
}
Reference:
Lab 6: 迴圈練習
Lab 6-1: 特殊數列產生機 (35%)
數列會以以下的方式產生:
$$ \begin{align} a_1 &= 1 \\ a_2 &= 2 \\ a_n &= a_{n-2} - a_{n-1}, n \ge 3 \end{align}$$
- 輸入:要得到的數列第幾項 \( n, n \le 92 \), \(n\) 為自然數。
- 輸出:
- 從 \( a_1 \) 至 \( a_n \) 的所有項,並以
,
分隔。 - 數列最後一項不需輸出
,
。
- 從 \( a_1 \) 至 \( a_n \) 的所有項,並以
- 檔名:lab6_1_<學號>.cpp (e.g. lab6_1_106062802.cpp)
程式需提示使用者輸入要得到的數列第幾項,輸出從 \( a_1 \) 至 \( a_n \) 的所有項。
使用者會在每次輸入時輸入任意實數,請考慮所有合法 double
型態的數字輸入。
若輸入不合法的數字則需顯示 Invalid input
並結束程式。
The number of terms: <n>⏎
The series: <a_1>, <a_2>, <a_3>, <a_n>
Example:
$ ./a.out
The number of terms: 1⏎
The series: 1
$ ./a.out
The number of terms: 2⏎
The series: 1, 2
$ ./a.out
The number of terms: 10⏎
The series: 1, 2, -1, 3, -4, 7, -11, 18, -29, 47
$ ./a.out
The number of terms: -1⏎
Invalid input
Reference Code:
Credit: 何宇智(110021110)
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main(void)
{
double z;
long long a = 4, b = 3, c = 0, d, x;
cout << "The number of terms: ";
cin >> z;
x = z;
if (z >= 93 or z <= 0 or x != z)
{
cout << "Invalid input";
return 0;
}
cout << "The series: ";
while (c < z)
{
d = a - b;
a = b;
b = d;
if (c == 0)
{
cout << b;
c += 1;
}
else
{
cout << ", " << b;
c += 1;
}
}
return 0;
}
Lab 6-2: 最大公因數 (35%)
Euclidean algorithm - Wikipedia
可以參考以下 pseudocode:
while a != b
if a > b
a = a - b
else
b = b - a
return a
- 輸入:
- 兩自然數字 \( a, b \) 並且各自不多於 18 位數字。
- 需偵測不合法的輸入,並輸出
Invalid input
。 - 需支援重複輸入,使用者輸入
0
則結束程式。
- 輸出:兩自然數字 \( a, b \) 的最大公因數 \( gcd(a, b) \)。
- 檔名:lab6_2_<學號>.cpp (e.g. lab6_2_106062802.cpp)
程式需提示使用者輸入兩數字 \( a, b \),輸出兩數字 \( a, b \) 的最大公因數 \( gcd(a, b) \)。
使用者會在每次輸入時輸入任意整數,請考慮所有合法 long long
型態的數字輸入。
The number a (input 0 to exit): <a>⏎
The number b (input 0 to exit): <b>⏎
The gcd(a, b) = <gcd(a, b)>
Example:
$ ./a.out
The number a (input 0 to exit): 15⏎
The number b (input 0 to exit): 17⏎
The gcd(a, b) = 1
The number a (input 0 to exit): 16⏎
The number b (input 0 to exit): 8⏎
The gcd(a, b) = 8
The number a (input 0 to exit): 16⏎
The number b (input 0 to exit): 0⏎
$ ./a.out
The number a (input 0 to exit): -1⏎
Invalid input
The number a (input 0 to exit): 16⏎
The number b (input 0 to exit): -1⏎
Invalid input
The number a (input 0 to exit): 0⏎
Reference Code:
Credit: 藍珮芳(110021116)
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main(int argc, char **argv)
{
long long int a, b, gcd;
int i = 0, j = 0, k = 0;
while (true)
{
cout << "The number a (input 0 to exit): ";
cin >> a;
if (a >= (long long int)pow(10, 18) || a < 0)
{
cout << "Invalid input\n";
continue;
}
if (a == 0)
return 0;
cout << "The number b (input 0 to exit): ";
cin >> b;
if (b >= (long long int)pow(10, 18) || b < 0)
{
cout << "Invalid input\n";
continue;
}
if (b == 0)
return 0;
while (a != 0 && b != 0)
{
if (a >= b)
{
a = a % b;
}
else
{
b = b % a;
}
}
if (a >= b)
gcd = a;
else
gcd = b;
cout << "The gcd(a, b) = " << gcd << "\n\n";
}
}
Lab 6-3: 大數取餘數 (15%)
Division algorithm - Wikipedia
可以參考以下正整數取餘數的 pseudocode:
R = N
while R >= D do
R = R - D
end
return R
- 輸入:
- 兩自然數字 \( N, D \) 並且各自不多於 36 位數字。
- 兩自然數字的前後 18 位數字需分別輸入。
- \( N = N_{first} \times 10^{18} + N_{second}, D = D_{first} \times 10^{18} + D_{second} \)
- 需偵測不合法的輸入,並輸出
Invalid input
後提示使用者再次輸入該自然數。 - 需支援重複輸入,使用者輸入
-1
則結束程式。
- 輸出:兩自然數字 \( N, D \) 及相除的餘數 \( N % D \)。
- 檔名:lab6_3_<學號>.cpp (e.g. lab6_3_106062802.cpp)
程式需提示使用者輸入兩自然數字 \( N, D \),輸出兩自然數字相除的餘數 \( N % D \)。
使用者會在每次輸入時輸入任意整數,請考慮所有合法 long long
型態的數字輸入。
The number N, first part (input -1 to exit): <N_first>⏎
The number N, second part (input -1 to exit): <N_second>⏎
The number N = <N>
The number D, first part (input -1 to exit): <D_first>⏎
The number D, second part (input -1 to exit): <D_second>⏎
The number D = <D>
The remainder N % D = <N % D>
Example:
$ ./a.out
The number N, first part (input -1 to exit): 2⏎
The number N, second part (input -1 to exit): 2⏎
The number N = 2000000000000000002
The number D, first part (input -1 to exit): 1⏎
The number D, second part (input -1 to exit): 0⏎
The number D = 1000000000000000000
The remainder N % D = 2
The number N, first part (input -1 to exit): 1234567890123456789⏎
Invalid input
The number N, first part (input -1 to exit): 123456789123456789⏎
The number N, second part (input -1 to exit): 1234567890123456789⏎
Invalid input
The number N, first part (input -1 to exit): 123456789123456789⏎
The number N, second part (input -1 to exit): 123456789123456789⏎
The number N = 123456789123456789123456789123456789
The number D, first part (input -1 to exit): 1111111110111111111⏎
Invalid input
The number D, first part (input -1 to exit): 111111111111111111⏎
The number D, second part (input -1 to exit): 1111111110111111111⏎
Invalid input
The number D, first part (input -1 to exit): 111111111111111111⏎
The number D, second part (input -1 to exit): 111111111111111111⏎
The number D = 111111111111111111111111111111111111
The remainder N % D = 12345678012345678012345678012345678
The number N, first part (input -1 to exit): -1⏎
$ ./a.out
The number N, first part (input -1 to exit): -2⏎
Invalid input
The number N, first part (input -1 to exit): 1⏎
The number N, second part (input -1 to exit): -2⏎
Invalid input
The number N, first part (input -1 to exit): 2⏎
The number N, second part (input -1 to exit): 2⏎
The number N = 2000000000000000002
The number D, first part (input -1 to exit): -1⏎
Reference:
Reference Code:
Credit: 林暐晉(110021134)
#include <iostream>
#include <iomanip>
#include <math.h>
using namespace std;
int main()
{
unsigned long long long_a_a = 0, long_a_b = 0, long_b_a = 0, long_b_b = 0;
while (1)
{
while (cout << "The number N, first part (input -1 to exit): ")
{
cin >> long_a_a;
if (long_a_a == -1)
{
return 0;
}
if (long_a_a < 0 || long_a_a >= (long long)pow(10, 18))
{
cout << "Invalid input" << endl;
continue;
}
cout << "The number N, second part (input -1 to exit): ";
cin >> long_a_b;
if (long_a_b == -1)
{
return 0;
}
if (long_a_b < 0 || long_a_b >= (long long)pow(10, 18))
{
cout << "Invalid input" << endl;
continue;
}
if (long_a_a == 0 && long_a_b == 0)
{
cout << "Invalid input" << endl;
continue;
}
if (long_a_a == 0)
{
cout << "The number N = " << long_a_b << endl;
}
else
{
cout << "The number N = " << long_a_a << setfill('0') << setw(18) << long_a_b << endl;
}
break;
}
while (cout << "The number D, first part (input -1 to exit): ")
{
cin >> long_b_a;
if (long_b_a == -1)
{
return 0;
}
if (long_b_a < 0 || long_b_a >= (long long)pow(10, 18))
{
cout << "Invalid input" << endl;
continue;
}
cout << "The number D, second part (input -1 to exit): ";
cin >> long_b_b;
if (long_b_b == -1)
{
return 0;
}
if (long_b_b < 0 || long_b_b >= (long long)pow(10, 18))
{
cout << "Invalid input" << endl;
continue;
}
if (long_b_a == 0 && long_b_b == 0)
{
cout << "Invalid input" << endl;
continue;
}
if (long_b_a == 0)
{
cout << "The number D = " << long_b_b << endl;
}
else
{
cout << "The number D = " << long_b_a << setfill('0') << setw(18) << long_b_b << endl;
}
break;
}
while (long_a_a >= long_b_a)
{
if (long_b_a == 0 && long_b_b != 0)
{
long_a_b %= long_b_b;
long_a_a--;
if (long_a_a > (long long)pow(10, 18))
{
long_a_a++;
break;
}
long_a_b += (long long)pow(10, 18);
}
else
{
long_a_a -= long_b_a;
if (long_a_b < long_b_b)
{
long_a_a--;
if (long_a_a > (long long)pow(10, 18))
{
long_a_a++;
long_a_a += long_b_a;
break;
}
long_a_b += (long long)pow(10, 18);
long_a_b -= long_b_b;
}
else
{
long_a_b -= long_b_b;
}
}
}
if (long_a_a == 0)
{
cout << "The remainder N % D = " << long_a_b << endl;
}
else
{
cout << "The remainder N % D = " << long_a_a << setfill('0') << setw(18) << long_a_b << endl;
}
cout << endl;
}
return 0;
}
Lab 6-4: 大數最大公因數 (15%)
Euclidean algorithm - Wikipedia
可以參考以下 pseudocode:
while b != 0
t = b
b = a % b
a = t
return a
- 輸入:
- 兩自然數字 \( a, b \) 並且各自不多於 36 位數字。
- 兩自然數字的前後 18 位數字需分別輸入。
- \( a = a_{first} \times 10^{18} + a_{second}, b = b_{first} \times 10^{18} + b_{second} \)
- 需偵測不合法的輸入,並輸出
Invalid input
後提示使用者再次輸入該自然數。 - 需支援重複輸入,使用者輸入
-1
則結束程式。
- 輸出:兩自然數字 \( a, b \) 的最大公因數 \( gcd(a, b) \)。
- 檔名:lab6_4_<學號>.cpp (e.g. lab6_4_106062802.cpp)
程式需提示使用者輸入兩數字 \( a, b \),輸出兩數字 \( a, b \) 的最大公因數 \( gcd(a, b) \)。
使用者會在每次輸入時輸入任意整數,請考慮所有合法 long long
型態的數字輸入。
The number a, first part (input -1 to exit): <a_first>⏎
The number a, second part (input -1 to exit): <a_second>
The number a = <a>
The number b, first part (input -1 to exit): <b_first>⏎
The number b, second part (input -1 to exit): <b_second>⏎
The number b = <b>
The gcd(a, b) = <gcd(a, b)>
Example:
$ ./a.out
The number a, first part (input -1 to exit): 123492001107001353⏎
The number a, second part (input -1 to exit): 123492001107001353⏎
The number a = 123492001107001353123492001107001353
The number b, first part (input -1 to exit): 99396000891001089⏎
The number b, second part (input -1 to exit): 99396000891001089⏎
The number b = 99396000891001089099396000891001089
The gcd(a, b) = 3012000027000033003012000027000033
The number a, first part (input -1 to exit): 987654321⏎
The number a, second part (input -1 to exit): 200000000000000000⏎
The number a = 987654321200000000000000000
The number b, first part (input -1 to exit): 123456789⏎
The number b, second part (input -1 to exit): 200000000000000000⏎
The number b = 123456789200000000000000000
The gcd(a, b) = 400000000000000000
The number a, first part (input -1 to exit): 1234567890123456789⏎
Invalid input
The number a, first part (input -1 to exit): 123456789123456789⏎
The number a, second part (input -1 to exit): 1234567890123456789⏎
Invalid input
The number a, first part (input -1 to exit): 123456789123456789⏎
The number a, second part (input -1 to exit): 123456789123456789⏎
The number a = 123456789123456789123456789123456789
The number b, first part (input -1 to exit): 1111111110111111111⏎
Invalid input
The number b, first part (input -1 to exit): 111111111111111111⏎
The number b, second part (input -1 to exit): 1111111110111111111⏎
Invalid input
The number b, first part (input -1 to exit): 111111111111111111⏎
The number b, second part (input -1 to exit): 111111111111111111⏎
The number b = 111111111111111111111111111111111111
The gcd(a, b) = 9000000009000000009000000009
The number a, first part (input -1 to exit): -1⏎
$ ./a.out
The number a, first part (input -1 to exit): -2⏎
Invalid input
The number a, first part (input -1 to exit): 1⏎
The number a, second part (input -1 to exit): -2⏎
Invalid input
The number a, first part (input -1 to exit): 2⏎
The number a, second part (input -1 to exit): 2⏎
The number a = 2000000000000000002
The number b, first part (input -1 to exit): -1⏎
Reference:
Reference Code:
Credit: 林暐晉(110021134)
#include <iostream>
#include <iomanip>
#include <math.h>
using namespace std;
int main()
{
unsigned long long long_a_a = 0, long_a_b = 0, long_b_a = 0, long_b_b = 0, t_a = 0, t_b = 0;
while (1)
{
while (cout << "The number a, first part (input -1 to exit): ")
{
cin >> long_a_a;
if (long_a_a == -1)
{
return 0;
}
if (long_a_a < 0 || long_a_a >= (long long)pow(10, 18))
{
cout << "Invalid input" << endl;
continue;
}
cout << "The number a, second part (input -1 to exit): ";
cin >> long_a_b;
if (long_a_b == -1)
{
return 0;
}
if (long_a_b < 0 || long_a_b >= (long long)pow(10, 18))
{
cout << "Invalid input" << endl;
continue;
}
if (long_a_a == 0 && long_a_b == 0)
{
cout << "Invalid input" << endl;
continue;
}
if (long_a_a == 0)
{
cout << "The number a = " << long_a_b << endl;
}
else
{
cout << "The number a = " << long_a_a << setfill('0') << setw(18) << long_a_b << endl;
}
break;
}
while (cout << "The number b, first part (input -1 to exit): ")
{
cin >> long_b_a;
if (long_b_a == -1)
{
return 0;
}
if (long_b_a < 0 || long_b_a >= (long long)pow(10, 18))
{
cout << "Invalid input" << endl;
continue;
}
cout << "The number b, second part (input -1 to exit): ";
cin >> long_b_b;
if (long_b_b == -1)
{
return 0;
}
if (long_b_b < 0 || long_b_b >= (long long)pow(10, 18))
{
cout << "Invalid input" << endl;
continue;
}
if (long_b_a == 0 && long_b_b == 0)
{
cout << "Invalid input" << endl;
continue;
}
if (long_b_a == 0)
{
cout << "The number b = " << long_b_b << endl;
}
else
{
cout << "The number b = " << long_b_a << setfill('0') << setw(18) << long_b_b << endl;
}
break;
}
while (long_b_a != 0 || long_b_b != 0)
{
t_a = long_b_a;
t_b = long_b_b;
while (long_a_a >= long_b_a)
{
if (long_b_a == 0 && long_b_b != 0)
{
long_a_b %= long_b_b;
long_a_a--;
if (long_a_a > (long long)pow(10, 18))
{
long_a_a++;
break;
}
long_a_b += (long long)pow(10, 18);
}
else
{
long_a_a -= long_b_a;
if (long_a_b < long_b_b)
{
long_a_a--;
if (long_a_a > (long long)pow(10, 18))
{
long_a_a++;
long_a_a += long_b_a;
break;
}
long_a_b += (long long)pow(10, 18);
long_a_b -= long_b_b;
}
else
{
long_a_b -= long_b_b;
}
}
}
long_b_a = long_a_a;
long_b_b = long_a_b;
long_a_a = t_a;
long_a_b = t_b;
}
if (long_a_a == 0)
{
cout << "The gcd(a, b) = " << long_a_b << endl;
}
else
{
cout << "The gcd(a, b) = " << long_a_a << setfill('0') << setw(18) << long_a_b << endl;
}
cout << endl;
}
return 0;
}
Lecture 7: Array & String (2)
std::vector
std::vector
是 C++ 提供的標準樣板函式庫 (Standard Template Library, STL)。
可以把這個想成更為方便使用的 array 。
支援任何型態、動態調整大小及使用變數宣告大小。
Ref: Vector in C++ STL - GeeksforGeeks
// C++ program to illustrate the
// iterators in vector
#include <iostream>
#include <vector>
#include <iterator>
int main()
{
std::vector<int> g1;
for (int i = 1; i <= 5; i++)
g1.push_back(i);
std::cout << "Output of begin and end: ";
for (auto i = g1.begin(); i != g1.end(); ++i)
std::cout << *i << " ";
std::cout << "\nOutput of cbegin and cend: ";
for (auto i = g1.cbegin(); i != g1.cend(); ++i)
std::cout << *i << " ";
std::cout << "\nOutput of rbegin and rend: ";
for (auto ir = g1.rbegin(); ir != g1.rend(); ++ir)
std::cout << *ir << " ";
std::cout << "\nOutput of crbegin and crend : ";
for (auto ir = g1.crbegin(); ir != g1.crend(); ++ir)
std::cout << *ir << " ";
return 0;
}
define
#include <vector>
int main()
{
std::vector<int> g1;
}
#include <vector>
是使用 std::vector
要 include 的函式庫。
宣告變數時需要使用 < >
來指定 std::vector
array 需要存放的型態,這邊以 int
為例,也可以使用其他型態,例如:double
、float
、long
等。
但是不建議用 char
,因為有 std::string
支援字串處理,使用起來比 std::vector<char>
方便。
set/get element's value, push()
& pop()
// C++ program to illustrate the
// Modifiers in vector
#include <vector>
#include <iostream>
using namespace std; // shortcut of std::
int main()
{
// Assign vector
vector<int> v;
// fill the array with 10 five times
v.assign(5, 10);
cout << "The vector elements are: ";
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
// inserts 15 to the last position
v.push_back(15);
int n = v.size();
cout << "\nThe last element is: " << v[n - 1];
// prints the vector
cout << "\nThe vector elements are: ";
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
// removes last element
v.pop_back();
// inserts 5 at the beginning
v.insert(v.begin(), 5);
cout << "\nThe first element is: " << v[0];
// prints the vector
cout << "\nThe vector elements are: ";
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
// removes the first element
v.erase(v.begin());
cout << "\nThe first element is: " << v[0];
// prints the vector
cout << "\nThe vector elements are: ";
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
// inserts at the beginning
v.emplace(v.begin(), 5);
cout << "\nThe first element is: " << v[0];
// prints the vector
cout << "\nThe vector elements are: ";
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
// Inserts 20 at the end
v.emplace_back(20);
n = v.size();
cout << "\nThe last element is: " << v[n - 1];
// prints the vector
cout << "\nThe vector elements are: ";
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
// change v[3] = 30
v[3] = 30;
// prints the vector
cout << "\nThe vector elements are: ";
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
// erases the vector
v.clear();
cout << "\nVector size after erase(): " << v.size();
// prints the vector
cout << "\nThe vector elements are: ";
for (int i = 0; i < v.size(); i++)
cout << v[i] << " ";
// two vector to perform swap
vector<int> v1, v2;
v1.push_back(1);
v1.push_back(2);
v2.push_back(3);
v2.push_back(4);
cout << "\n\nVector 1: ";
for (int i = 0; i < v1.size(); i++)
cout << v1[i] << " ";
cout << "\nVector 2: ";
for (int i = 0; i < v2.size(); i++)
cout << v2[i] << " ";
// Swaps v1 and v2
v1.swap(v2);
cout << "\nAfter Swap \nVector 1: ";
for (int i = 0; i < v1.size(); i++)
cout << v1[i] << " ";
cout << "\nVector 2: ";
for (int i = 0; i < v2.size(); i++)
cout << v2[i] << " ";
}
2-d std::vector
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
int main()
{
// define 2-d vector
vector< vector<int> > table;
// setup 2-d vector
for (int i = 0; i < 9; i++) // index start from 0
{
table.push_back(vector<int>()); // push a vector<int> without varaible name
for (int j = 0; j < 9; j++) // index start from 0
{
table[i].push_back((i + 1) * (j + 1));
}
}
// print 2-d vector
for (int i = 0; i < table.size(); i++) // index start from 0
{
for (int j = 0; j < table[i].size(); j++) // index start from 0
{
std::cout << (i+1) << '*' << (j+1) << " = "
<< std::setw(2) << table[i][j] << ' '
<< std::flush;
}
std::cout << std::endl;
}
return 0;
}
atoi
, atol
Ref: Converting Strings to Numbers in C/C++ - GeeksforGeeks
// For C++11 above
#include <cstdlib>
#include <iostream>
using namespace std;
int main()
{
const char *str1 = "42";
const char *str2 = "3.14159";
const char *str3 = "31337 geek";
int num1 = atoi(str1);
int num2 = atoi(str2);
int num3 = atoi(str3);
cout << "atoi(\"" << str1 << "\") is " << num1 << '\n';
cout << "atoi(\"" << str2 << "\") is " << num2 << '\n';
cout << "atoi(\"" << str3 << "\") is " << num3 << '\n';
return 0;
}
Ref: Write your own atoi() - GeeksforGeeks
// Initialize result
int res = 0;
// Initialize sign as positive
int sign = 1;
// Initialize index of first digit
int i = 0;
// If number is negative,
// then update sign
if (str[0] == '-')
{
sign = -1;
// Also update index of first digit
i++;
}
// Iterate through all digits
// and update the result
for (; str[i] != '\0'; ++i)
res = res * 10 + str[i] - '0';
// Return result with sign
return sign * res;
use a std::vector
to save a number
#include <iostream>
#include <cctype> // isdigit()
#include <algorithm> // reverse()
#include <vector> // vector<int>()
using namespace std;
int main()
{
char input_buffer[10000 + 1]; // + 1 for the last '\0'
vector<int> a;
cout << "Input a non-negative integer number (a): ";
cin >> input_buffer;
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return 0;
}
else
{
int digit = input_buffer[i] - '0';
a.push_back(digit);
}
}
reverse(a.begin(), a.end());
// clean up input buffer
for (int i = 0; i < (10000 + 1); i++)
{
input_buffer[i] = '\0';
}
// remove leading zeros
for (int i = a.size() - 1; i >= 1; i--)
{
if (a[i] == 0)
{
a.pop_back();
}
else
{
break;
}
}
cout << "(a) = ";
for (int i = a.size() - 1; i >= 0; i--)
{
cout << a[i];
}
cout << endl;
}
Output:
$ ./a.out
Input a non-negative integer number (a): 000001234500000⏎
(a) = 1234500000
check isdigit
Ref: std::isdigit - cpprefrence.com
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return 0;
}
push digits & reverse
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return 0;
}
else
{
int digit = input_buffer[i] - '0';
a.push_back(digit);
}
}
// check gdb
reverse(a.begin(), a.end());
// check gdb
std::reverse
Reference: std::reverse - cppreference.com
#include <vector>
#include <iostream>
#include <algorithm>
int main()
{
std::vector<int> v{1, 2, 3};
std::reverse(v.begin(), v.end());
for (int i = 0; i < v.size(); i++)
{
std::cout << v[i];
}
std::cout << '\n';
}
clean up input buffer & remove leading zeros
// clean up input buffer
for (int i = 0; i < (10000 + 1); i++)
{
input_buffer[i] = '\0';
}
// remove leading zeros
for (int i = a.size() - 1; i >= 1; i--) // start from the most significant digit
{
if (a[i] == 0)
{
a.pop_back(); // remove the last element in vector,
// which is the most significant digit
}
else
{
break; // stop if process the most significant non-zero digit
}
}
print the number
cout << "(a) = ";
for (int i = a.size() - 1; i >= 0; i--)
{
cout << a[i];
}
cout << endl;
Lab 7: 真・大數四則運算
- 輸入:
- 要執行的計算功能,分別如下:
- 僅顯示兩數 (
display only
) (35%) - 加 (
+
) (25%) - 減 (
-
) (20%) - 乘 (
*
) (10%) - 除 (
/
) (10%)
- 僅顯示兩數 (
- 兩整數 \( a, b \),兩整數不多於 10,000 位數字。。
- 要執行的計算功能,分別如下:
- 輸出:所選擇的功能 (i.e.
display only
,+
,-
,*
,/
)及兩整數 \( a, b \) 的運算結果,結果為整數。。 - 檔名:lab7_1_<學號>.cpp (e.g. lab7_1_106062802.cpp)
程式需提示使用者輸入需要的運算功能,輸出該運算功能及兩整數 \( a, b \) 的運算結果。
程式需檢查使用者輸入的是否符合題目要求,不合法的輸入需在當下的輸入完成後顯示 Invalid input:
以及錯誤的輸入並結束程式。
不合法輸出格式範例請參考以下範例。
使用者會在每次輸入時輸入任意非空白 (i.e. 非 space, tab) 字元。
程式需在 30 秒之內執行完畢,所有測資皆不會超過 30 秒的執行時間。
Format
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: <1-5>⏎
You selected: <display only,+, -, *, />
Input integer number (a): <a>⏎
Input integer number (b): <b>⏎
(a) = <a>
(b) = <b>
(a) <+, -, *, /> (b) = <result>
Example
Display
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 1⏎
You selected: display only
Input integer number (a): 123456789012345678901234567890⏎
Input integer number (b): 987654321098765432109876543210⏎
(a) = 123456789012345678901234567890
(b) = 987654321098765432109876543210
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 1⏎
You selected: display only
Input integer number (a): -0000000000000
Input integer number (b): -00000001
(a) = 0
(b) = -1
Plus
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 2⏎
You selected: +
Input integer number (a): 123456789012345678901234567890⏎
Input integer number (b): 987654321098765432109876543210⏎
(a) = 123456789012345678901234567890
(b) = 987654321098765432109876543210
(a) + (b) = 1111111110111111111011111111100
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 2⏎
You selected: +
Input integer number (a): 123456789012345678901234567890⏎
Input integer number (b): -987654321098765432109876543210⏎
(a) = 123456789012345678901234567890
(b) = -987654321098765432109876543210
(a) + (b) = -864197532086419753208641975320
Minus
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 3⏎
You selected: -
Input integer number (a): 123456789012345678901234567890⏎
Input integer number (b): 987654321098765432109876543210⏎
(a) = 123456789012345678901234567890
(b) = 987654321098765432109876543210
(a) - (b) = -864197532086419753208641975320
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 3⏎
You selected: -
Input integer number (a): 123456789012345678901234567890⏎
Input integer number (b): -987654321098765432109876543210⏎
(a) = 123456789012345678901234567890
(b) = -987654321098765432109876543210
(a) - (b) = 1111111110111111111011111111100
Multiplication
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 4⏎
You selected: *
Input integer number (a): 123456789012345678901234567890⏎
Input integer number (b): 987654321098765432109876543210⏎
(a) = 123456789012345678901234567890
(b) = 987654321098765432109876543210
(a) * (b) = 121932631137021795226185032733622923332237463801111263526900
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 4⏎
You selected: *
Input integer number (a): 123456789012345678901234567890⏎
Input integer number (b): -987654321098765432109876543210⏎
(a) = 123456789012345678901234567890
(b) = -987654321098765432109876543210
(a) * (b) = -121932631137021795226185032733622923332237463801111263526900
Division
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 5⏎
You selected: /
Input integer number (a): 987654321098765432109876543210⏎
Input integer number (b): 123456789012345678901234567890⏎
(a) = 987654321098765432109876543210
(b) = 123456789012345678901234567890
(a) / (b) = 8
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 5⏎
You selected: /
Input integer number (a): -987654321098765432109876543210⏎
Input integer number (b): 123456789012345678901234567890⏎
(a) = -987654321098765432109876543210
(b) = 123456789012345678901234567890
(a) / (b) = -8
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 5⏎
You selected: /
Input integer number (a): -987654321098765432109876543210⏎
Input integer number (b): -123456789012345678901234567890⏎
(a) = -987654321098765432109876543210
(b) = -123456789012345678901234567890
(a) / (b) = 8
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 5⏎
You selected: /
Input integer number (a): -123456789012345678901234567890⏎
Input integer number (b): 987654321098765432109876543210⏎
(a) = -123456789012345678901234567890
(b) = 987654321098765432109876543210
(a) / (b) = 0
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 5⏎
You selected: /
Input integer number (a): 123456789012345678901234567890⏎
Input integer number (b): -987654321098765432109876543210⏎
(a) = 123456789012345678901234567890
(b) = -987654321098765432109876543210
(a) / (b) = 0
Exceptions
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: +⏎
Invalid input: +
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 00000001⏎
Invalid input: 00000001
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 1⏎
You selected: display only
Input integer number (a): +_)(*(&#$)(*@()*@!⏎
Invalid input: +_)(*(&#$)(*@()*@!
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 1⏎
You selected: display only
Input integer number (a): 0.0⏎
Invalid input: 0.0
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) minus (-)
4) multiplication (*)
5) division (/)
Please select the operator: 4⏎
You selected: /
Input integer number (a): 123⏎
Input integer number (b): 0⏎
Invalid input: 0
Pseudo Code
#include <iostream>
#include <algorithm>
#include <cctype>
#include <vector>
using namespace std ;
int main()
{
char buffer[10001]; // "+1" to store '\0'
vector <int> a, b, n;
int a_sign = 1, b_sign = 1 ;
cin >> buffer ;
// ToDo:逐字檢查,不合法顯示input後exit,合法則將(buffer[i] - '0')存入vector<int> n
// isdigit():判斷是否為數字
// reverse(.begin(), .end()):顛倒,將個位數移至第一位
// .push_back():進位、填入數字
// .pop_back():清除不必的'0'和'-'
// .size():n的位數
// 注意以上函數都需加上(),即便有的沒有input
// 有加'.'在前面的,開頭需指定該vector,例如: n.size()
switch (n[0])
{
case /* 1-5 */:
/* 提示字串 */
break;
}
for(int i=0; buffer[i] != '\0'; i++) buffer[i] = '\0' ; // 清空buffer
cin >> buffer ;
// ToDo:逐字檢查,不合法顯示input後exit,合法轉int存入vector<int> a
cin >> buffer ;
// ToDo:逐字檢查,不合法顯示input後exit,合法轉int存入vector<int> b
cout << a << b ;
// Initialization Done ---------------------------------------------
if(n[0]==1) // "Display Only"
return 0 ;
else if(n[0]==2) // "+"
{
// ToDo
return 0 ;
}
else if(n[0]==3) // "-"
{
// ToDo
return 0 ;
}
else if(n[0]==4) // "*"
{
// ToDo
return 0 ;
}
else if(n[0]==5) // "/"
{
// ToDo
return 0 ;
}
// 乘法最簡單,加減相同作法,除法用減法
return 0;
}
Reference:
Reference Code: TA
#include <iostream>
#include <vector>
using namespace std;
int main()
{
char buffer[10001];
vector<int> a, b, c;
int n, a_sign = 1, b_sign = 1, c_sign = 1;
bool zero_start = true;
cout << "Big number calculator"
<< endl
<< "1) display only"
<< endl
<< "2) plus (+)"
<< endl
<< "3) minus (-)"
<< endl
<< "4) multiplication (*)"
<< endl
<< "5) division (/)"
<< endl
<< "Please select the operator: ";
cin >> buffer;
if (!isdigit(buffer[0]) || buffer[1] != '\0')
{
cout << "Invalid input: " << buffer << endl;
return 0;
}
n = buffer[0]-'0' ;
switch (n)
{
case 1:
cout << "You selected: display only" << endl;
break;
case 2:
cout << "You selected: +" << endl;
break;
case 3:
cout << "You selected: -" << endl;
break;
case 4:
cout << "You selected: *" << endl;
break;
case 5:
cout << "You selected: /" << endl;
break;
default:
cout << "Invalid input: " << buffer << endl;
return 0;
}
for(int i=0; buffer[i] != '\0'; i++) buffer[i] = '\0' ;
cout << "Input integer number (a): ";
cin >> buffer;
zero_start = true;
for (int i = 0; buffer[i] != '\0'; i++)
{
if (!isdigit(buffer[i]))
{
if (i == 0 && buffer[i] == '-')
{
a_sign = -1;
continue;
}
else
{
cout << "Invalid input: " << buffer << endl;
return 0;
}
}
if (buffer[i] == '0' && zero_start)
continue;
else
{
a.push_back((int)(buffer[i] - '0'));
zero_start = false;
}
}
if (a.empty())
{
a.push_back(0);
a_sign = 0;
}
reverse(a.begin(), a.end());
for(int i=0; buffer[i] != '\0'; i++) buffer[i] = '\0' ;
cout << "Input integer number (b): ";
cin >> buffer;
zero_start = true;
for (int i = 0; buffer[i] != '\0'; i++)
{
if (!isdigit(buffer[i]))
{
if (i == 0 && buffer[i] == '-')
{
b_sign = -1;
continue;
}
else
{
cout << "Invalid input: " << buffer << endl;
return 0;
}
}
if (buffer[i] == '0' && zero_start)
continue;
else
{
b.push_back((int)(buffer[i] - '0'));
zero_start = false;
}
}
if (b.empty())
{
if (n == 5)
{
cout << "Invalid input: " << buffer << endl;
return 0;
}
b.push_back(0);
b_sign = 0;
}
reverse(b.begin(), b.end());
cout << "(a) = " << (a_sign == -1 ? "-" : "");
for (int i = a.size() - 1; i >= 0; i--)
cout << a[i];
cout << '\n'
<< "(b) = " << (b_sign == -1 ? "-" : "");
for (int i = b.size() - 1; i >= 0; i--)
cout << b[i];
cout << endl;
// Initialization Done
int carry = 0;
if (n == 1)
return 0; // "Display only"
// "Display only" Done
else if (n == 2) // "+"
{
for (int i = 0; i < a.size() && i < b.size(); i++)
{
c.push_back((a[i] * a_sign + b[i] * b_sign + carry) % 10);
carry = (a[i] * a_sign + b[i] * b_sign + carry) / 10;
}
if (a.size() > b.size())
{
for (int i = b.size(); i < a.size(); i++)
{
c.push_back((a[i] * a_sign + carry) % 10);
carry = (a[i] * a_sign + carry) / 10;
}
}
else
{
for (int i = a.size(); i < b.size(); i++)
{
c.push_back((b[i] * b_sign + carry) % 10);
carry = (b[i] * b_sign + carry) / 10;
}
}
c.push_back(carry);
zero_start = true;
for (int i = c.size() - 1; i >= 0; i--)
{
if (c[i] == 0 && zero_start)
c.pop_back();
else
break;
}
if (c.empty())
c.push_back(0);
c_sign = (c[c.size() - 1] < 0 ? -1 : 1);
for (int i = 0; i < c.size(); i++)
{
if (c_sign * c[i] < 0)
{
c[i] += (10 * c_sign);
if (i + 1 < c.size())
c[i + 1] += (-1 * c_sign);
else
c.push_back(-1 * c_sign);
}
c[i] *= c_sign;
}
cout << "(a) + (b) = " << (c_sign < 0 ? "-" : "");
for (int i = c.size() - 1; i >= 0; i--)
cout << c[i];
cout << endl;
return 0;
}
// "+" Done
else if (n == 3) // "-"
{
for (int i = 0; i < a.size() && i < b.size(); i++)
{
c.push_back((a[i] * a_sign - b[i] * b_sign + carry) % 10);
carry = (a[i] * a_sign - b[i] * b_sign + carry) / 10;
}
if (a.size() > b.size())
{
for (int i = b.size(); i < a.size(); i++)
{
c.push_back((a[i] * a_sign + carry) % 10);
carry = (a[i] * a_sign + carry) / 10;
}
}
else
{
for (int i = a.size(); i < b.size(); i++)
{
c.push_back((-b[i] * b_sign + carry) % 10);
carry = (-b[i] * b_sign + carry) / 10;
}
}
c.push_back(carry);
zero_start = true;
for (int i = c.size() - 1; i >= 0; i--)
{
if (c[i] == 0 && zero_start)
c.pop_back();
else
break;
}
if (c.empty())
c.push_back(0);
c_sign = (c[c.size() - 1] < 0 ? -1 : 1);
for (int i = 0; i < c.size(); i++)
{
if (c_sign * c[i] < 0)
{
c[i] += (10 * c_sign);
if (i + 1 < c.size())
c[i + 1] += (-1 * c_sign);
else
c.push_back(-1 * c_sign);
}
c[i] *= c_sign;
}
cout << "(a) - (b) = " << (c_sign < 0 ? "-" : "");
for (int i = c.size() - 1; i >= 0; i--)
cout << c[i];
cout << endl;
return 0;
}
// "-" Done
else if (n == 4) // "*"
{
c_sign = a_sign * b_sign;
for (int i = 0; i < b.size(); i++)
{
for (int j = 0; j < a.size(); j++)
{
if (i + j >= c.size())
c.push_back(a[j] * b[i]);
else
c[i + j] += a[j] * b[i];
}
}
for (int i = 0; i < c.size(); i++)
{
carry += c[i];
c[i] = carry % 10;
carry /= 10;
}
c.push_back(carry);
zero_start = true;
for (int i = c.size() - 1; i >= 0; i--)
{
if (c[i] == 0 && zero_start)
c.pop_back();
else
break;
}
if (c.empty())
c.push_back(0);
cout << "(a) * (b) = " << (c_sign < 0 ? "-" : "");
for (int i = c.size() - 1; i >= 0; i--)
cout << c[i];
cout << endl;
return 0;
}
// "*" Done
else if (n == 5) // "/"
{
c_sign = a_sign * b_sign;
c.push_back(0) ;
while (a.size() >= b.size())
{
for (int i = 0; i < b.size(); i++)
{
carry += a[i] - b[i] + 10 ;
a[i] = carry % 10 ;
carry = carry / 10 - 1 ;
}
for (int i = b.size(); i < a.size(); i++)
{
if( carry == 0 ) break ;
carry += a[i] + 10 ;
a[i] = carry % 10 ;
carry = carry / 10 - 1 ;
}
a.push_back(carry) ;
zero_start = true;
for (int i = a.size() - 1; i >= 0; i--)
{
if (a[i] == 0 && zero_start)
a.pop_back();
else
break;
}
if (a.empty())
a.push_back(0);
if ( a[a.size()-1] >= 0 ) c[0]++ ;
else break ;
}
cout << "(a) / (b) = " << (c_sign < 0 ? "-" : "") << c[0] << endl ;
return 0 ;
}
// "/" Done
return 0;
}
Lecture 8: Array & String (3)
std::string
std::string
是 C++ 提供的標準樣板函式庫 (Standard Template Library, STL)。
可以把這個想成更為方便使用的 charactor array。
支援動態調整大小及內建字串內 (in-place) 的調整。
Ref: std::string class in C++ - GeeksforGeeks
// C++ code to demonstrate the working of
// getline(), push_back() and pop_back()
#include<iostream>
#include<string> // for string class
using namespace std;
int main()
{
// Declaring string
string str;
// Taking string input using getline()
// "geeksforgeek" in giving output
getline(cin,str);
// Displaying string
cout << "The initial string is : ";
cout << str << endl;
// Using push_back() to insert a character
// at end
// pushes 's' in this case
str.push_back('s');
// Displaying string
cout << "The string after push_back operation is : ";
cout << str << endl;
// Using pop_back() to delete a character
// from end
// pops 's' in this case
str.pop_back();
// Displaying string
cout << "The string after pop_back operation is : ";
cout << str << endl;
return 0;
}
std::getline
& std::cin
Ref: std::getline - cppreference.com
getline
reads characters from an input stream and places them into a string:
- Same as
getline(input, str, input.widen('\n'))
, that is, the default delimiter is the endline character.
std::string
manipulations
Ref: std::string class in C++ - GeeksforGeeks
// C++ code to demonstrate the working of
// copy() and swap()
#include<iostream>
#include<string> // for string class
using namespace std;
int main()
{
// Initializing 1st string
string str1 = "geeksforgeeks is for geeks";
// Declaring 2nd string
string str2 = "geeksforgeeks rocks";
// Declaring character array
char ch[80];
// using copy() to copy elements into char array
// copies "geeksforgeeks"
str1.copy(ch,13,0);
// Displaying char array
cout << "The new copied character array is : ";
cout << ch << endl << endl;
// Displaying strings before swapping
cout << "The 1st string before swapping is : ";
cout << str1 << endl;
cout << "The 2nd string before swapping is : ";
cout << str2 << endl;
// using swap() to swap string content
str1.swap(str2);
// Displaying strings after swapping
cout << "The 1st string after swapping is : ";
cout << str1 << endl;
cout << "The 2nd string after swapping is : ";
cout << str2 << endl;
return 0;
}
std::stringstream
Ref: Converting Strings to Numbers in C/C++ - GeeksforGeeks
// A program to demonstrate the use of stringstream
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
string s = "12345";
// object from the class stringstream
stringstream geek(s);
// The object has the value 12345 and stream
// it to the integer x
int x = 0;
geek >> x;
// Now the variable x holds the value 12345
cout << "Value of x : " << x;
return 0;
}
example: a simple command line integer calculator
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int main()
{
string input_buffer;
vector<long> operands;
vector<string> operators;
cout << "Input a forumla: ";
std::getline(cin, input_buffer);
stringstream terms_extractor(input_buffer);
for (string term; (terms_extractor >> term);)
{
if (term == "+" || term == "-" || term == "*" || term == "/")
{
operators.push_back(term);
}
else
{
long operand = stol(term);
operands.push_back(operand);
}
}
cout << "Operands: ";
for (int i = 0; i < operands.size(); i++)
{
cout << operands[i] << " ";
}
cout << endl;
cout << "Operators: ";
for (int i = 0; i < operators.size(); i++)
{
cout << operators[i] << " ";
}
cout << endl;
cout << "Result: ";
for (int i = 0; i < operators.size(); i++)
{
if (operators[i] == "+")
{
cout << operands[i] + operands[i + 1] << " ";
operands[i + 1] = operands[i] + operands[i + 1];
}
else if (operators[i] == "-")
{
cout << operands[i] - operands[i + 1] << " ";
operands[i + 1] = operands[i] - operands[i + 1];
}
else if (operators[i] == "*")
{
cout << operands[i] * operands[i + 1] << " ";
operands[i + 1] = operands[i] * operands[i + 1];
}
else if (operators[i] == "/")
{
cout << operands[i] / operands[i + 1] << " ";
operands[i + 1] = operands[i] / operands[i + 1];
}
}
cout << endl;
return 0;
}
example:
$ ./a.out⏎
Input a forumla: 1 + 2 * 3⏎
Operands: 1 2 3
Operators: + *
Result: 3 9
Midterm: Simpilified Big Number Calculator (extra homework, 15% grade)
-
Inputs:
- The function to be calculated, listed below:
- Display two nubmers only (
display only
) (50%, with exceptions handled) - Plus (
+
) (30%) - Greatest Common Divider (
gcd
) (20%)
- Display two nubmers only (
- Two non-negative integers \( a, b \). Two numbers will not more than 50 digits.
- If the calculated function is greatest common divider, \( a, b \) should be natural numbers (自然數), otherwise, the two numbers should be integer.
- The function to be calculated, listed below:
-
Outputs:
- The calculated function. (i.e.
display only
,+
,gcd
) - Two nubmers. \( a, b \)
- The corrsponding calculation result.
- The calculated function. (i.e.
-
File name:
midterm_1_<student_id>.cpp
(e.g.midterm_1_106062802.cpp
) -
The program should inform the user to select the function and type the numbers, \( a, b \) to be calculated. Output the typed numbers and the calculation result of the selected function.
-
The program supports the calculation repeatly. User can select the function
0
to exit the program. -
The program should check user's input, output the error message and inform the user to input again if the input is invalid. If the input is invalid, the program should inform the user with
Invalid input: <the wrong input>
right after the input and inform the user to type the same input again. -
The format of the error message is shown in the Exception section.
-
The program should assume the user's input is any non-space character (i.e. not space, tab) string.
-
The program should be finished within 30 seconds. Any test cases will garuntee the program is finished within 30 seconds.
Format
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: <1-3>⏎
You selected: <display only,+,gcd>
Input integer number (a): <a>⏎
Input integer number (b): <b>⏎
(a) = <a>
(b) = <b>
(a) <+, gcd> (b) = <result>
Example
Display
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 1⏎
You selected: display only
Input integer number (a): 0000000000000⏎
Input integer number (b): 00000001⏎
(a) = 0
(b) = 1
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 1⏎
You selected: display only
Input integer number (a): 123456789012345678901234567890⏎
Input integer number (b): 987654321098765432109876543210⏎
(a) = 123456789012345678901234567890
(b) = 987654321098765432109876543210
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 1⏎
You selected: display only
Input integer number (a): 12345678901234567890123456789012345678901234567890⏎
Input integer number (b): 98765432109876543210987654321098765432109876543210⏎
(a) = 12345678901234567890123456789012345678901234567890
(b) = 98765432109876543210987654321098765432109876543210
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 0⏎
$
Plus
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 2⏎
You selected: +
Input integer number (a): 111⏎
Input integer number (b): 222⏎
(a) = 111
(b) = 222
(a) + (b) = 333
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 2⏎
You selected: +
Input integer number (a): 123⏎
Input integer number (b): 987⏎
(a) = 123
(b) = 987
(a) + (b) = 1110
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 2⏎
You selected: +
Input integer number (a): 123456789012345678901234567890⏎
Input integer number (b): 987654321098765432109876543210⏎
(a) = 123456789012345678901234567890
(b) = 987654321098765432109876543210
(a) + (b) = 1111111110111111111011111111100
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 2⏎
You selected: +
Input integer number (a): 12345678901234567890123456789012345678901234567890⏎
Input integer number (b): 98765432109876543210987654321098765432109876543210⏎
(a) = 12345678901234567890123456789012345678901234567890
(b) = 98765432109876543210987654321098765432109876543210
(a) + (b) = 111111111011111111101111111110111111111011111111100
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 0⏎
$
Greatest Common Divider
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 3⏎
You selected: gcd
Input integer number (a): 3⏎
Input integer number (b): 27⏎
(a) = 3
(b) = 27
(a) gcd (b) = 3
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 3⏎
You selected: gcd
Input integer number (a): 47⏎
Input integer number (b): 7⏎
(a) = 47
(b) = 7
(a) gcd (b) = 1
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 3⏎
You selected: gcd
Input integer number (a): 123492001107001353123492001107001353⏎
Input integer number (b): 99396000891001089099396000891001089⏎
(a) = 123492001107001353123492001107001353
(b) = 99396000891001089099396000891001089
(a) gcd (b) = 3012000027000033003012000027000033
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 3⏎
You selected: gcd
Input integer number (a): 7369130657357778596659⏎
Input integer number (b): 2305843009213693951⏎
(a) = 7369130657357778596659
(b) = 2305843009213693951
(a) gcd (b) = 1
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 3⏎
You selected: gcd
Input integer number (a): 2305843009213693951⏎
Input integer number (b): 7369130657357778596659⏎
(a) = 2305843009213693951
(b) = 7369130657357778596659
(a) gcd (b) = 1
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 3⏎
You selected: gcd
Input integer number (a): 12345678901234567890123456789012345678901234567890⏎
Input integer number (b): 1234567890123456789012345678901234567890123456789⏎
(a) = 12345678901234567890123456789012345678901234567890
(b) = 1234567890123456789012345678901234567890123456789
(a) gcd (b) = 1234567890123456789012345678901234567890123456789
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 3⏎
You selected: gcd
Input integer number (a): 987654321098765432109876543210⏎
Input integer number (b): 123456789012345678901234567890⏎
(a) = 987654321098765432109876543210
(b) = 123456789012345678901234567890
(a) gcd (b) = 9000000000900000000090
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 0⏎
$
Exceptions
$ ./a.out
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: +⏎
Invalid input: +
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 00000001⏎
Invalid input: 00000001
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: -2⏎
Invalid input: -2
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 4⏎
Invalid input: 4
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 1⏎
You selected: display only
Input integer number (a): +_)(*(&#$)(*@()*@!⏎
Invalid input: +_)(*(&#$)(*@()*@!
Input integer number (a): -0.0⏎
Invalid input: -0.0
Input integer number (a): 123⏎
Input integer number (b): 0.1⏎
Invalid input: 0.1
Input integer number (b): 0⏎
(a) = 123
(b) = 0
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 3⏎
You selected: gcd
Input integer number (a): 0⏎
Invalid input: 0
Input integer number (a): 987654321098765432109876543210⏎
Input integer number (b): 0⏎
Invalid input: 0
Input integer number (b): 987654321098765432109876543210⏎
(a) = 987654321098765432109876543210
(b) = 987654321098765432109876543210
(a) gcd (b) = 987654321098765432109876543210
Big number calculator
1) display only
2) plus (+)
3) greatest common divider (gcd)
0) exit
Please select the operator: 0⏎
$
Pseudo code
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <cctype>
using namespace std;
int main()
{
vector<int> num_a, num_b;
string input_buffer;
int select_function;
while (true)
{
// select function
cout << "Big number calculator" << endl
<< "1) display only" << endl
<< "2) plus (+)" << endl
<< "3) greatest common divider (gcd)" << endl
<< "0) exit" << endl
<< "Please select the operator: ";
cin >> input_buffer;
// start of check selected function is valid
// end of check selected function is valid
// start of inform selected function
// end of inform selected function
// clear buffer
input_buffer.clear();
// input number (a)
// check if input is valid
// if not valid, inform user and continue to input
// convert string to int
// remove leading zeros
// check if input is valid for gcd
// clear buffer
input_buffer.clear();
// input number (b)
// check if input is valid
// if not valid, inform user and continue to input
// convert string to int
// remove leading zeros
// check if input is valid for gcd
// clear buffer
input_buffer.clear();
// print number (a) (b)
// calculation
// plus
// gcd
// compaire a and b
// if a == b, break
// if a > b
// a = a - b
// if a < b
// b = b - a
// remove leading zeros
// if a == 0, b is the result
// else if b == 0, a is the result
// clean up (a) and (b)
}
}
C++ References
- unzip
html-book-20201016.zip
. - open
html-book-20201016/reference/en/index.html
. - Find what you want.
Reference Code:
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <cctype>
using namespace std;
int main()
{
vector<int> num_a, num_b;
string input_buffer;
int select_function;
while (true)
{
// select function
cout << "Big number calculator" << endl
<< "1) display only" << endl
<< "2) plus (+)" << endl
<< "3) greatest common divider (gcd)" << endl
<< "0) exit" << endl
<< "Please select the operator: ";
cin >> input_buffer;
// start of check selected function is valid
if (input_buffer == "0")
{
select_function = 0;
}
else if (input_buffer == "1")
{
select_function = 1;
}
else if (input_buffer == "2")
{
select_function = 2;
}
else if (input_buffer == "3")
{
select_function = 3;
}
else
{
cout << "Invalid input: " << input_buffer << endl
<< endl;
continue;
}
// end of check selected function is valid
// start of inform selected function
if (select_function == 0)
{
cout << endl;
break;
}
else if (select_function == 1)
{
cout << "You selected: display only" << endl;
}
else if (select_function == 2)
{
cout << "You selected: +" << endl;
}
else if (select_function == 3)
{
cout << "You selected: gcd" << endl;
}
// end of inform selected function
// clear buffer
input_buffer.clear();
// input number (a)
while (true)
{
cout << "Input integer number (a): ";
cin >> input_buffer;
bool valid = true;
// check if input is valid
for (int i = 0; i < input_buffer.size(); i++)
{
if (!isdigit(input_buffer[i]))
{
valid = false;
break;
}
}
if (!valid)
{
cout << "Invalid input: " << input_buffer << endl
<< endl;
continue;
}
// convert string to int
for (int i = 0; i < input_buffer.size(); i++)
{
num_a.push_back(input_buffer[i] - '0');
}
// remove leading zeros
while (num_a.size() > 1 && num_a[0] == 0)
{
num_a.erase(num_a.begin());
}
reverse(num_a.begin(), num_a.end());
// check if input is valid for gcd
if (select_function == 3 && num_a.size() == 1 && num_a[0] == 0)
{
cout << "Invalid input: " << input_buffer << endl
<< endl;
num_a.clear();
continue;
}
break;
}
// clear buffer
input_buffer.clear();
// input number (b)
while (true)
{
cout << "Input integer number (b): ";
cin >> input_buffer;
bool valid = true;
// check if input is valid
for (int i = 0; i < input_buffer.size(); i++)
{
if (!isdigit(input_buffer[i]))
{
valid = false;
break;
}
}
if (!valid)
{
cout << "Invalid input: " << input_buffer << endl
<< endl;
continue;
}
// convert string to int
for (int i = 0; i < input_buffer.size(); i++)
{
num_b.push_back(input_buffer[i] - '0');
}
// remove leading zeros
while (num_b.size() > 1 && num_b[0] == 0)
{
num_b.erase(num_b.begin());
}
reverse(num_b.begin(), num_b.end());
// check if input is valid for gcd
if (select_function == 3 && num_b.size() == 1 && num_b[0] == 0)
{
cout << "Invalid input: " << input_buffer << endl
<< endl;
num_b.clear();
continue;
}
break;
}
// clear buffer
input_buffer.clear();
// print number (a)
cout << "(a) = ";
for (int i = num_a.size() - 1; i >= 0; i--)
{
cout << num_a[i];
}
cout << endl;
// print number (b)
cout << "(b) = ";
for (int i = num_b.size() - 1; i >= 0; i--)
{
cout << num_b[i];
}
cout << endl;
// calculation
if (select_function == 2)
{
// plus
vector<int> result;
int carry = 0;
for (int i = 0; i < max(num_a.size(), num_b.size()); i++)
{
int sum = carry;
if (i < num_a.size())
{
sum += num_a[i];
}
if (i < num_b.size())
{
sum += num_b[i];
}
result.push_back(sum % 10);
carry = sum / 10;
}
if (carry != 0)
{
result.push_back(carry);
}
cout << "(a) + (b) = ";
for (int i = result.size() - 1; i >= 0; i--)
{
cout << result[i];
}
cout << endl;
}
else if (select_function == 3)
{
// gcd
while (true)
{
// compaire a and b
int comp = 0;
if (num_a.size() > num_b.size())
{
comp = 1;
}
else if (num_a.size() < num_b.size())
{
comp = -1;
}
else
{
for (int i = num_a.size() - 1; i >= 0; i--)
{
if (num_a[i] > num_b[i])
{
comp = 1;
break;
}
else if (num_a[i] < num_b[i])
{
comp = -1;
break;
}
}
}
// if a == b, break
if (comp == 0)
{
break;
}
// if a > b
else if (comp == 1)
{
// a = a - b
int carry = 0;
for (int i = 0; i < num_a.size(); i++)
{
int sub = num_a[i] - carry;
if (i < num_b.size())
{
sub -= num_b[i];
}
if (sub < 0)
{
sub += 10;
carry = 1;
}
else
{
carry = 0;
}
num_a[i] = sub;
}
}
// if a < b
else
{
// b = b - a
int carry = 0;
for (int i = 0; i < num_b.size(); i++)
{
int sub = num_b[i] - carry;
if (i < num_a.size())
{
sub -= num_a[i];
}
if (sub < 0)
{
sub += 10;
carry = 1;
}
else
{
carry = 0;
}
num_b[i] = sub;
}
}
// remove leading zeros
for (int i = num_a.size() - 1; i >= 0; i--)
{
if (num_a[i] != 0)
{
break;
}
num_a.pop_back();
}
for (int i = num_b.size() - 1; i >= 0; i--)
{
if (num_b[i] != 0)
{
break;
}
num_b.pop_back();
}
// if a == 0, b is the result
if (num_a.size() == 1 && num_a[0] == 0)
{
num_a.swap(num_b);
break;
}
// else if b == 0, a is the result
else if (num_b.size() == 1 && num_b[0] == 0)
{
break;
}
}
cout << "(a) gcd (b) = ";
for (int i = num_a.size() - 1; i >= 0; i--)
{
cout << num_a[i];
}
cout << endl;
}
// clean up (a) and (b)
num_a.clear();
num_b.clear();
cout << endl;
}
}
Lecture 10: Function & Reference (1)
Why Functions?
寫 function 好處
- 可以把一些重複的程式碼抽出來,放在 function 中,並且可以在不同的地方呼叫這些 function。
- 可以把常用的程式碼統整並放在獨立的 source file 中,方便管理。並可以將 function 先行編譯,降低整體編譯時間。
- 可以獨立測試 function,將問題獨立出來。
One and the only: main
function
main
的定義
int main () { body }
int main (int argc, char *argv[]) { body }
The main function is called at program startup after initialization of the non-local objects with static storage duration. It is the designated entry point to a program that is executed in hosted environment (that is, with an operating system). The entry points to freestanding programs (boot loaders, OS kernels, etc) are implementation-defined.
Example:
#include <iostream>
#include <sstream>
#include <cstdlib> // EXIT_SUCCESS
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0;
std::cout << "The program have " << argc << " arguments." << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "argv[" << i << "] = " << argv[i] << std::endl;
}
std::stringstream char_array_to_int;
char_array_to_int << argv[1] << ' ' << argv[2];
char_array_to_int >> a;
char_array_to_int >> b;
c = a + b;
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a + b = " << c << std::endl;
return EXIT_SUCCESS;
}
$ ./a.out 1 2 3⏎
The program have 4 arguments.
argv[0] = ./a.out
argv[1] = 1
argv[2] = 2
argv[3] = 3
a: 1
b: 2
a + b = 3
Function Definition
<type> <name>(<parameter list>)
{
<body>
}
程式 compile 是由上至下,第一個符合的 function 就會被執行。 重複定義的 function 會是錯誤的。
Exception: 同名字但不同參數的 function 會是不同的 function (Function Overloading)。
Example:
#include <iostream>
#include <sstream>
#include <cstdlib> // EXIT_SUCCESS
int add(int a, int b)
{
return a + b;
}
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0;
std::cout << "The program have " << argc << " arguments." << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "argv[" << i << "] = " << argv[i] << std::endl;
}
std::stringstream char_array_to_int;
char_array_to_int << argv[1] << ' ' << argv[2];
char_array_to_int >> a;
char_array_to_int >> b;
c = add(a, b);
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a + b = " << c << std::endl;
return EXIT_SUCCESS;
}
Function Prototype
若要將 function 放在其他位置或是在其他 source file 中,需要寫 function prototype。
#include <iostream>
#include <sstream>
#include <cstdlib> // EXIT_SUCCESS
int add(int a, int b); // function prototype
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0;
std::cout << "The program have " << argc << " arguments." << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "argv[" << i << "] = " << argv[i] << std::endl;
}
std::stringstream char_array_to_int;
char_array_to_int << argv[1] << ' ' << argv[2];
char_array_to_int >> a;
char_array_to_int >> b;
c = add(a, b); // function call
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a + b = " << c << std::endl;
return EXIT_SUCCESS;
}
int add(int a, int b) // function definition
{
return a + b;
}
Function Call
如何呼叫 function?
僅需要使用的時候直接寫該 function 的名字,並將要傳入的相對應的變數放到參數 ()
內。
Example:
add(a, b); // function call, order is important
Note: 放參數的順序和數量一定要和 function 定義的順序一樣,數量不同會 comiple 錯誤, 順序不同意思不同,而且可能形成 compiler 無法揪錯,直到執行階段才有機會被發掘的重大錯誤。
Wrong example:
#include <iostream>
#include <sstream>
#include <cstdlib> // EXIT_SUCCESS
int sub(int a, int b); // function prototype
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0;
std::cout << "The program have " << argc << " arguments." << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "argv[" << i << "] = " << argv[i] << std::endl;
}
std::stringstream char_array_to_int;
char_array_to_int << argv[1] << ' ' << argv[2];
char_array_to_int >> a;
char_array_to_int >> b;
c = sub(a, b); // function call
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a - b = " << c << std::endl;
c = sub(b, a); // function call, wrong example
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a - b = " << c << std::endl;
return EXIT_SUCCESS;
}
int sub(int a, int b) // function definition
{
return a - b;
}
Function Parameter
Function 參數可以是任何型態,並且有以下三種方式來傳遞變數:
Call by Value
Call by Value 參數傳遞的方式是:傳遞變數的值,而非變數本身。 傳遞時會多複製一份變數的值。
Example:
#include <iostream>
#include <sstream>
#include <cstdlib> // EXIT_SUCCESS
int sub(int a, int b); // function prototype
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0;
std::cout << "The program have " << argc << " arguments." << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "argv[" << i << "] = " << argv[i] << std::endl;
}
std::stringstream char_array_to_int;
char_array_to_int << argv[1] << ' ' << argv[2];
char_array_to_int >> a;
char_array_to_int >> b;
c = sub(a, b); // function call
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a - b = " << c << std::endl;
return EXIT_SUCCESS;
}
int sub(int a, int b) // function definition
{
a = a - b;
return a;
}
Call by Reference
Call by Reference 參數傳遞的方式是:傳遞變數本身,可減少傳遞時候所需要的記憶體。
Example:
#include <iostream>
#include <sstream>
#include <cstdlib> // EXIT_SUCCESS
void sub(int &c, int &a, int &b); // call by reference
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0;
std::cout << "The program have " << argc << " arguments." << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "argv[" << i << "] = " << argv[i] << std::endl;
}
std::stringstream char_array_to_int;
char_array_to_int << argv[1] << ' ' << argv[2];
char_array_to_int >> a;
char_array_to_int >> b;
sub(c, a, b); // function call
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a - b = " << c << std::endl;
return EXIT_SUCCESS;
}
void sub(int &c, int &a, int &b) // call by reference, a and b can be changed
{
a = a - b; // a in main function is changed
c = a;
return; // return value is not used if return type is void
}
const
Reference
若需要使用 call by reference 參數,且參數值不能被改變,則可以使用 const
reference 參數。
Example:
#include <iostream>
#include <sstream>
#include <cstdlib> // EXIT_SUCCESS
void sub(int &c, const int &a, const int &b); // call by reference
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0;
std::cout << "The program have " << argc << " arguments." << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "argv[" << i << "] = " << argv[i] << std::endl;
}
std::stringstream char_array_to_int;
char_array_to_int << argv[1] << ' ' << argv[2];
char_array_to_int >> a;
char_array_to_int >> b;
sub(c, a, b); // function call
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a - b = " << c << std::endl;
return EXIT_SUCCESS;
}
void sub(int &c, const int &a, const int &b)
// call by reference, a and b cannot be changed
{
a = a - b; // compile error, a cannot be changed
c = a;
//c = a - b;
return; // return value is not used if return type is void
}
Call by Pointer (Optional, Old Style)
Call by Pointer 參數傳遞的方式是:傳遞變數的記憶體位置,與 Call by Reference 相同,但處理起來較為複雜。
Note: 看看就好
Example:
#include <iostream>
#include <sstream>
#include <cstdlib> // EXIT_SUCCESS
void sub(int *c, const int *a, const int *b); // call by pointer
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0;
std::cout << "The program have " << argc << " arguments." << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "argv[" << i << "] = " << argv[i] << std::endl;
}
std::stringstream char_array_to_int;
char_array_to_int << argv[1] << ' ' << argv[2];
char_array_to_int >> a;
char_array_to_int >> b;
sub(&c, &a, &b); // function call
// use &a to get the address of a
std::cout << "a: " << a << " @ " << &a << std::endl
<< "b: " << b << " @ " << &b << std::endl
<< "a - b = " << c << " @ " << &c << std::endl;
return EXIT_SUCCESS;
}
void sub(int *c, const int *a, const int *b)
// call by pointer, a and b cannot be changed, c can be changed
{
std::cout << "a: " << *a << " @ " << a << std::endl
<< "b: " << *b << " @ " << b << std::endl
<< "c: " << *c << " @ " << c << std::endl;
*a = *a - *b; // compile error, a cannot be changed
*c = *a;
//*c = *a - *b;
return; // return value is not used if return type is void
}
Function Return
Function return 傳回值的方式是:傳回變數本身,任何型態皆可,但只能回傳一個值。
若不需回傳值,則可以使用 void
型態,並且不需要寫 return
或是 return;
即可。
若要回傳多個變數,則會需要使用 call by reference。
#include <iostream>
#include <sstream>
#include <cstdlib> // EXIT_SUCCESS
int sub(int &c_complement, const int &a, const int &b); // call by reference
int main(int argc, char *argv[])
{
int a = 0, b = 0, c = 0, c_complement = 0;
std::cout << "The program have " << argc << " arguments." << std::endl;
for (int i = 0; i < argc; i++)
{
std::cout << "argv[" << i << "] = " << argv[i] << std::endl;
}
std::stringstream char_array_to_int;
char_array_to_int << argv[1] << ' ' << argv[2];
char_array_to_int >> a;
char_array_to_int >> b;
c = sub(c, a, b); // function call
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl
<< "a - b = " << c << std::endl;
return EXIT_SUCCESS;
}
int sub(int &c_complement, const int &a, const int &b)
// use call by reference to return multiple values
{
c_complement = b - a;
return a - b;
}
Function example
#include <iostream>
#include <cctype> // isdigit()
#include <algorithm> // reverse()
#include <vector> // vector<int>()
using namespace std;
vector<int> parse_big_number(const char input_buffer[])
{
vector<int> big_number;
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return vector<int>();
}
else
{
int digit = input_buffer[i] - '0';
big_number.push_back(digit);
}
}
reverse(big_number.begin(), big_number.end());
// remove leading zeros
for (int i = big_number.size() - 1; i >= 1; i--)
{
if (big_number[i] == 0)
{
big_number.pop_back();
}
else
{
break;
}
}
return big_number;
}
void print_big_number(const vector<int> &big_number)
{
for (int i = big_number.size() - 1; i >= 0; i--)
{
cout << big_number[i];
}
cout << endl;
}
void clean_up_input_buffer(char input_buffer[])
{
for (int i = 0; input_buffer[i] != '\0'; i++)
{
input_buffer[i] = '\0';
}
}
int main()
{
char input_buffer[10000 + 1]; // + 1 for the last '\0'
vector<int> a, b;
cout << "Input a non-negative integer number (a): ";
cin >> input_buffer;
a = parse_big_number(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "(a) = ";
print_big_number(a);
cout << "Input a non-negative integer number (b): ";
cin >> input_buffer;
b = parse_big_number(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "(b) = ";
print_big_number(b);
return 0;
}
Lecture 11: Function & Reference (2)
How to create a function from scratch
以下會介紹如何從頭將原本重複的程式碼轉換成函式,並且把原本的程式碼改寫。
確認重複使用的程式碼片段
#include <iostream>
#include <cctype> // isdigit()
#include <algorithm> // reverse()
#include <vector> // vector<int>()
using namespace std;
int main()
{
char input_buffer[10000 + 1]; // + 1 for the last '\0'
vector<int> a, b;
cout << "Input a non-negative integer number (a): ";
cin >> input_buffer;
// begin repeated codes, parse the input (a)
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return 0;
}
else
{
int digit = input_buffer[i] - '0';
a.push_back(digit);
}
}
reverse(a.begin(), a.end());
// remove leading zeros
for (int i = a.size() - 1; i >= 1; i--)
{
if (a[i] == 0)
{
a.pop_back();
}
else
{
break;
}
}
// end repeated codes, parse the input (a)
// begin repeated codes, clean up the input buffer
for (int i = 0; i < (10000 + 1); i++)
{
input_buffer[i] = '\0';
}
// end repeated codes, clean up the input buffer
cout << "(a) = ";
// begin repeated codes, print the input (a)
for (int i = a.size() - 1; i >= 0; i--)
{
cout << a[i];
}
cout << endl;
// end repeated codes, print the input (a)
cout << "Input a non-negative integer number (b): ";
cin >> input_buffer;
// begin repeated codes, parse the input (b)
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return 0;
}
else
{
int digit = input_buffer[i] - '0';
b.push_back(digit);
}
}
reverse(b.begin(), b.end());
// remove leading zeros
for (int i = b.size() - 1; i >= 1; i--)
{
if (b[i] == 0)
{
b.pop_back();
}
else
{
break;
}
}
// end repeated codes, parse the input (b)
// begin repeated codes, clean up the input buffer
for (int i = 0; i < (10000 + 1); i++)
{
input_buffer[i] = '\0';
}
// end repeated codes, clean up the input buffer
cout << "(b) = ";
// begin repeated codes, print the input (b)
for (int i = b.size() - 1; i >= 0; i--)
{
cout << b[i];
}
cout << endl;
// end repeated codes, print the input (b)
}
建立 function 的輸入參數及輸出
parse the input
// begin repeated codes, parse the input (a)
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return 0;
}
else
{
int digit = input_buffer[i] - '0';
a.push_back(digit);
}
}
reverse(a.begin(), a.end());
// remove leading zeros
for (int i = a.size() - 1; i >= 1; i--)
{
if (a[i] == 0)
{
a.pop_back();
}
else
{
break;
}
}
// end repeated codes, parse the input (a)
- Input:
input_buffer
- Output:
a
clean up the input buffer
// begin repeated codes, clean up the input buffer
for (int i = 0; i < (10000 + 1); i++)
{
input_buffer[i] = '\0';
}
// end repeated codes, clean up the input buffer
- Input:
input_buffer
- Output: none
print the input
// begin repeated codes, print the input (a)
for (int i = a.size() - 1; i >= 0; i--)
{
cout << a[i];
}
cout << endl;
// end repeated codes, print the input (a)
- Input:
a
- Output: none
將使用的變數替換成 function 的參數或 function 內的變數
parse the input
vector<int> parse_big_number(const char input_buffer[])
{
vector<int> big_number;
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return vector<int>();
}
else
{
int digit = input_buffer[i] - '0';
big_number.push_back(digit);
}
}
reverse(big_number.begin(), big_number.end());
// remove leading zeros
for (int i = big_number.size() - 1; i >= 1; i--)
{
if (big_number[i] == 0)
{
big_number.pop_back();
}
else
{
break;
}
}
return big_number;
}
- Input:
input_buffer
->const char input_buffer[]
- Output:
a
->vector<int> big_number
- Return:
big_number
clean up the input buffer
void clean_up_input_buffer(char input_buffer[])
{
for (int i = 0; input_buffer[i] != '\0'; i++)
{
input_buffer[i] = '\0';
}
}
- Input:
input_buffer
->char input_buffer[]
- Output: none
print the input
void print_big_number(const vector<int> &big_number)
{
for (int i = big_number.size() - 1; i >= 0; i--)
{
cout << big_number[i];
}
cout << endl;
}
- Input:
a
->const vector<int> &big_number
- Output: none
將原本的程式碼片段改成使用 function
#include <iostream>
#include <cctype> // isdigit()
#include <algorithm> // reverse()
#include <vector> // vector<int>()
using namespace std;
int main()
{
char input_buffer[10000 + 1]; // + 1 for the last '\0'
vector<int> a, b;
cout << "Input a non-negative integer number (a): ";
cin >> input_buffer;
// begin repeated codes, parse the input (a)
/*
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return 0;
}
else
{
int digit = input_buffer[i] - '0';
a.push_back(digit);
}
}
reverse(a.begin(), a.end());
// remove leading zeros
for (int i = a.size() - 1; i >= 1; i--)
{
if (a[i] == 0)
{
a.pop_back();
}
else
{
break;
}
}
*/
// end repeated codes, parse the input (a)
a = parse_big_number(input_buffer);
// begin repeated codes, clean up the input buffer
/*
for (int i = 0; i < (10000 + 1); i++)
{
input_buffer[i] = '\0';
}
*/
// end repeated codes, clean up the input buffer
clean_up_input_buffer(input_buffer);
cout << "(a) = ";
// begin repeated codes, print the input (a)
/*
for (int i = a.size() - 1; i >= 0; i--)
{
cout << a[i];
}
cout << endl;
*/
// end repeated codes, print the input (a)
print_big_number(a);
cout << "Input a non-negative integer number (b): ";
cin >> input_buffer;
// begin repeated codes, parse the input (b)
/*
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return 0;
}
else
{
int digit = input_buffer[i] - '0';
b.push_back(digit);
}
}
reverse(b.begin(), b.end());
// remove leading zeros
for (int i = b.size() - 1; i >= 1; i--)
{
if (b[i] == 0)
{
b.pop_back();
}
else
{
break;
}
}
*/
// end repeated codes, parse the input (b)
b = parse_big_number(input_buffer);
// begin repeated codes, clean up the input buffer
/*
for (int i = 0; i < (10000 + 1); i++)
{
input_buffer[i] = '\0';
}
*/
// end repeated codes, clean up the input buffer
clean_up_input_buffer(input_buffer);
cout << "(b) = ";
// begin repeated codes, print the input (b)
/*
for (int i = b.size() - 1; i >= 0; i--)
{
cout << b[i];
}
cout << endl;
*/
// end repeated codes, print the input (b)
print_big_number(b);
return 0;
}
Result
請參考 Lecture 10: Function & Reference (1) - Function example
Multiple C++ source files & Prebuilt functions
在 function 較多的情況下,我們可以將 function 分開到不同的 source file 中,這樣可以減少 compile time,並且可以減少程式碼的複雜度。
常見的方法是以功能為單位,並且每個功能都有自己的 source file (or header file),例如:
big_number.h
#include <iostream>
#include <cctype> // isdigit()
#include <algorithm> // reverse()
#include <vector> // vector<int>()
using namespace std;
vector<int> parse_big_number(const char input_buffer[])
{
vector<int> big_number;
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return vector<int>();
}
else
{
int digit = input_buffer[i] - '0';
big_number.push_back(digit);
}
}
reverse(big_number.begin(), big_number.end());
// remove leading zeros
for (int i = big_number.size() - 1; i >= 1; i--)
{
if (big_number[i] == 0)
{
big_number.pop_back();
}
else
{
break;
}
}
return big_number;
}
void print_big_number(const vector<int> &big_number)
{
for (int i = big_number.size() - 1; i >= 0; i--)
{
cout << big_number[i];
}
cout << endl;
}
input_buffer.h
void clean_up_input_buffer(char input_buffer[])
{
for (int i = 0; input_buffer[i] != '\0'; i++)
{
input_buffer[i] = '\0';
}
}
main.cpp
#include <iostream>
#include <vector> // vector<int>()
#include "big_number.h"
#include "input_buffer.h"
using namespace std;
int main()
{
char input_buffer[10000 + 1]; // + 1 for the last '\0'
vector<int> a, b;
cout << "Input a non-negative integer number (a): ";
cin >> input_buffer;
a = parse_big_number(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "(a) = ";
print_big_number(a);
cout << "Input a non-negative integer number (b): ";
cin >> input_buffer;
b = parse_big_number(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "(b) = ";
print_big_number(b);
return 0;
}
這個例子中就是將多個 function 分配到不同的 header file 中,並且在 main.cpp 中引用這些 header file 來使用這些 function。 這種方法的可以方便的減少程式碼的複雜度,但是並不能減少 compile time。
Multiple C++ source files and a header file
若需要減少 compile time,可以將 function 分開到不同的 source file 中,並且依據 source file 建立各自的 header file,並在該 header file 中宣告 function prototype。 最後在 main.cpp 中引用這些 header file 來使用這些 function。
Example:
big_number.h
#ifndef BIG_NUMBER_H
#define BIG_NUMBER_H
#include <vector> // vector<int>()
using namespace std;
vector<int> parse_big_number(const char input_buffer[]);
void print_big_number(const vector<int> &big_number);
#endif
big_number.cpp
#include <iostream>
#include <cctype> // isdigit()
#include <algorithm> // reverse()
#include <vector> // vector<int>()
#include "big_number.h"
using namespace std;
vector<int> parse_big_number(const char input_buffer[])
{
vector<int> big_number;
for (int i = 0; input_buffer[i] != '\0'; i++)
{
if (!isdigit(input_buffer[i]))
{
cout << "Invalid input: " << input_buffer << endl;
return vector<int>();
}
else
{
int digit = input_buffer[i] - '0';
big_number.push_back(digit);
}
}
reverse(big_number.begin(), big_number.end());
// remove leading zeros
for (int i = big_number.size() - 1; i >= 1; i--)
{
if (big_number[i] == 0)
{
big_number.pop_back();
}
else
{
break;
}
}
return big_number;
}
void print_big_number(const vector<int> &big_number)
{
for (int i = big_number.size() - 1; i >= 0; i--)
{
cout << big_number[i];
}
cout << endl;
}
input_buffer.h
#ifndef INPUT_BUFFER_H
#define INPUT_BUFFER_H
void clean_up_input_buffer(char input_buffer[]);
#endif
input_buffer.cpp
#include "input_buffer.h"
void clean_up_input_buffer(char input_buffer[])
{
for (int i = 0; input_buffer[i] != '\0'; i++)
{
input_buffer[i] = '\0';
}
}
main.cpp
#include <iostream>
#include <vector> // vector<int>()
#include "big_number.h"
#include "input_buffer.h"
using namespace std;
int main()
{
char input_buffer[10000 + 1]; // + 1 for the last '\0'
vector<int> a, b;
cout << "Input a non-negative integer number (a): ";
cin >> input_buffer;
a = parse_big_number(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "(a) = ";
print_big_number(a);
cout << "Input a non-negative integer number (b): ";
cin >> input_buffer;
b = parse_big_number(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "(b) = ";
print_big_number(b);
return 0;
}
Compile Multiple C++ source files and link them together
如何將多個 c++ source 檔案 compile 並且 link 一起,並且可以避免 compile time?
- 將所有 c++ source 檔案 compile 成
.o
檔案
$ g++ -c big_number.cpp input_buffer.cpp⏎
$ ls⏎
big_number.cpp big_number.h big_number.o input_buffer.cpp
input_buffer.h input_buffer.o main.cpp test.cpp
- compile
main.cpp
時,將所有.o
檔案 link 起來
$ g++ main.cpp big_number.o input_buffer.o⏎
$ ls⏎
a.out big_number.h input_buffer.cpp input_buffer.o test.cpp
big_number.cpp big_number.o input_buffer.h main.cpp
$ ./a.out⏎
Input a non-negative integer number (a): 12345⏎
(a) = 12345
Input a non-negative integer number (b): 54321⏎
(b) = 54321
$
- 若僅修改
main.cpp
時,只要再 compilemain.cpp
就可以了
# modify main.cpp
$ g++ main.cpp big_number.o input_buffer.o⏎
$ ./a.out⏎
Input a non-negative integer number (a): 12345⏎
(a) = 12345
Input a non-negative integer number (b): 54321⏎
(b) = 54321
$
- 若僅修改
big_number.cpp
或input_buffer.cpp
時,需要再 compile 相對應的.cpp
,並且再次 compilemain.cpp
就可以了
# modify big_number.cpp
$ g++ -c big_number.cpp⏎
$ g++ main.cpp big_number.o input_buffer.o⏎
$ ./a.out⏎
Input a non-negative integer number (a): 12345⏎
(a) = 12345
Input a non-negative integer number (b): 54321⏎
(b) = 54321
$
Function Overloading
如果有多個 function 名稱相同,但是參數不同,這時候就會有 function overloading。 此時同名的 function 彼此視為不同的 function,而且可以有不同的程式碼。
Example:
void print_big_number(const vector<int> &big_number)
{
for (int i = big_number.size() - 1; i >= 0; i--)
{
cout << big_number[i];
}
cout << endl;
}
void print_big_number(const vector<int> &big_number, int base)
{
if(base > 10)
{
cout << "Base must be less than or equal to 10." << endl;
return;
}
else if(base < 2)
{
cout << "Base must be greater than or equal to 2." << endl;
return;
}
else if(base == 10)
{
print_big_number(big_number);
return;
}
else
{
vector<int> big_number_in_base(big_number); // copy big_number
for (int i = 0; i < big_number_in_base.size(); i++)
{
if(big_number_in_base[i] >= base)
{
if(i == big_number_in_base.size() - 1)
{
big_number_in_base.push_back(big_number_in_base[i] / base);
}
else
{
big_number_in_base[i + 1] += big_number_in_base[i] / base;
}
big_number_in_base[i] = big_number_in_base[i] % base;
}
}
print_big_number(big_number_in_base);
}
}
Variable Scope & Lifetime
Reference: Scope of Variables in C++
Local Variable
Wrong example: use others' local variable
// CPP program to illustrate
// usage of local variables
#include <iostream>
using namespace std;
void func()
{
// this variable is local to the
// function func() and cannot be
// accessed outside this function
int age = 18;
}
int main()
{
cout << "Age is: " << age;
return 0;
}
Correct example: use owned local variable
// CPP program to illustrate
// usage of local variables
#include <iostream>
using namespace std;
void func()
{
// this variable is local to the
// function func() and cannot be
// accessed outside this function
int age = 18;
cout << age;
}
int main()
{
cout << "Age is: ";
func();
return 0;
}
Global Variable
// CPP program to illustrate
// usage of global variables
#include <iostream>
using namespace std;
// global variable
int global = 5;
// global variable accessed from
// within a function
void display()
{
cout << global << endl;
}
// main function
int main()
{
display();
// changing value of global
// variable from main function
global = 10;
display();
}
Variable Lifetime
// CPP program to illustrate
// scope of local variables
// and global variables together
#include <iostream>
using namespace std;
// global variable
int global = 5;
// main function
int main()
{
// local variable with same
// name as that of global variable
int global = 2;
cout << global << endl;
}
To access global variable with same name as that of local variable, you need to use ::
to access global variable.
// C++ program to show that we can access a global
// variable using scope resolution operator :: when
// there is a local variable with same name
#include <iostream>
using namespace std;
// Global x
int x = 0;
int main()
{
// Local x
int x = 10;
cout << "Value of global x is " << ::x;
cout << "\nValue of local x is " << x;
return 0;
}
Namespace
如果有多個 function 同名,需要使用 namespace 才能避免名稱衝突。
#include <iostream>
namespace a
{
void print_char()
{
std::cout << "a" << std::endl;
}
}
namespace b
{
void print_char()
{
std::cout << "b" << std::endl;
}
}
int main()
{
a::print_char();
b::print_char();
return 0;
}
Lecture 12: Function & Reference (3)
Function call stack
Function 會有呼叫的 stack,讓我們可以知道哪些 function 被呼叫了,以及呼叫的順序。
Call Stack 的狀況:
- 從下而上,每個 function 呼叫都會加入一個 call stack,並且把自己的名稱加入。
- 越上方的 function 呼叫越新被呼叫,越下方的 function 呼叫越舊被呼叫。
- 每次執行的時候,皆是執行最上方的 function。
- 可透過 return address 找到呼叫的 function,並將結果回傳給呼叫的 function。
Function Caller & Callee
Example:
#include <iostream>
void print_operand(int operand) // callee
{
std::cout << "Operand: " << operand << std::endl;
}
void print_result(int result) // callee
{
std::cout << "Result: " << result << std::endl;
}
int add(int a, int b) // callee & caller
{
print_operand(a);
print_operand(b);
int c = a + b;
print_result(c);
return c;
}
int main(int argc, char *argv[]) // caller
{
int a = 1;
int b = 2;
int c = add(a, b);
return 0;
}
Recursion
Recursion 是一種遞迴的程式碼,可以讓我們 function 自己呼叫 function 自己。
Example: Fibonacci sequence
#include <iostream>
int fibonacci(int n) // caller & callee
{
if (n == 0)
{
return 0;
}
else if (n == 1)
{
return 1;
}
else
{
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
int main(int argc, char *argv[]) // caller
{
int n = 10;
std::cout << "Fibonacci sequence of " << n << ": ";
for (int i = 0; i < n; i++)
{
std::cout << fibonacci(i) << " ";
}
std::cout << std::endl;
return 0;
}
Expanded version: i = 3
#include <iostream>
int fibonacci_0(void)
{
return 0;
}
int fibonacci_1(void)
{
return 1;
}
int fibonacci_2(void)
{
return fibonacci_0() + fibonacci_1();
}
int fibonacci_3(void)
{
return fibonacci_1() + fibonacci_2();
}
int main(int argc, char *argv[]) // caller
{
int n = 3;
std::cout << "Fibonacci sequence of " << n << ": ";
std::cout << fibonacci_1() << " ";
std::cout << fibonacci_2() << " ";
std::cout << fibonacci_3() << " ";
std::cout << std::endl;
return 0;
}
Infix notation & Calculation
一般的數學式皆是 infix notation,也就是從左到右的計算,並且運算子是在兩運算元的中間。
Example: \(1 + 2 * 3 = 9\) (沒有先乘後加的 infix notation)
進行運算的時候,會先運算左手邊運算子,再運算右手邊運算子。如: $$1 + 2 * 3 = (1 + 2) * 3 = (3) * 3 = 9$$
我們可以由左至右依序運算就可以得到答案,但也可以從右到左,使用遞迴的方式來運算。
Method:
expression = expression op expression | number
number = [0-9]+
op = '+' | '-' | '*' | '/'
Example:
$$ \begin{align} 1 + 2 * 3 &= (1 + 2) * 3 \\ &= ((1) + 2) * 3 \\ &= (1 + 2) * 3 \\ &= (3) * 3 \\ &= 3 * 3 \\ &= 9 \end{align}$$
Postfix notation
另一種數學運算式表達法式 postfix notation,也是從左到右的計算,但運算子是在兩運算元或運算式的右邊。
Example: 1 2 + 3 * = 9
進行運算的時候,從左至右依序讀取,碰到運算子時會將最近讀取或是運算結束的運算元拿來進行運算,並將運算結果儲存回去。如:
1 2 + 3 * = (1) 2 + 3 *
= (1 2) + 3 *
= (1 2 +) 3 *
= (3) 3 *
= (3 3) *
= (3 3 *)
= (9)
當然這也可以從右到左,使用遞迴的方式來運算,但是會有尋找運算子的兩運算元的問題。
Find postfix operands using recursion
因為 postfix notation 是從右到左的使用該運算子的兩運算元,所以我們可以用遞迴的方式來找到運算元。
Method:
- postfix 運算
- 使用傳入的最後一個 token 的位置,並記錄下來。
- 跳過 1. 記錄的最後一個運算子,從右往左尋找
- 如果是運算元,則回傳目前的運算元作為運算結果。
- 如果是運算子,則從目前位置開始視為新的 postfix notation,重複步驟 1 到 2,並紀錄回傳的結果為運算元 1。
- 透過更新的上次運算最後位置,從該位置繼續視為新的 postfix notation,重複步驟 1 到 2,並紀錄回傳的結果為運算元 2。
- 更新最後位置為運算元 2 的最後位置。
- 回傳運算元 1 & 2 的運算結果。
Example:
1 2 3 4 + + * = 1 2 3 4 + + (*)
= (1 2 3 4 + +) *) // find operand1
= (1 2 3 4 + (+)) *) // find operand1
= ((1 2 3 4 +) +)) *) // find operand1, find operand1
= ((1 2 3 4 (+)) +)) *) // find operand1, find operand1
= ((1 2 3) (4 +)) +)) *) // find operand1, find operand1, found operand1
= ((1 2 (3) (4 +)) +)) *) // find operand1, find operand1, found operand2
= ((1 2) (7 +)) *) // find operand1, find operand1, result
= ((1 (2) (7 +)) *) // find operand1, find operand2
= ((1) (9 *) // find operand1, result
= ((1) (9 *) // find operand2
= 9 // result
Further Reading: Recursion and ProblemSolving
// This algorithm modifies its argument in the process of evaluating it.
float evaluate_prefix(string &prefix_str)
{
char ch = prefix_str[first]; // get character at position first
// Delete first character from prefix_str;
prefix_str = prefix_str.substr(first + 1);
if (is_identifier(ch))
{
return value of the identifier;
}
// if the character is an operator, then
else if (is_operator(ch))
{
op = ch;
operand1 = evaluate_prefix(prefix_str);
operand2 = evaluate_prefix(prefix_str);
return operand1 op operand2;
}
}
Lab 12: 整數指令列四則計算機
Lab 12-1: 整數指令列四則計算機顯示 (40%)
- 輸入:包含整數 (符合
long
型態) 的四則計算運算式- 用空白字元分開整數數字及運算子,如:
1 + 2 * 3
、1 2 3 4 * + -
。
- 用空白字元分開整數數字及運算子,如:
- 輸出:顯示使用者輸入的整數數字及運算子。
- 數字:
1 2 3
、1 2 3 4
。 - 運算子:
+ *
、* + -
。
- 數字:
- 檔名:lab12_1_<學號>.cpp (e.g. lab12_1_106062802.cpp)
Notice:
- 程式需提示使用者輸入運算式,程式需分析後顯示使用者輸入的整數數字及運算子。
- 程式需檢查數字跟運算子的數量是否相符,若不相符則顯示錯誤訊息
Invalid expression.
並結束程式。- Example:使用者輸入 4 個數字,但運算子只有 2 個,並非 3 個,則顯示錯誤訊息
Invalid expression.
並結束程式。
- Example:使用者輸入 4 個數字,但運算子只有 2 個,並非 3 個,則顯示錯誤訊息
- 程式需檢查運算子是否為 '+'、'-'、'*'、'/',若不相符則顯示錯誤訊息
Invalid expression.
並結束程式。 - 程式需使用 function 來處理整數指令列四則計算機的輸入與輸出。
- 程式需在 30 秒之內執行完畢,所有測資皆不會超過 30 秒的執行時間。
Format
Please input the expression: <expression, space seprated>⏎
Operands: <numbers, space seprated>
Operators: <operators, space seprated>
Example
$ ./a.out⏎
Please input the expression: 1 2 3 4 * + -⏎
Operands: 1 2 3 4
Operators: * + -
$ ./a.out⏎
Please input the expression: -1 -2 -3 -4 * + -⏎
Operands: -1 -2 -3 -4
Operators: * + -
$ ./a.out⏎
Please input the expression: -1 -2 * -3 + -4 -⏎
Operands: -1 -2 -3 -4
Operators: * + -
$ ./a.out⏎
Please input the expression: -1 -2 -3 -4 * +⏎
Invalid expression.
$ ./a.out⏎
Please input the expression: -1 -2 -3 * + -⏎
Invalid expression.
$ ./a.out⏎
Please input the expression: -1 -2 -3 -4 * + %⏎
Invalid expression.
Pseudo Code
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_ops(tokens);
print_nums(tokens);
return EXIT_SUCCESS;
}
Reference:
Reference Code:
2-passes parsing: Credit: 賴杰弘 (110021118)
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
void parse_expression(const string &expression, vector<string> &check);
void parse_expression(
const vector<string> &check,
vector<long> &operands,
vector<string> &operators);
bool check_expression(const vector<string> &check);
void print_ops(const vector<long> &operands);
void print_nums(const vector<string> &operators);
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> check;
vector<long> operands;
vector<string> operators;
cout << "Please input the expression: ";
getline(cin, input_buffer);
parse_expression(input_buffer, check);
if (!check_expression(check))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
parse_expression(check, operands, operators);
if ((operands.size() - 1) != operators.size())
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_ops(operands);
print_nums(operators);
return EXIT_SUCCESS;
}
void parse_expression(const string &expression, vector<string> &check)
{
stringstream terms_extractor(expression);
for (string term; (terms_extractor >> term);)
{
check.push_back(term);
}
}
void parse_expression(
const vector<string> &check,
vector<long> &operands,
vector<string> &operators)
{
for (int i = 0; i < check.size(); i++)
{
if (check[i] == "+"
|| check[i] == "-"
|| check[i] == "*"
|| check[i] == "/")
{
operators.push_back(check[i]);
}
else
{
long operand = stol(check[i]);
operands.push_back(operand);
}
}
}
bool check_expression(const vector<string> &check)
{
if ((check.size() % 2) == 0)
{
return false;
}
for (int i = 0; i < check.size(); i++)
{
string s = check[i];
if (s.size() == 1)
{
if (s[0] != '+'
&& s[0] != '-'
&& s[0] != '*'
&& s[0] != '/')
{
if (!isdigit(s[0]))
{
return false;
}
}
}
else
{
if (s[0] != '-')
{
if (!isdigit(s[0]))
{
return false;
}
}
for (int i = 1; i < check[i].size(); i++)
{
if (!isdigit(s[i]))
{
return false;
}
}
}
}
return true;
}
void print_ops(const vector<long> &operands)
{
cout << "Operands: ";
for (int i = 0; i < operands.size(); i++)
{
cout << operands[i] << " ";
}
cout << endl;
}
void print_nums(const vector<string> &operators)
{
cout << "Operators: ";
for (int i = 0; i < operators.size(); i++)
{
cout << operators[i] << " ";
}
cout << endl;
}
by character parsing: Credit: 吳咨萱 (110021132)
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens)
{
int n = 0;
string stringtokens;
while (n <= expression.size() - 1)
{
if (expression[n] != ' ')
{
do
{
stringtokens.push_back(expression[n]);
n = n + 1;
} while (expression[n] != ' ' && n <= expression.size() - 1);
tokens.push_back(stringtokens);
stringtokens.clear();
}
else
{
n = n + 1;
}
}
}
bool check_expression(const vector<string> &tokens)
{
int operandsnum = 0;
int operatorsnum = 0;
for (int i = 0; i <= tokens.size() - 1; i++)
{
int j = 0;
if (tokens[i][j] == '-' && tokens[i].size() != 1)
{
for (int j = 1; j <= tokens[i].size() - 1; j++)
{
if (!isdigit(tokens[i][j]))
{
return false;
}
}
operandsnum = operandsnum + 1;
}
else if (tokens[i][j] == '+'
|| tokens[i][j] == '-'
|| tokens[i][j] == '*'
|| tokens[i][j] == '/')
{
if (tokens[i].size() != 1)
{
return false;
}
else
{
operatorsnum = operatorsnum + 1;
}
}
else
{
for (int j = 0; j <= tokens[i].size() - 1; j++)
{
if (!isdigit(tokens[i][j]))
{
return false;
}
}
operandsnum = operandsnum + 1;
}
}
if (operandsnum != operatorsnum + 1)
{
return false;
}
else
{
return true;
}
}
void print_ops(const vector<string> &tokens)
{
std::cout << "Operators: ";
for (int i = 0; i <= tokens.size() - 1; i++)
{
if (tokens[i] == "+"
|| tokens[i] == "-"
|| tokens[i] == "*"
|| tokens[i] == "/")
{
std::cout << tokens[i];
if (i != tokens.size() - 1)
{
std::cout << " ";
}
}
}
}
void print_nums(const vector<string> &tokens)
{
std::cout << "Operands: ";
for (int i = 0; i <= tokens.size() - 1; i++)
{
if (tokens[i] != "+"
&& tokens[i] != "-"
&& tokens[i] != "*"
&& tokens[i] != "/")
{
std::cout << tokens[i];
if (i != tokens.size() - 1)
{
std::cout << " ";
}
}
}
std::cout << "\n";
}
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_nums(tokens);
print_ops(tokens);
return EXIT_SUCCESS;
}
function of function: Credit: 陶威綸 (110021121)
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
bool is_number(string num);
bool is_oper(string token);
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_nums(tokens);
print_ops(tokens);
return EXIT_SUCCESS;
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators: ";
for (int i = 0; i < tokens.size(); i++)
{
if (is_oper(tokens[i]))
cout << tokens[i] << " ";
}
cout << endl;
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands: ";
for (int i = 0; i < tokens.size(); i++)
{
if (is_number(tokens[i]))
cout << tokens[i] << " ";
}
cout << endl;
}
void parse_expression(const string &expression, vector<string> &tokens)
{
stringstream buffer(expression);
for (string tmp; (buffer >> tmp);)
{
tokens.push_back(tmp);
}
}
bool is_number(string num)
{
int suc_case = 0;
if (num[0] == '-' && num.size() != 1)
suc_case = 1;
for (int i = 0; i < num.size(); i++)
{
for (int tmp = 0; tmp < 10; tmp++)
{
if (num[i] - '0' == tmp)
{
suc_case += 1;
}
}
}
return suc_case == num.size();
}
bool is_oper(string token)
{
if (token.size() != 1)
return false;
string valid = "+-*/";
for (int i = 0; i < valid.size(); i++)
{
if (token[0] == valid[i])
return true;
}
return false;
}
bool check_expression(const vector<string> &tokens)
{
int numC = 0, operC = 0;
for (int i = 0; i < tokens.size(); i++)
{
if (is_number(tokens[i]))
numC += 1;
else if (is_oper(tokens[i]))
operC += 1;
else
return false;
}
return (numC - operC == 1);
}
try ... catch ...
: Credit: 陳柏志 (107021128)
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_nums(tokens);
print_ops(tokens);
return EXIT_SUCCESS;
}
void parse_expression(string const &expression, vector<string> &tokens)
{
stringstream tokens_extractor(expression);
for (string new_token; (tokens_extractor >> new_token);)
{
tokens.push_back(new_token);
}
}
bool check_expression(const vector<string> &tokens)
{
int op_cnt = 0, num_cnt = 0;
for (int i = 0; i < tokens.size(); ++i)
{
if (tokens[i] == "+"
|| tokens[i] == "-"
|| tokens[i] == "*"
|| tokens[i] == "/")
{
++op_cnt;
}
else
{
try
{
stol(tokens[i]);
++num_cnt;
}
catch (const std::exception &e)
{
return false;
}
}
}
return num_cnt == op_cnt + 1;
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators:";
for (int i = 0; i < tokens.size(); ++i)
{
if (tokens[i] == "+"
|| tokens[i] == "-"
|| tokens[i] == "*"
|| tokens[i] == "/")
{
cout << " " << tokens[i];
}
}
cout << endl;
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands:";
for (int i = 0; i < tokens.size(); ++i)
{
try
{
long number = stol(tokens[i]);
cout << " " << number;
}
catch (const std::exception &e)
{
}
}
cout << endl;
}
TA version:
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_nums(tokens);
print_ops(tokens);
return EXIT_SUCCESS;
}
void parse_expression(const string &expression, vector<string> &tokens)
{
string temp;
for (int i = 0; i < expression.size(); i++)
{
if (expression[i] == ' ')
{
tokens.push_back(temp);
temp.clear();
}
else if (i == expression.size() - 1) // End of Input_Buffer
{
temp += expression.substr(i, 1);
tokens.push_back(temp);
}
else
temp += expression.substr(i, 1);
}
}
bool check_expression(const vector<string> &tokens)
{
int oper_counts = 0, num_counts = 0;
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i].length() == 1)
{
if (isdigit(tokens[i][0]))
{
num_counts++;
continue;
}
else
{
if (tokens[i][0] == '+'
|| tokens[i][0] == '-'
|| tokens[i][0] == '*'
|| tokens[i][0] == '/')
{
oper_counts++;
continue;
}
else
return false;
}
}
for (int j = 0; j < tokens[i].length(); j++)
{
if (!isdigit(tokens[i][j]))
{
if (j == 0 && tokens[i][j] == '-')
// Check if Negative Sign
continue;
return false;
}
}
num_counts++;
}
if (num_counts != oper_counts + 1)
return false;
return true;
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands:";
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i].length() != 1 || isdigit(tokens[i][0]))
cout << ' ' << tokens[i];
}
cout << endl;
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators:";
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i].length() == 1 && !isdigit(tokens[i][0]))
cout << ' ' << tokens[i];
}
cout << endl;
}
Lab 12-2: Infix 整數指令列四則計算機 (40%)
- 輸入:包含整數 (符合
long
型態) 的四則計算運算式- 用空白字元分開整數數字及運算子,如:
-1 + -2 * -3
、1 * 2 + 3 - 4
。
- 用空白字元分開整數數字及運算子,如:
- 輸出:顯示使用者輸入的整數數字及運算子、以及運算結果。
- 數字:
-1 -2 -3
、1 2 3 4
。 - 運算子:
+ *
、* + -
。 - 計算結果:
-3 9
、2 5 1
。
- 數字:
- 檔名:lab12_2_<學號>.cpp (e.g. lab12_2_106062802.cpp)
Notice:
- 程式需提示使用者輸入運算式,程式需分析後顯示使用者輸入的整數數字及運算子。
- 程式需檢查數字跟運算子的數量是否相符,若不相符則顯示錯誤訊息
Invalid expression.
並結束程式。- Example:使用者輸入 4 個數字,但運算子只有 2 個,並非 3 個,則顯示錯誤訊息
Invalid expression.
並結束程式。
- Example:使用者輸入 4 個數字,但運算子只有 2 個,並非 3 個,則顯示錯誤訊息
- 程式需檢查運算子是否為 '+'、'-'、'*'、'/',若不相符則顯示錯誤訊息
Invalid expression.
並結束程式。 - 程式需檢查運算式是否為 Infix notation,若不相符則顯示錯誤訊息
Invalid expression.
並結束程式。 - 程式需依據 infix 的規則計算運算式,不考慮先乘除後加減,並將每步運算子的計算中間結果輸出。
- Example:使用者輸入
-1 + -2 * -3
,程式將計算結果為-3 9
。
- Example:使用者輸入
- 程式需使用 function 來處理整數指令列四則計算機的輸入、計算與輸出。
- 程式需在 30 秒之內執行完畢,所有測資皆不會超過 30 秒的執行時間。
Format
Please input the expression: <expression, space seprated>⏎
Operands: <numbers, space seprated>
Operators: <operators, space seprated>
Result: <result>
Example
$ ./a.out⏎
Please input the expression: 1 * 2 + 3 - 4⏎
Operands: 1 2 3 4
Operators: * + -
Result: 2 5 1
$ ./a.out⏎
Please input the expression: -1 * -2 + -3 - -4⏎
Operands: -1 -2 -3 -4
Operators: * + -
Result: 2 -1 3
$ ./a.out⏎
Please input the expression: -1 + -2 * -3 - -4⏎
Operands: -1 -2 -3 -4
Operators: + * -
Result: -3 9 13
$ ./a.out⏎
Please input the expression: -1 * -2 + -3 -4⏎
Invalid expression.
$ ./a.out⏎
Please input the expression: -1 * -2 + -3 -⏎
Invalid expression.
$ ./a.out⏎
Please input the expression: -1 * -2 + -3 % -4⏎
Invalid expression.
$ ./a.out⏎
Please input the expression: 1 2 3 4 * + -⏎
Invalid expression.
Pseudo Code
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
string calculate_infix_expression(const vector<string> &tokens);
// hint: use std::to_string(long) to convert long to string
// see https://en.cppreference.com/w/cpp/string/basic_string/to_string
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_ops(tokens);
print_nums(tokens);
cout << "Result: ";
calculate_infix_expression(tokens);
cout << endl;
return EXIT_SUCCESS;
}
Reference:
Reference Code:
Iterative:
Credit: 陳柏志 (107021128)
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
string calculate_infix_expression(const vector<string> &tokens);
// hint: use std::to_string(long) to convert long to string
// see https://en.cppreference.com/w/cpp/string/basic_string/to_string
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_nums(tokens);
print_ops(tokens);
cout << "Result:";
calculate_infix_expression(tokens);
cout << endl;
return EXIT_SUCCESS;
}
void parse_expression(string const &expression, vector<string> &tokens)
{
stringstream tokens_extractor(expression);
for (string new_token; (tokens_extractor >> new_token);)
{
tokens.push_back(new_token);
}
}
bool check_expression(const vector<string> &tokens)
{
int op_cnt = 0, num_cnt = 0;
for (int i = 0; i < tokens.size(); ++i)
{
if (i % 2 == 1)
{
if (tokens[i] == "+"
|| tokens[i] == "-"
|| tokens[i] == "*"
|| tokens[i] == "/")
++op_cnt;
else
return false;
}
else
{
try
{
stol(tokens[i]);
++num_cnt;
}
catch (const std::exception &e)
{
return false;
}
}
}
return num_cnt == op_cnt + 1;
}
string calculate_infix_expression(const vector<string> &tokens)
{
if (tokens.size() == 0)
return "";
if (tokens.size() == 1)
{
cout << " " << tokens[0];
return tokens[0];
}
long ans = stol(tokens[0]);
for (int i = 1; i + 1 < tokens.size(); i += 2)
{
if (tokens[i] == "+")
ans += stol(tokens[i + 1]);
else if (tokens[i] == "-")
ans -= stol(tokens[i + 1]);
else if (tokens[i] == "*")
ans *= stol(tokens[i + 1]);
else if (tokens[i] == "/" && stol(tokens[i + 1]) != 0)
ans /= stol(tokens[i + 1]);
else
cerr << endl
<< "ERROR: divided by zero" << endl;
cout << " " << ans;
}
return to_string(ans);
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators:";
for (int i = 0; i < tokens.size(); ++i)
{
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
cout << " " << tokens[i];
}
}
cout << endl;
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands:";
for (int i = 0; i < tokens.size(); ++i)
{
try
{
long number = stol(tokens[i]);
cout << " " << number;
}
catch (const std::exception &e)
{
}
}
cout << endl;
}
Recursive:
Credit: 陳光齊 (110021102)
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <math.h>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
bool string_isdigit(string s);
vector<string> calculate_infix_expression(vector<string> tokens);
void answer(const vector<string> &tokens);
long string_to_long(string s);
int main()
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return 0;
}
print_nums(tokens);
print_ops(tokens);
cout << "Result:";
if (tokens.size() == 1)
{
cout << " " << tokens[0];
}
else
{
tokens = calculate_infix_expression(tokens);
}
return 0;
}
void parse_expression(const string &expression, vector<string> &tokens)
{
stringstream terms_extractor(expression);
for (string term; (terms_extractor >> term);)
{
tokens.push_back(term);
}
}
bool string_isdigit(string s)
{
if (s[0] == '-')
{
if (s.size() == 1)
{
return false;
}
for (int i = 1; i < s.size(); i++)
{
if (!isdigit(s[i]))
{
return false;
}
}
return true;
}
else
{
for (int i = 0; i < s.size(); i++)
{
if (!isdigit(s[i]))
{
return false;
}
}
return true;
}
}
bool check_expression(const vector<string> &tokens)
{
int opcount = 0;
int numcount = 0;
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i] == "+" or tokens[i] == "-"
or tokens[i] == "*" or tokens[i] == "/")
{
opcount += 1;
}
else if (string_isdigit(tokens[i]))
{
numcount += 1;
}
else
{
return 0;
}
}
if (numcount == opcount + 1)
{
for (int i = 1; i < tokens.size() - 1; i += 2)
{
if (!(tokens[i] == "+" or tokens[i] == "-"
or tokens[i] == "*" or tokens[i] == "/"))
{
return 0;
}
}
return 1;
}
else
{
return 0;
}
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators:";
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i] == "+" or tokens[i] == "-"
or tokens[i] == "*" or tokens[i] == "/")
{
cout << " " << tokens[i];
}
}
cout << endl;
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands:";
for (int i = 0; i < tokens.size(); i++)
{
if (string_isdigit(tokens[i]))
{
cout << " " << tokens[i];
}
}
cout << endl;
}
vector<string> calculate_infix_expression(vector<string> tokens)
{
long a = 0;
vector<string> vs;
if (tokens.size() == 3)
{
if (tokens[1] == "+")
{
a = string_to_long(tokens[0]) + string_to_long(tokens[2]);
cout << " " << a;
string str = to_string(a);
vs.push_back(str);
return vs;
}
else if (tokens[1] == "-")
{
a = string_to_long(tokens[0]) - string_to_long(tokens[2]);
cout << " " << a;
string str = to_string(a);
vs.push_back(str);
return vs;
}
else if (tokens[1] == "*")
{
a = string_to_long(tokens[0]) * string_to_long(tokens[2]);
cout << " " << a;
string str = to_string(a);
vs.push_back(str);
return vs;
}
else if (tokens[1] == "/")
{
a = string_to_long(tokens[0]) / string_to_long(tokens[2]);
cout << " " << a;
string s = to_string(a);
vs.push_back(s);
return vs;
}
}
else
{
if (tokens[tokens.size() - 2] == "+")
{
long temp = 0;
temp = string_to_long(tokens[tokens.size() - 1]);
tokens.pop_back();
tokens.pop_back();
a = string_to_long((calculate_infix_expression(tokens))[0]) + temp;
cout << " " << a;
string str = to_string(a);
vs.push_back(str);
return vs;
}
else if (tokens[tokens.size() - 2] == "-")
{
long temp = 0;
temp = string_to_long(tokens[tokens.size() - 1]);
tokens.pop_back();
tokens.pop_back();
a = string_to_long((calculate_infix_expression(tokens))[0]) - temp;
cout << " " << a;
string str = to_string(a);
vs.push_back(str);
return vs;
}
else if (tokens[tokens.size() - 2] == "*")
{
long temp = 0;
temp = string_to_long(tokens[tokens.size() - 1]);
tokens.pop_back();
tokens.pop_back();
a = string_to_long((calculate_infix_expression(tokens))[0]) * temp;
cout << " " << a;
string str = to_string(a);
vs.push_back(str);
return vs;
}
else if (tokens[tokens.size() - 2] == "/")
{
long temp = 0;
temp = string_to_long(tokens[tokens.size() - 1]);
tokens.pop_back();
tokens.pop_back();
a = string_to_long((calculate_infix_expression(tokens))[0]) / temp;
cout << " " << a;
string str = to_string(a);
vs.push_back(str);
return vs;
}
}
}
long string_to_long(string s)
{
long a = 0;
if (s[0] == '-')
{
for (int i = 1; i < s.size(); i++)
{
a *= 10;
a += (s[i] - '0');
}
a *= -1;
return a;
}
else
{
for (int i = s.size() - 1; i >= 0; i--)
{
a += (s[i] - '0') * pow(10, s.size() - 1 - i);
}
return a;
}
}
Credit: 林暐晉 (110021134)
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <cctype>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
string calculate_infix_expression(const vector<string> &tokens);
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
int main()
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_nums(tokens);
print_ops(tokens);
cout << "Result:";
if (tokens.size() == 1)
{
cout << tokens[0];
}
calculate_infix_expression(tokens);
cout << endl;
return EXIT_SUCCESS;
}
void parse_expression(const string &expression, vector<string> &tokens)
{
stringstream stream_ex(expression);
for (string s; stream_ex >> s;)
{
tokens.push_back(s);
}
}
bool check_expression(const vector<string> &tokens)
{
unsigned long long num = 0, op = 0;
bool bool_before = 0, bool_after = 0;
for (int i = 0; i < tokens.size(); i++)
{
bool n = 0;
for (int j = 0; j < tokens[i].size(); j++)
{
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
op += 1;
bool_after = 0;
break;
}
else if (tokens[i][j] == '-')
{
if (j != 0)
{
return 0;
}
}
else if (isdigit(tokens[i][j]))
{
n = 1;
}
else
{
return 0;
}
}
if (n == 1)
{
num += 1;
bool_after = 1;
}
if (bool_after == bool_before)
{
return 0;
}
bool_before = bool_after;
}
if (num - op != 1)
{
return 0;
}
return 1;
}
string calculate_infix_expression(const vector<string> &tokens)
{
if (tokens.size() == 1)
{
return tokens[0];
}
vector<string> left(tokens.begin(), tokens.end() - 2),
right(tokens.end() - 2, tokens.end());
string ans;
if (right[0] == "+")
{
ans = to_string(stol(calculate_infix_expression(left)) + stol(right[1]));
cout << " " << ans;
return ans;
}
else if (right[0] == "-")
{
ans = to_string(stol(calculate_infix_expression(left)) - stol(right[1]));
cout << " " << ans;
return ans;
}
else if (right[0] == "*")
{
ans = to_string(stol(calculate_infix_expression(left)) * stol(right[1]));
cout << " " << ans;
return ans;
}
else if (right[0] == "/")
{
ans = to_string(stol(calculate_infix_expression(left)) / stol(right[1]));
cout << " " << ans;
return ans;
}
else
{
return "qwertyuiop";
}
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands:";
for (int i = 0; i < tokens.size(); i++)
{
bool n = 0;
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
n = 1;
}
if (n == 0)
{
cout << " " << tokens[i];
}
}
cout << endl;
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators:";
for (int i = 0; i < tokens.size(); i++)
{
bool n = 0;
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
n = 1;
}
if (n == 1)
{
cout << " " << tokens[i];
}
}
cout << endl;
}
TA version:
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
void calculate_infix_expression(const vector<string> &tokens);
// hint: use std::to_string(long) to convert long to string
// see https://en.cppreference.com/w/cpp/string/basic_string/to_string
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_nums(tokens);
print_ops(tokens);
cout << "Result: ";
calculate_infix_expression(tokens);
cout << endl;
return EXIT_SUCCESS;
}
bool is_number(string str)
{
for (int i = 0; i < str.length(); i++)
{
if (i == 0 && str[i] == '-' && str.length() > 1)
continue;
if (!isdigit(str[i]))
return false;
}
return true;
}
void parse_expression(const string &expression, vector<string> &tokens)
{
string temp;
for (int i = 0; i < expression.size(); i++)
{
if (expression[i] == ' ')
{
tokens.push_back(temp);
temp.clear();
}
else if (i == expression.size() - 1) // End of Input_Buffer
{
temp += expression.substr(i, 1);
tokens.push_back(temp);
}
else
temp += expression.substr(i, 1);
}
}
bool check_expression(const vector<string> &tokens)
{
int oper_counts = 0, num_counts = 0;
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
oper_counts++;
else if (is_number(tokens[i]))
num_counts++;
else
return false;
if (num_counts != oper_counts + (i + 1) % 2)
return false;
}
if (!is_number(tokens[tokens.size() - 1]))
return false;
return true;
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands:";
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i].length() != 1 || isdigit(tokens[i][0]))
cout << ' ' << tokens[i];
}
cout << endl;
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators:";
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i].length() == 1 && !isdigit(tokens[i][0]))
cout << ' ' << tokens[i];
}
cout << endl;
}
void calculate_infix_expression(const vector<string> &tokens)
{
if (tokens.size() == 1)
{
cout << tokens[0];
return;
}
long a, b;
for (int i = 0; i < tokens.size() - 2; i += 2)
{
if (i == 0)
a = stol(tokens[i]);
b = stol(tokens[i + 2]);
if (tokens[i + 1] == "+")
a += b;
else if (tokens[i + 1] == "-")
a -= b;
else if (tokens[i + 1] == "*")
a *= b;
else if (tokens[i + 1] == "/")
a /= b;
cout << a << ' ';
}
return;
}
Lab 12-3: Postfix 整數指令列四則計算機 (20%)
- 輸入:包含整數 (符合
long
型態) 的四則計算運算式- 用空白字元分開整數數字及運算子,如:
-1 -2 -3 + *
、1 2 3 4 * + -
。
- 用空白字元分開整數數字及運算子,如:
- 輸出:顯示使用者輸入的整數數字及運算子、以及運算結果。
- 數字:
-1 -2 -3
、1 2 3 4
。 - 運算子:
+ *
、* + -
。 - 計算結果:
-5 5
、12 14 -13
。
- 數字:
- 檔名:lab12_3_<學號>.cpp (e.g. lab12_3_106062802.cpp)
Notice:
- 程式需提示使用者輸入運算式,程式需分析後顯示使用者輸入的整數數字及運算子。
- 程式需檢查數字跟運算子的數量是否相符,若不相符則顯示錯誤訊息
Invalid expression.
並結束程式。- Example:使用者輸入 4 個數字,但運算子只有 2 個,並非 3 個,則顯示錯誤訊息
Invalid expression.
並結束程式。
- Example:使用者輸入 4 個數字,但運算子只有 2 個,並非 3 個,則顯示錯誤訊息
- 程式需檢查運算子是否為 '+'、'-'、'*'、'/',若不相符則顯示錯誤訊息
Invalid expression.
並結束程式。 - 程式需檢查運算式是否為 Postfix notation,若不相符則顯示錯誤訊息
Invalid expression.
並結束程式。 - 程式需依據 postfix 的規則計算運算式,並將每步運算子的計算中間結果輸出。
- Example:使用者輸入
1 2 3 4 * + -
,程式將計算結果為12 14 -13
。
- Example:使用者輸入
- 程式需使用 function 來處理整數指令列四則計算機的輸入、計算與輸出。
- 程式需在 30 秒之內執行完畢,所有測資皆不會超過 30 秒的執行時間。
Format
Please input the expression: <expression, space seprated>⏎
Operands: <numbers, space seprated>
Operators: <operators, space seprated>
Result: <result>
Example
$ ./a.out⏎
Please input the expression: 1 2 3 4 * + -⏎
Operands: 1 2 3 4
Operators: * + -
Result: 12 14 -13
$ ./a.out⏎
Please input the expression: -1 -2 -3 -4 * + -⏎
Operands: -1 -2 -3 -4
Operators: * + -
Result: 12 10 -11
$ ./a.out⏎
Please input the expression: -1 -2 * -3 + -4 -⏎
Operands: -1 -2 -3 -4
Operators: * + -
Result: 2 -1 3
$ ./a.out⏎
Please input the expression: 1 2 - 2 1 + /⏎
Operands: 1 2 2 1
Operators: - + /
Result: -1 3 0
$ ./a.out⏎
Please input the expression: -1 -2 -3 -4 * +⏎
Invalid expression.
$ ./a.out⏎
Please input the expression: -1 -2 -3 * + -⏎
Invalid expression.
$ ./a.out⏎
Please input the expression: -1 -2 -3 -4 * + %⏎
Invalid expression.
$ ./a.out⏎
Please input the expression: 1 * 2 + 3 - 4⏎
Invalid expression.
Pseudo Code
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
string calculate_postfix_expression(vector<string> &tokens, int &last_pos);
// hint: use std::to_string(long) to convert long to string
// see https://en.cppreference.com/w/cpp/string/basic_string/to_string
// hint: use recursion to find the subexpressions of two operands
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_ops(tokens);
print_nums(tokens);
cout << "Result: ";
int last_pos = tokens.size() - 1;
calculate_postfix_expression(tokens, last_pos);
cout << endl;
return EXIT_SUCCESS;
}
Reference Code:
Iterative:
Credit: 陳柏志 (107021128)
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
string calculate_postfix_expression(vector<string> &tokens, int &last_pos);
// hint: use std::to_string(long) to convert long to string
// see https://en.cppreference.com/w/cpp/string/basic_string/to_string
// hint: use recursion to find the subexpressions of two operands
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_nums(tokens);
print_ops(tokens);
cout << "Result:";
int last_pos = tokens.size() - 1;
calculate_postfix_expression(tokens, last_pos);
cout << endl;
return EXIT_SUCCESS;
}
void parse_expression(string const &expression, vector<string> &tokens)
{
stringstream tokens_extractor(expression);
for (string new_token; (tokens_extractor >> new_token);)
{
tokens.push_back(new_token);
}
}
bool check_expression(const vector<string> &tokens)
{
int op_cnt = 0, num_cnt = 0;
int number_in_stack_cnt = 0;
for (int i = 0; i < tokens.size(); ++i)
{
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
++op_cnt;
if (--number_in_stack_cnt < 1)
return false;
}
else
{
try
{
stol(tokens[i]);
++num_cnt;
++number_in_stack_cnt;
}
catch (const std::exception &e)
{
return false;
}
}
}
return num_cnt == op_cnt + 1 && number_in_stack_cnt == 1;
}
string calculate_postfix_expression(vector<string> &tokens, int &last_pos)
{
if (tokens.size() == 1)
{
cout << " " << tokens[0];
return tokens[0];
}
vector<long> numbers;
for (int i = 0; i < tokens.size(); ++i)
{
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
long b = numbers.back();
numbers.pop_back();
long a = numbers.back();
numbers.pop_back();
if (tokens[i] == "+")
numbers.push_back(a + b);
else if (tokens[i] == "-")
numbers.push_back(a - b);
else if (tokens[i] == "*")
numbers.push_back(a * b);
else if (b != 0)
numbers.push_back(a / b);
else
{
numbers.push_back(a);
cerr << endl
<< "ERROR: divided by zero" << endl;
}
cout << " " << numbers.back();
}
else
{
numbers.push_back(stol(tokens[i]));
}
}
return to_string(numbers.back());
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators:";
for (int i = 0; i < tokens.size(); ++i)
{
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
cout << " " << tokens[i];
}
}
cout << endl;
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands:";
for (int i = 0; i < tokens.size(); ++i)
{
try
{
long number = stol(tokens[i]);
cout << " " << number;
}
catch (const std::exception &e)
{
}
}
cout << endl;
}
Recursive:
Credit: 林元鴻 (110021120)
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>
using namespace std;
void parse_expression(string &expression, vector<string> &tokens)
{
stringstream str(expression);
for (string term; (str >> term);)
{
tokens.push_back(term);
}
}
bool check_expression(const vector<string> &tokens, int &n)
{
int o = 0;
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
n++;
}
else
{
for (int j = 0; j < tokens[i].size(); j++)
{
if (j == 0 && (!isdigit(tokens[i][j]) && tokens[i][j] != '-'))
return false;
else if (!isdigit(tokens[i][j]) && j != 0)
return false;
}
o++;
}
}
return (o == n + 1);
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators: ";
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
cout << tokens[i] << ' ';
}
}
cout << endl;
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands: ";
for (int i = 0; i < tokens.size(); i++)
{
if (!(tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/"))
{
cout << stol(tokens[i]) << ' ';
}
}
cout << endl;
}
string calculate_postfix_expression(
vector<string> &tokens,
int &last_pos,
vector<string> &final_ans)
{
if (tokens.size() == 1)
{
return tokens[0];
}
int n = last_pos, o = last_pos;
long b, a;
string ans, re;
last_pos--;
n = last_pos;
if (last_pos < 0)
return "error";
if (tokens[last_pos] == "+" || tokens[last_pos] == "-"
|| tokens[last_pos] == "*" || tokens[last_pos] == "/")
{
re = calculate_postfix_expression(tokens, last_pos, final_ans);
if (re == "error")
return "error";
else
{
b = stol(re);
final_ans[n] = re;
}
}
else
b = stol(tokens[last_pos]);
last_pos--;
n = last_pos;
if (last_pos < 0)
return "error";
if (tokens[last_pos] == "+" || tokens[last_pos] == "-"
|| tokens[last_pos] == "*" || tokens[last_pos] == "/")
{
re = calculate_postfix_expression(tokens, last_pos, final_ans);
if (re == "error")
return "error";
else
{
a = stol(re);
final_ans[n] = re;
}
}
else
a = stol(tokens[last_pos]);
if (tokens[o] == "+")
a = a + b;
else if (tokens[o] == "-")
a = a - b;
else if (tokens[o] == "*")
a = a * b;
else if (tokens[o] == "/")
a = a / b;
ans = to_string(a);
return ans;
}
int main(int argc, char *argv[])
{
string input_buffer, str;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
int n = 0;
if (!check_expression(tokens, n))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
int last_pos = tokens.size() - 1;
vector<string> final_ans;
final_ans.assign(n * 2 + 2, "error");
str = calculate_postfix_expression(tokens, last_pos, final_ans);
if (str == "error")
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_nums(tokens);
print_ops(tokens);
cout << "Result: ";
for (int i = 0; i < final_ans.size(); i++)
{
if (final_ans[i] != "error")
cout << final_ans[i] << ' ';
}
cout << str;
cout << endl;
return EXIT_SUCCESS;
}
Credit: 林暐晉 (110021134)
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <cctype>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(const vector<string> &tokens);
string calculate_postfix_expression(
vector<string> &tokens, vector<string> &vector_ans);
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
void expand(vector<string> &vector_ans, int len);
void prinf_ans(vector<string> &vector_ans);
int main()
{
int len = 0;
string input_buffer;
vector<string> tokens, vector_ans;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_nums(tokens);
print_ops(tokens);
cout << "Result:";
if (tokens.size() == 1)
{
cout << " " << tokens[0];
}
len = tokens.size();
expand(vector_ans, len);
calculate_postfix_expression(tokens, vector_ans);
prinf_ans(vector_ans);
return EXIT_SUCCESS;
}
void parse_expression(const string &expression, vector<string> &tokens)
{
stringstream stream_ex(expression);
for (string s; stream_ex >> s;)
{
tokens.push_back(s);
}
}
bool check_expression(const vector<string> &tokens)
{
unsigned long long num = 0, op = 0;
for (int i = 0; i < tokens.size(); i++)
{
bool n = 0;
for (int j = 0; j < tokens[i].size(); j++)
{
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
op += 1;
break;
}
else if (tokens[i][j] == '-')
{
if (j != 0)
{
return 0;
}
}
else if (isdigit(tokens[i][j]))
{
n = 1;
}
else
{
return 0;
}
}
if (n == 1)
{
num += 1;
}
if (num - op < 1)
{
return 0;
}
}
if (num - op != 1)
{
return 0;
}
return 1;
}
void expand(vector<string> &vector_ans, int len)
{
for (int i = 0; i < len; i++)
{
vector_ans.push_back("\0");
}
}
string calculate_postfix_expression(
vector<string> &tokens, vector<string> &vector_ans)
{
string ans, num_1, num_2, op;
int location = tokens.size() - 1;
op = tokens[tokens.size() - 1];
tokens.pop_back();
if (op == "+")
{
num_2 = calculate_postfix_expression(tokens, vector_ans);
num_1 = calculate_postfix_expression(tokens, vector_ans);
ans = to_string(stol(num_1) + stol(num_2));
vector_ans[location] = ans;
return ans;
}
else if (op == "-")
{
num_2 = calculate_postfix_expression(tokens, vector_ans);
num_1 = calculate_postfix_expression(tokens, vector_ans);
ans = to_string(stol(num_1) - stol(num_2));
vector_ans[location] = ans;
return ans;
}
else if (op == "*")
{
num_2 = calculate_postfix_expression(tokens, vector_ans);
num_1 = calculate_postfix_expression(tokens, vector_ans);
ans = to_string(stol(num_1) * stol(num_2));
vector_ans[location] = ans;
return ans;
}
else if (op == "/")
{
num_2 = calculate_postfix_expression(tokens, vector_ans);
num_1 = calculate_postfix_expression(tokens, vector_ans);
ans = to_string(stol(num_1) / stol(num_2));
vector_ans[location] = ans;
return ans;
}
else
{
return op;
}
}
void prinf_ans(vector<string> &vector_ans)
{
for (int i = 0; i < vector_ans.size(); i++)
{
if (vector_ans[i] != "\0")
{
cout << " " << vector_ans[i];
}
}
cout << endl;
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands:";
for (int i = 0; i < tokens.size(); i++)
{
bool n = 0;
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
n = 1;
}
if (n == 0)
{
cout << " " << tokens[i];
}
}
cout << endl;
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators:";
for (int i = 0; i < tokens.size(); i++)
{
bool n = 0;
if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
{
n = 1;
}
if (n == 1)
{
cout << " " << tokens[i];
}
}
cout << endl;
}
TA version:
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void parse_expression(const string &expression, vector<string> &tokens);
bool check_expression(vector<string> &tokens, int &pos);
bool check_postfix_expression(const vector<string> &tokens);
string calculate_postfix_expression(vector<string> &tokens, int &last_pos);
// hint: use std::to_string(long) to convert long to string
// see https://en.cppreference.com/w/cpp/string/basic_string/to_string
// hint: use recursion to find the subexpressions of two operands
void print_ops(const vector<string> &tokens);
void print_nums(const vector<string> &tokens);
void calculate_expression(const vector<string> tokens);
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_postfix_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_nums(tokens);
print_ops(tokens);
cout << "Result:";
int last_pos = tokens.size() - 1;
calculate_expression(tokens);
cout << endl;
return EXIT_SUCCESS;
}
bool is_number(string str) // Check if a string is a number
{
for (int i = 0; i < str.length(); i++)
{
if (i == 0 && str[i] == '-' && str.length() > 1)
continue;
if (!isdigit(str[i]))
return false;
}
return true;
}
void parse_expression(const string &expression, vector<string> &tokens)
{
string temp;
for (int i = 0; i < expression.size(); i++)
{
if (expression[i] == ' ')
{
tokens.push_back(temp);
temp.clear();
}
else if (i == expression.size() - 1) // End of Input_Buffer
{
temp += expression.substr(i, 1);
tokens.push_back(temp);
}
else
temp += expression.substr(i, 1);
}
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands:";
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i].length() != 1 || isdigit(tokens[i][0]))
cout << ' ' << tokens[i];
}
cout << endl;
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators:";
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i].length() == 1 && !isdigit(tokens[i][0]))
cout << ' ' << tokens[i];
}
cout << endl;
}
void calculate_expression(const vector<string> tokens)
{
if (tokens.size() == 1)
{
cout << tokens[0];
return;
}
vector<long> num_stack;
int now = 0;
while (now < tokens.size())
{
while (is_number(tokens[now]))
{
num_stack.push_back(stol(tokens[now]));
now++;
}
long a = num_stack[num_stack.size() - 2],
b = num_stack[num_stack.size() - 1];
num_stack.pop_back();
num_stack.pop_back();
if (tokens[now] == "+")
num_stack.push_back(a + b);
else if (tokens[now] == "-")
num_stack.push_back(a - b);
else if (tokens[now] == "*")
num_stack.push_back(a * b);
else if (tokens[now] == "/")
num_stack.push_back(a / b);
cout << ' ' << num_stack[num_stack.size() - 1];
now++;
}
}
bool check_postfix_expression(const vector<string> &tokens)
{
int stack_count = 0;
for (int i = 0; i < tokens.size(); i++)
{
if (i > 0 && stack_count < 1)
return false;
if (is_number(tokens[i]))
stack_count++;
else if (tokens[i] == "+" || tokens[i] == "-"
|| tokens[i] == "*" || tokens[i] == "/")
stack_count--;
else
return false;
}
return stack_count == 1;
}
Lecture 13: Structure (1)
Why Structure?
寫 structure 好處
- 可以讓多個型態的資料放在一起,視為一個單一的型態,並且用一個變數來存取。
- 可以讓變數賦予更多的意義,以下內容會介紹。
- 讓 function 回傳多個值,因為 structure 可以包涵多個不同型態的資料,並視為一個單一的型態。
Structure Definition
struct <name>
{
<type> <member>;
<type> <member>;
...
};
Example: Structures in C - GeeksforGeeks
// A variable declaration with structure declaration.
struct Point
{
int x, y;
} p1; // The variable p1 is declared with 'Point'
// A variable declaration like basic data types
struct Point
{
int x, y;
};
int main()
{
struct Point p1; // The variable p1 is declared like a normal variable
}
Structure Usage
如何使用 structure?
初始化的時候可以使用 {}
來初始化。若有 structure 內有陣列的話,則需要透過多層的 {}
來初始化。
要使用的時候可以透過 .
運算子來存取 structure 裡頭的資料,如下所示:
Example: Structures in C - GeeksforGeeks
#include <iostream>
struct Point
{
int x, y;
};
int main()
{
struct Point p1 = {0, 1};
// Accessing members of point p1
p1.x = 20;
std::cout << "x = " << p1.x << ", y = " << p1.y << std::endl;
return 0;
}
Array example:
#include <iostream>
struct Triangle
{
int x[3];
int y[3];
};
struct Point
{
int x, y;
};
int main()
{
struct Point p1 = {0, 1};
struct Triangle t1 = {{0, 1, 2}, {0, 1, 2}};
// Accessing members of point p1
p1.x = 20;
std::cout << "x = " << p1.x << ", y = " << p1.y << std::endl;
// Accessing members of triangle t1
t1.x[0] = 20;
for (int i = 0; i < 3; i++)
{
std::cout << "x[" << i << "] = " << t1.x[i]
<< ", y[" << i << "] = " << t1.y[i] << std::endl;
}
return 0;
}
同時 structure 也可以作為 array 的元素,如下所示:
Reference: Structures in C - GeeksforGeeks
#include <iostream>
struct Point
{
int x, y;
};
int main()
{
// Create an array of structures
struct Point arr[10];
// Access array members
arr[0].x = 10;
arr[0].y = 20;
std::cout << arr[0].x << " " << arr[0].y << std::endl;
return 0;
}
若為了程式碼可讀性,可以寫 function 使各個 structure 內的 member 各自分開初始化。
並可以使用 memset()
來初始化 array 宣告的 member 的值。
#include <iostream>
#include <cstring> // For memset()
struct Point
{
int x, y;
int metadata[10];
};
void init(struct Point p[], int size)
{
for (int i = 0; i < size; i++)
{
p[i].x = 1;
p[i].y = 2;
memset(p[i].metadata, 0, sizeof(int) * 10); // Initialize metadata to 0
// Note: memset() can only initialize with 0 or '\0'
}
}
int main()
{
// Create an array of structures
struct Point arr[10];
// Initialize the array
init(arr, 10);
// Access array members
arr[0].x = 10;
arr[0].y = 20;
std::cout << arr[0].x << " " << arr[0].y << std::endl;
for (int i = 0; i < 10; i++)
{
std::cout << arr[i].metadata[i] << " ";
}
std::cout << std::endl;
return 0;
}
Structure as a Type
在使用 structure 除了用 struct <name>
之外,也可以將 structure 宣告為 type 來使用。
typedef struct <struct_name>
{
<type> <member>;
<type> <member>;
...
} <type_name>;
Example:
#include <iostream>
typedef struct Triangle_struct
{
int x[3];
int y[3];
} Triangle;
typedef struct Point_struct
{
int x, y;
} Point;
int main()
{
Point p1 = {0, 1};
Triangle t1 = {{0, 1, 2}, {0, 1, 2}};
// Accessing members of point p1
p1.x = 20;
std::cout << "x = " << p1.x << ", y = " << p1.y << std::endl;
// Accessing members of triangle t1
t1.x[0] = 20;
for (int i = 0; i < 3; i++)
{
std::cout << "x[" << i << "] = " << t1.x[i]
<< ", y[" << i << "] = " << t1.y[i] << std::endl;
}
return 0;
}
Structure example: Complex Numbers
#include <iostream>
#include <cctype> // isdigit()
using namespace std;
typedef struct Complex
{
int real;
int imag;
} Complex;
Complex parse_complex_number(const char input_buffer[])
{
Complex complex_number;
complex_number.real = 0;
complex_number.imag = 0;
// Parse real part
int i = 0, real_sign = 1;
if (input_buffer[i] == '-')
{
real_sign = -1;
i++;
}
else if (input_buffer[i] == '+')
{
i++;
}
else
{
complex_number.real = input_buffer[i] - '0';
i++;
}
while (isdigit(input_buffer[i]))
{
complex_number.real = 10 * complex_number.real + (input_buffer[i] - '0');
i++;
}
complex_number.real *= real_sign;
// Skip the '+'
if (input_buffer[i] == '+')
{
i++;
}
// Parse imaginary part
int imag_sign = 1;
if (input_buffer[i] == '-')
{
imag_sign = -1;
i++;
}
else if (input_buffer[i] == '+')
{
i++;
}
else
{
complex_number.imag = input_buffer[i] - '0';
i++;
}
while (isdigit(input_buffer[i]))
{
complex_number.imag = 10 * complex_number.imag + (input_buffer[i] - '0');
i++;
}
if (input_buffer[i] == 'i')
{
i++;
}
complex_number.imag *= imag_sign;
// check if there is any extra characters
if (input_buffer[i] != '\0')
{
cout << "Error: Invalid input" << endl;
exit(1);
}
// Return the complex number
return complex_number;
}
void print_complex_number(Complex &complex_number)
{
cout << complex_number.real << " + " << complex_number.imag << "i" << endl;
}
void clean_up_input_buffer(char input_buffer[])
{
for (int i = 0; input_buffer[i] != '\0'; i++)
{
input_buffer[i] = '\0';
}
}
int main()
{
char input_buffer[10000 + 1]; // + 1 for the last '\0'
Complex a, b;
cout << "Input a complex number (a) in (+|-)[0-9]+(+|-)[0-9]i : ";
cin >> input_buffer;
a = parse_complex_number(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "(a) = ";
print_complex_number(a);
cout << "Input a complex number (b) in (+|-)[0-9]+(+|-)[0-9]i : ";
cin >> input_buffer;
b = parse_complex_number(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "(b) = ";
print_complex_number(b);
return 0;
}
Lecture 14: Structure (2)
Structure example: Floating point number
#include <iostream>
#include <cctype> // isdigit()
#include <cmath> // pow()
#include <climits> // INT_MAX
using namespace std;
typedef struct Float_t
{
int sign;
int exp;
long mantissa;
} Float;
void normalize_float(Float &f)
{
while (f.mantissa > 0 && f.mantissa % 10 == 0)
{
f.mantissa /= 10;
f.exp++;
}
while (f.mantissa > INT_MAX)
{
f.mantissa /= 10;
f.exp++;
}
if (f.mantissa == 0)
{
f.exp = 0;
f.sign = 1;
}
}
Float parse_float_number(char input_buffer[])
{
Float result;
result.sign = 1;
result.exp = 0;
result.mantissa = 0;
int i = 0;
if (input_buffer[i] == '-')
{
result.sign = -1;
i++;
}
if (input_buffer[i] == '0')
{
i++;
}
else
{
while (isdigit(input_buffer[i]))
{
result.mantissa = result.mantissa * 10 + input_buffer[i] - '0';
i++;
}
}
if (input_buffer[i] == '.')
{
i++;
int factor = 1;
while (isdigit(input_buffer[i]))
{
result.mantissa = result.mantissa * 10 + input_buffer[i] - '0';
factor *= 10;
i++;
}
result.exp = -1 * (int)log10(factor);
}
if (input_buffer[i] == 'e' || input_buffer[i] == 'E')
{
i++;
int exp = 0;
if (input_buffer[i] == '-')
{
i++;
while (isdigit(input_buffer[i]))
{
exp = exp * 10 + input_buffer[i] - '0';
i++;
}
result.exp += -1 * exp;
}
else
{
while (isdigit(input_buffer[i]))
{
exp = exp * 10 + input_buffer[i] - '0';
i++;
}
result.exp += exp;
}
}
// normalize the result float
normalize_float(result);
return result;
}
void print_float_number(Float number)
{
if (number.sign == -1)
{
cout << "-";
}
cout << number.mantissa;
if (number.exp != 0)
{
cout << "e" << number.exp;
}
}
void clean_up_input_buffer(char input_buffer[])
{
for (int i = 0; input_buffer[i] != '\0'; i++)
{
input_buffer[i] = '\0';
}
}
Float add(Float a, Float b)
{
Float result;
int same_sign = a.sign * b.sign;
if (a.exp == b.exp)
{
result.sign = a.sign;
result.exp = a.exp;
result.mantissa = a.mantissa + same_sign * b.mantissa;
}
else if (a.exp > b.exp)
{
result.sign = a.sign;
result.exp = a.exp;
long exp_factor = (long)pow(10, a.exp - b.exp);
result.mantissa = a.mantissa + same_sign * b.mantissa / exp_factor;
}
else
{
result.sign = b.sign;
result.exp = b.exp;
long exp_factor = (long)pow(10, b.exp - a.exp);
result.mantissa = b.mantissa + same_sign * a.mantissa / exp_factor;
}
if (result.mantissa < 0)
{
result.sign *= -1;
result.mantissa *= -1;
} else if (result.mantissa == 0) {
result.sign = 1;
}
normalize_float(result);
return result;
}
Float sub(Float a, Float b)
{
Float result;
int same_sign = a.sign * b.sign;
if (a.exp == b.exp)
{
result.sign = a.sign;
result.exp = a.exp;
result.mantissa = a.mantissa - same_sign * b.mantissa;
}
else if (a.exp > b.exp)
{
result.sign = a.sign;
result.exp = a.exp;
long exp_factor = (long)pow(10, a.exp - b.exp);
result.mantissa = a.mantissa - same_sign * b.mantissa / exp_factor;
}
else
{
result.sign = b.sign;
result.exp = b.exp;
long exp_factor = (long)pow(10, b.exp - a.exp);
result.mantissa = b.mantissa - same_sign * a.mantissa / exp_factor;
}
if (result.mantissa < 0)
{
result.sign *= -1;
result.mantissa *= -1;
} else if (result.mantissa == 0) {
result.sign = 1;
}
normalize_float(result);
return result;
}
Float mul(Float a, Float b)
{
Float result;
result.sign = a.sign * b.sign;
result.exp = a.exp + b.exp;
result.mantissa = a.mantissa * b.mantissa;
normalize_float(result);
return result;
}
Float div(Float a, Float b)
{
Float result;
result.sign = a.sign * b.sign;
result.exp = a.exp - b.exp;
result.mantissa = a.mantissa / b.mantissa;
normalize_float(result);
return result;
}
int main()
{
char input_buffer[10000 + 1]; // + 1 for the last '\0'
Float a, b;
cout << "Input a float number (a) in (-)xxx.xxx(e|E)(-)(xxx): ";
cin >> input_buffer;
a = parse_float_number(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "(a) = ";
print_float_number(a);
cout << endl;
cout << "Input a float number (b) in (-)xxx.xxx(e|E)(-)(xxx): ";
cin >> input_buffer;
b = parse_float_number(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "(b) = ";
print_float_number(b);
cout << endl;
cout << "(a) + (b) = ";
print_float_number(add(a, b));
cout << endl;
cout << "(a) - (b) = ";
print_float_number(sub(a, b));
cout << endl;
cout << "(a) * (b) = ";
print_float_number(mul(a, b));
cout << endl;
cout << "(a) / (b) = ";
print_float_number(div(a, b));
cout << endl;
return 0;
}
$ g++ -g test.cpp && ./a.out
Input a float number (a) in (-)xxx.xxx(e|E)(-)(xxx): -123.456e3
(a) = -123456
Input a float number (b) in (-)xxx.xxx(e|E)(-)(xxx): 456.789e4
(b) = 456789e1
(a) + (b) = 444444e1
(a) - (b) = 469134e1
(a) * (b) = -563933427e3
(a) / (b) = 0
$
Structure example: Polynomial Equation
#include <iostream>
#include <cctype> // isdigit()
#include <vector>
using namespace std;
typedef struct Poly_t
{
int coef;
int exp;
} Poly;
void normalize(vector<Poly> &poly)
{
for (int i = 0; i < poly.size(); i++)
{
if (poly[i].coef == 0)
{
poly.erase(poly.begin() + i);
i--;
}
}
}
vector<Poly> parse_poly(char input_buffer[])
{
vector<Poly> result;
int i = 0;
while (input_buffer[i] != '\0')
{
Poly p;
int sign = 1;
p.coef = 0;
p.exp = 0;
if (input_buffer[i] == '-')
{
sign = -1;
i++;
}
else if (input_buffer[i] == '+')
{
i++;
}
if (input_buffer[i] == '0')
{
p.coef = 0;
i++;
}
else
{
while (isdigit(input_buffer[i]))
{
p.coef = p.coef * 10 + input_buffer[i] - '0';
i++;
}
}
p.coef *= sign;
if (input_buffer[i] == 'x')
{
i++;
if (input_buffer[i] == '^')
{
i++;
while (isdigit(input_buffer[i]))
{
p.exp = p.exp * 10 + input_buffer[i] - '0';
i++;
}
}
}
result.push_back(p);
}
return result;
}
void print_poly(const vector<Poly> &poly)
{
if (poly.size() == 0)
{
cout << "0";
return;
}
for (int i = 0; i < poly.size(); i++)
{
if (poly[i].coef == 0)
{
continue;
}
if (poly[i].coef >= 1 && i != 0)
{
cout << "+" << poly[i].coef;
}
else if (poly[i].coef == -1)
{
cout << "-";
}
else
{
cout << poly[i].coef;
}
if (poly[i].exp == 0)
{
continue;
}
else
{
cout << "x^" << poly[i].exp;
}
}
}
vector<Poly> poly_add(const vector<Poly> &a, const vector<Poly> &b)
{
vector<Poly> result;
int i = 0;
int j = 0;
while (i < a.size() && j < b.size())
{
if (a[i].exp == b[j].exp)
{
Poly p;
p.exp = a[i].exp;
p.coef = a[i].coef + b[j].coef;
result.push_back(p);
i++;
j++;
}
else if (a[i].exp > b[j].exp)
{
result.push_back(a[i]);
i++;
}
else
{
result.push_back(b[j]);
j++;
}
}
while (i < a.size())
{
result.push_back(a[i]);
i++;
}
while (j < b.size())
{
result.push_back(b[j]);
j++;
}
normalize(result);
return result;
}
void clean_up_input_buffer(char input_buffer[])
{
for (int i = 0; input_buffer[i] != '\0'; i++)
{
input_buffer[i] = '\0';
}
}
int main()
{
char input_buffer[10000 + 1];
vector<Poly> a, b;
cout << "Enter polynomial a: ";
cin >> input_buffer;
a = parse_poly(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "Polynomial a: ";
print_poly(a);
cout << endl;
cout << "Enter polynomial b: ";
cin >> input_buffer;
b = parse_poly(input_buffer);
clean_up_input_buffer(input_buffer);
cout << "Polynomial b: ";
print_poly(b);
cout << endl;
vector<Poly> c = poly_add(a, b);
cout << "Polynomial c = a + b: ";
print_poly(c);
cout << endl;
}
$ g++ -g test.cpp && ./a.out
Enter polynomial a: -30x^3+20x^1
Polynomial a: -30x^3+20x^1
Enter polynomial b: 50x^3-20
Polynomial b: 50x^3-20
Polynomial c = a + b: 20x^3+20x^1-20
$
Structure example: Geometry Object
Ref: Computational geometry - Wikipedia
#include <iostream>
#include <vector>
using namespace std;
typedef struct Point_t
{
int x;
int y;
} Point;
typedef struct Triangle_t
{
Point p[3];
} Triangle;
typedef struct Quadrilateral_t
{
Point p[4];
} Quadrilateral;
typedef struct Polygon_t
{
vector<Point> p;
} Polygon;
Point parse_point(char input_buffer[], int i)
{
Point p;
while (input_buffer[i] != '\0')
{
if (input_buffer[i] == '(')
{
i++;
}
if (input_buffer[i] == '0')
{
p.x = 0;
i++;
}
else
{
while (isdigit(input_buffer[i]))
{
p.x = p.x * 10 + input_buffer[i] - '0';
i++;
}
}
if (input_buffer[i] == ',')
{
i++;
}
if (input_buffer[i] == '0')
{
p.y = 0;
i++;
}
else
{
while (isdigit(input_buffer[i]))
{
p.y = p.y * 10 + input_buffer[i] - '0';
i++;
}
}
if (input_buffer[i] == ')')
{
break;
}
}
return p;
}
Triangle parse_triangle(char input_buffer[])
{
Triangle t;
int i = 0;
while (input_buffer[i] != '\0')
{
for (int j = 0; j < 3; j++)
{
if (input_buffer[i] == '(')
{
i++;
}
t.p[j] = parse_point(input_buffer, i);
i += 2;
}
}
return t;
}
Quadrilateral parse_quadrilateral(char input_buffer[])
{
Quadrilateral q;
int i = 0;
while (input_buffer[i] != '\0')
{
for (int j = 0; j < 4; j++)
{
if (input_buffer[i] == '(')
{
i++;
}
q.p[j] = parse_point(input_buffer, i);
i += 2;
}
}
return q;
}
Polygon parse_polygon(char input_buffer[])
{
Polygon poly;
int i = 0;
while (input_buffer[i] != '\0')
{
if (input_buffer[i] == '(')
{
i++;
}
poly.p.push_back(parse_point(input_buffer, i));
i += 2;
}
return poly;
}
void print_geometry(const Point &p)
{
cout << "(" << p.x << ", " << p.y << ")" << endl;
}
void print_geometry(const Triangle &t)
{
cout << "Triangle: " << endl;
for (int i = 0; i < 3; i++)
{
print_geometry(t.p[i]);
}
}
void print_geometry(const Quadrilateral &q)
{
cout << "Quadrilateral: " << endl;
for (int i = 0; i < 4; i++)
{
print_geometry(q.p[i]);
}
}
void print_geometry(const Polygon &poly)
{
cout << "Polygon: " << endl;
for (int i = 0; i < poly.p.size(); i++)
{
print_geometry(poly.p[i]);
}
}
unsigned long int area(const Triangle &t)
{
unsigned long int sum = 0;
for (int i = 0; i < 3; i++)
{
sum += t.p[i].x * t.p[(i + 1) % 3].y;
sum -= t.p[i].y * t.p[(i + 1) % 3].x;
}
return sum / 2;
}
unsigned long int area(const Quadrilateral &q)
{
unsigned long int sum = 0;
for (int i = 0; i < 4; i++)
{
sum += q.p[i].x * q.p[(i + 1) % 4].y;
sum -= q.p[i].y * q.p[(i + 1) % 4].x;
}
return sum / 2;
}
unsigned long int area(const Polygon &poly)
{
unsigned long int sum = 0;
for (int i = 0; i < poly.p.size(); i++)
{
sum += poly.p[i].x * poly.p[(i + 1) % poly.p.size()].y;
sum -= poly.p[i].y * poly.p[(i + 1) % poly.p.size()].x;
}
return sum / 2;
}
int main()
{
char input_buffer[10000 + 1];
cout << "Enter the type of geometry (t|q|p) with points (x,y): ";
cin.getline(input_buffer, 10000);
if (input_buffer[0] == 't')
{
Triangle t = parse_triangle(input_buffer + 1);
print_geometry(t);
cout << "Area: " << area(t) << endl;
}
else if (input_buffer[0] == 'q')
{
Quadrilateral q = parse_quadrilateral(input_buffer + 1);
print_geometry(q);
cout << "Area: " << area(q) << endl;
}
else if (input_buffer[0] == 'p')
{
Polygon poly = parse_polygon(input_buffer + 1);
print_geometry(poly);
cout << "Area: " << area(poly) << endl;
}
else
{
cout << "Invalid input" << endl;
}
}
$ g++ -g test.cpp && ./a.out
Enter the type of geometry (t|q|p) with points (x,y): t((0,0),(1,1),(0,4))
Triangle:
(0, 0)
(1, 1)
(0, 4)
Area: 2
$ g++ -g test.cpp && ./a.out
Enter the type of geometry (t|q|p) with points (x,y): q((0,0),(1,1),(3,4),(0,4))
Quadrilateral:
(0, 0)
(1, 1)
(3, 4)
(0, 4)
Area: 6
$ g++ -g test.cpp && ./a.out
Enter the type of geometry (t|q|p) with points (x,y): p((0,0),(1,1),(3,4),(4,4),(0,4))
Polygon:
(0, 0)
(1, 1)
(3, 4)
(4, 4)
(0, 4)
Area: 6
$
Structure example: Vector & Matrix (Mathmatical)
#include <iostream>
#include <vector>
#include <cmath>
using std::cin;
using std::cout;
using std::endl;
typedef struct Vector_t
{
std::vector<double> unit;
double length;
} Vector;
typedef struct Matrix_t
{
std::vector<Vector> row_vectors;
} Matrix;
Vector parse_vector(char input_buffer[], int &i)
{
Vector v;
int number = 0
// parse normal vector
while (input_buffer[i] != '\0')
{
if (input_buffer[i] == '[')
{
i++;
}
if (input_buffer[i] == '0')
{
v.unit.push_back(0);
i++;
}
else
{
while (isdigit(input_buffer[i]))
{
number = number * 10 + input_buffer[i] - '0';
i++;
}
v.unit.push_back(number);
number = 0;
}
if (input_buffer[i] == ',')
{
i++;
}
if (input_buffer[i] == ' ')
{
i++;
}
if (input_buffer[i] == ']')
{
break;
}
}
// calc vector length
for (int j = 0; j < v.unit.size(); j++)
{
v.length = v.length + v.unit[j] * v.unit[j];
}
v.length = sqrt(v.length);
// normalize vector
for (int j = 0; j < v.unit.size(); j++)
{
v.unit[j] = v.unit[j] / v.length;
}
return v;
}
Matrix parse_matrix(char input_buffer[])
{
Matrix m;
// parse matrix
int i = 0;
while (input_buffer[i] != '\0')
{
if (input_buffer[i] == '[')
{
i++;
}
while (input_buffer[i] != ']')
{
m.row_vectors.push_back(parse_vector(input_buffer, i));
}
if (input_buffer[i] == ']')
{
i++;
}
}
return m;
}
void print_vector(const Vector &v)
{
cout << "[";
for (int i = 0; i < v.unit.size(); i++)
{
cout << v.unit[i] * v.length;
if (i != v.unit.size() - 1)
{
cout << ", ";
}
}
cout << "]";
}
void print_matrix(const Matrix &m)
{
cout << "[";
for (int i = 0; i < m.row_vectors.size(); i++)
{
print_vector(m.row_vectors[i]);
cout << endl;
}
cout << "]";
}
int main()
{
char input_buffer[10000 + 1];
cout << "Enter a vector: ";
cin.getline(input_buffer, 10000);
int i = 0;
Vector v = parse_vector(input_buffer, i);
cout << "Vector v: " << endl;
print_vector(v);
cout << endl;
cout << "Length: " << v.length << endl;
cout << endl;
cout << "Enter a matrix: ";
cin.getline(input_buffer, 10000);
Matrix m = parse_matrix(input_buffer);
cout << "Matrix m: " << endl;
print_matrix(m);
cout << endl;
}
$ g++ -g test.cpp && ./a.out
Enter a vector: [9,10,11,12]
Vector v:
[9, 10, 11, 12]
Length: 21.1187
Enter a matrix: [[1,2,3,4][5,6,7,8][9,10,11,12]]
Matrix m:
[[1, 2, 3, 4]
[5, 6, 7, 8]
[9, 10, 11, 12]
]
$
Lab 14: Matrix & Sparse Matrix
Lab 14-1: COO Sparse Matrix 儲存及顯示 (40%)
- 輸入:以整數 (符合
long
型態) 的矩陣 \(M\)- 矩陣 \(M\) 的維度為正整數 \(n\) 與 \(m\)。
- 矩陣的起始位置為 \(M[0][0]\)。
- 欲輸入的元素的個數為 \(elements\)。
- 元素紀錄的方式為
(i, j, v)
。 - 紀錄格式參考以下的範例。
- 輸出:顯示使用者輸入的 Sparse Matrix 及展開的 Matrix。
- 矩陣 \(M\) 的維度 \(n\) 與 \(m\)。
- 非
0
元素(i, j, v)
。- 需以
(0, 0), (0, 1), ... (0, n-1), (1, 0), ... (n-1, m-1)
的順序顯示。
- 需以
- 展開 matrix 輸出格式參考以下的範例。
- 檔名:lab14_1_<學號>.cpp (e.g. lab14_1_106062802.cpp)
Notice:
- 程式無需任何輸入提示,程式需分析輸入後顯示使用者輸入的矩陣資訊。
- 程式需檢查輸入的資料是否為合法矩陣,若不相符則顯示錯誤訊息
Invalid matrix.
並結束程式。- 若矩陣維度為非正整數,則該矩陣為非法矩陣。
- 若欲輸入的元素的個數為負整數,則該矩陣為非法矩陣。
- 如元素的索引值大於矩陣的維度則不算為合法元素。
- 若元素的索引值重複,則以最後一筆紀錄為主。
- 若元素的值為
0
則忽略不紀錄。
- 程式需使用 function 來處理輸入與輸出。
- 程式需在 30 秒之內執行完畢,所有測資皆不會超過 30 秒的執行時間。
Format
<matrix rows>⏎
<matrix columns>⏎
<number of input elements>⏎
<element index> <element index> <element value>⏎
<element index> <element index> <element value>⏎
...⏎
The COO matrix is:
<matrix rows>
<matrix columns>
<element index> <element index> <element value>
<element index> <element index> <element value>
...
The matrix is:
<M0_0> <M0_1> ... <M0_n>
<M1_0> <M1_1> ... <M1_n>
...
<Mm_0> <Mm_1> ... <Mm_n>
Example
$ ./a.out⏎
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
0 3 4⏎
The COO matrix is:
3
4
0 0 1
0 3 4
1 1 2
2 2 3
The matrix is:
1 0 0 4
0 2 0 0
0 0 3 0
$ ./a.out⏎
3⏎
4⏎
4⏎
0 0 1⏎
1 1 0⏎
2 2 3⏎
0 0 4⏎
The COO matrix is:
3
4
0 0 4
2 2 3
The matrix is:
4 0 0 0
0 0 0 0
0 0 3 0
$ ./a.out⏎
0⏎
Invalid matrix.
$ ./a.out⏎
3⏎
0⏎
Invalid matrix.
$ ./a.out⏎
3⏎
4⏎
-1⏎
Invalid matrix.
$ ./a.out⏎
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
3 3 4⏎
Invalid matrix.
Pseudo Code
#include <iostream>
#include <vector>
typedef struct coo_sparse_matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector<unsigned long> row_ind;
std::vector<unsigned long> col_ind;
std::vector<long> val;
} coo_sparse_matrix;
typedef struct matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector<std::vector<long> > val;
} matrix;
void insert_coo_matrix_element(
coo_sparse_matrix &coo_matrix,
unsigned long row,
unsigned long col,
long val);
void sort_col_ind(coo_sparse_matrix &coo_matrix)
{
for (i = 0; i < coo_matrix.col_ind.size(); i++)
{
for (j = i + 1; j < coo_matrix.col_ind.size(); j++)
{
if (coo_matrix.col_ind[i] > coo_matrix.col_ind[j])
{
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_row_ind(coo_sparse_matrix &coo_matrix)
{
for (i = 0; i < coo_matrix.row_ind.size(); i++)
{
for (j = i + 1; j < coo_matrix.row_ind.size(); j++)
{
if (coo_matrix.row_ind[i] > coo_matrix.row_ind[j] ||
(coo_matrix.row_ind[i] == coo_matrix.row_ind[j] &&
coo_matrix.col_ind[i] > coo_matrix.col_ind[j]))
{
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_coo_matrix(coo_sparse_matrix &coo_matrix)
{
sort_col_ind(coo_matrix);
sort_row_ind(coo_matrix);
}
void parse_coo_matrix(coo_sparse_matrix &coo_matrix);
void print_coo_matrix(const coo_sparse_matrix &coo_matrix);
matrix convert_coo_matrix_to_matrix(const coo_sparse_matrix &coo_matrix);
void print_matrix(const matrix &matrix);
int main()
{
coo_sparse_matrix coo_matrix_M;
parse_coo_matrix(coo_matrix_M);
std::cout << "The COO matrix is:" << std::endl;
print_coo_matrix(coo_matrix_M);
matrix matrix_M = convert_coo_matrix_to_matrix(coo_matrix_M);
std::cout << "The matrix is:" << std::endl;
print_matrix(matrix_M);
return 0;
}
Reference: Sparse matrix - Wikipedia
TA version:
#include <iostream>
#include <vector>
using namespace std;
typedef struct coo_sparse_matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector<unsigned long> row_ind;
std::vector<unsigned long> col_ind;
std::vector<long> val;
} coo_sparse_matrix;
typedef struct matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector< std::vector<long> > val;
} matrix;
void insert_coo_matrix_element(
coo_sparse_matrix &coo_matrix,
unsigned long row,
unsigned long col,
long val);
void sort_col_ind(coo_sparse_matrix &coo_matrix)
{
for (int i = 0; i < coo_matrix.col_ind.size(); i++)
{
for (int j = i + 1; j < coo_matrix.col_ind.size(); j++)
{
if (coo_matrix.col_ind[i] > coo_matrix.col_ind[j])
{
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_row_ind(coo_sparse_matrix &coo_matrix)
{
for (int i = 0; i < coo_matrix.row_ind.size(); i++)
{
for (int j = i + 1; j < coo_matrix.row_ind.size(); j++)
{
if (coo_matrix.row_ind[i] > coo_matrix.row_ind[j] ||
(coo_matrix.row_ind[i] == coo_matrix.row_ind[j] &&
coo_matrix.col_ind[i] > coo_matrix.col_ind[j]))
{
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_coo_matrix(coo_sparse_matrix &coo_matrix)
{
sort_col_ind(coo_matrix);
sort_row_ind(coo_matrix);
}
void parse_coo_matrix(coo_sparse_matrix &coo_matrix);
void print_coo_matrix(const coo_sparse_matrix &coo_matrix);
matrix convert_coo_matrix_to_matrix(const coo_sparse_matrix &coo_matrix);
void print_matrix(const matrix &matrix);
int main()
{
coo_sparse_matrix coo_matrix_M;
parse_coo_matrix(coo_matrix_M);
sort_coo_matrix(coo_matrix_M);
std::cout << "The COO matrix is:" << std::endl;
print_coo_matrix(coo_matrix_M);
matrix matrix_M = convert_coo_matrix_to_matrix(coo_matrix_M);
std::cout << "The matrix is:" << std::endl;
print_matrix(matrix_M);
return 0;
}
void parse_coo_matrix(coo_sparse_matrix &coo_matrix)
{
int n; // n lines of inputs
// Exceptions: rows and col can't be nonpositive, n can't be negative
for (int i = 0; i < 3; i++)
{
int temp;
cin >> temp;
if (i == 0 && temp > 0)
{
coo_matrix.rows = temp;
continue;
}
if (i == 1 && temp > 0)
{
coo_matrix.cols = temp;
continue;
}
if (i == 2 && temp >= 0)
{
n = temp;
continue;
}
cout << "Invalid matrix." << endl;
exit(0);
}
// Start of Parsing
while (n--)
{
unsigned long r, c;
long v;
cin >> r >> c >> v;
// Exceptions: Index Out of Range
if (r < 0 ||
r >= coo_matrix.rows ||
c < 0 ||
c >= coo_matrix.cols)
{
cout << "Invalid matrix." << endl;
exit(0);
}
// Store COO
bool flag = false;
for (int i = 0; i < coo_matrix.row_ind.size(); i++)
{
// Rewrite val if already exist
if (r == coo_matrix.row_ind[i] && c == coo_matrix.col_ind[i])
{
if (v == 0) // Erase if val = 0
{
coo_matrix.row_ind.erase(coo_matrix.row_ind.begin() + i);
coo_matrix.col_ind.erase(coo_matrix.col_ind.begin() + i);
coo_matrix.val.erase(coo_matrix.val.begin() + i);
}
else // Rewrite if val != 0
coo_matrix.val[i] = v;
flag = true;
break;
}
}
// Push_back if not exists yet
if (!flag && v != 0)
{
coo_matrix.row_ind.push_back(r);
coo_matrix.col_ind.push_back(c);
coo_matrix.val.push_back(v);
}
}
// End of Parsing
// Sorting
sort_coo_matrix(coo_matrix);
}
matrix convert_coo_matrix_to_matrix(const coo_sparse_matrix &coo_matrix)
{
matrix mtrx;
mtrx.rows = coo_matrix.rows;
mtrx.cols = coo_matrix.cols;
for (unsigned long i = 0; i < mtrx.rows; i++)
mtrx.val.push_back(vector<long>(mtrx.cols, 0));
for (int i = 0; i < coo_matrix.row_ind.size(); i++)
{
unsigned long r = coo_matrix.row_ind[i], c = coo_matrix.col_ind[i];
long v = coo_matrix.val[i];
mtrx.val[r][c] = v;
}
return mtrx;
}
void print_matrix(const matrix &matrix)
{
for (unsigned long i = 0; i < matrix.rows; i++)
{
for (unsigned long j = 0; j < matrix.cols; j++)
{
cout << matrix.val[i][j] << ' ';
}
cout << endl;
}
}
void print_coo_matrix(const coo_sparse_matrix &coo_matrix)
{
cout << coo_matrix.rows << '\n'
<< coo_matrix.cols << endl;
for (unsigned long i = 0; i < coo_matrix.row_ind.size(); i++)
{
cout << coo_matrix.row_ind[i]
<< " "
<< coo_matrix.col_ind[i]
<< " "
<< coo_matrix.val[i]
<< endl;
}
}
Lab 14-2: COO Sparse Matrix 相加 (40%)
- 輸入:以整數 (符合
long
型態) 的兩矩陣 \(M\) 及 \(N\)- 矩陣 \(M\) 及 \(N\) 的維度為正整數 \(M_n\)、\(M_m\)、\(N_n\)、\(N_m\)。
- 矩陣的起始位置為 \(M[0][0]\)、\(N[0][0]\)。
- 欲輸入的元素的個數為 \(M_{elements}\)、\(N_{elements}\)。
- 元素紀錄的方式為
(i, j, v)
。 - 紀錄格式參考以下的範例。
- 輸出:顯示使用者輸入的兩 Sparse Matrix 及相加結果的 Sparse Matrix & 展開的 Matrix。
- 矩陣 \(P\) 的維度 \(P_n\) 與 \(P_m\)。
- 非
0
元素(i, j, v)
。- 需以
(0, 0), (0, 1), ... (0, n-1), (1, 0), ... (n-1, m-1)
的順序顯示。
- 需以
- 展開 matrix 輸出格式參考以下的範例。
- 檔名:lab14_2_<學號>.cpp (e.g. lab14_2_106062802.cpp)
Notice:
- 程式需提示輸入 Sparse Matrix \(M\) 及 \(N\),程式需分析輸入後顯示使用者輸入的矩陣資訊。
- 計算完 \(P\) 後,顯示矩陣 \(P\),及展開後的 \(P\)。
- 程式需檢查輸入的資料是否為合法矩陣,若不相符則顯示錯誤訊息
Invalid matrix.
並結束程式。- 若矩陣維度為非正整數,則該矩陣為非法矩陣。
- 欲輸入的元素的個數為負整數,則該矩陣為非法矩陣。
- 如元素的索引值大於矩陣的維度則不算為合法元素。
- 若元素的索引值重複,則以最後一筆紀錄為主。
- 若元素的值為
0
則忽略不紀錄。
- 程式需檢查矩陣 \(N\) 的維度是否與矩陣 \(M\) 相同,若不相同則在輸入完矩陣 \(N\) 時顯示錯誤訊息
Invalid matrix.
並結束程式。 - 程式需使用 function 來處理輸入與輸出、比較、運算。
- 程式需在 30 秒之內執行完畢,所有測資皆不會超過 30 秒的執行時間。
Format
Input COO matrix M:
<matrix rows>⏎
<matrix columns>⏎
<number of input elements>⏎
<element index> <element index> <element value>⏎
<element index> <element index> <element value>⏎
...⏎
The COO matrix M is:
<matrix rows>
<matrix columns>
<element index> <element index> <element value>
<element index> <element index> <element value>
...
Input COO matrix N:
<matrix rows>⏎
<matrix columns>⏎
<number of input elements>⏎
<element index> <element index> <element value>⏎
<element index> <element index> <element value>⏎
...⏎
The COO matrix N is:
<matrix rows>
<matrix columns>
<element index> <element index> <element value>
<element index> <element index> <element value>
...
The COO matrix P = M + N is:
<matrix rows>
<matrix columns>
<element index> <element index> <element value>
<element index> <element index> <element value>
...
The matrix P is:
<P0_0> <P0_1> ... <P0_n>
<P1_0> <P1_1> ... <P1_n>
...
<Pm_0> <Pm_1> ... <Pm_n>
Example
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
0 3 4⏎
The COO matrix M is:
3
4
0 0 1
0 3 4
1 1 2
2 2 3
Input COO matrix N:
3⏎
4⏎
4⏎
0 1 5⏎
1 2 6⏎
2 3 7⏎
2 2 4⏎
The COO matrix N is:
3
4
0 1 5
1 2 6
2 2 4
2 3 7
The COO matrix P = M + N is:
3
4
0 0 1
0 1 5
0 3 4
1 1 2
1 2 6
2 2 7
2 3 7
The matrix P is:
1 5 0 4
0 2 6 0
0 0 7 7
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 0⏎
2 2 3⏎
0 0 4⏎
The COO matrix M is:
3
4
0 0 4
2 2 3
Input COO matrix N:
3⏎
4⏎
4⏎
0 1 5⏎
1 2 6⏎
2 3 7⏎
1 1 4⏎
The COO matrix N is:
3
4
0 1 5
1 1 4
1 2 6
2 3 7
The COO matrix P = M + N is:
3
4
0 0 4
0 1 5
1 1 4
1 2 6
2 2 3
2 3 7
The matrix P is:
4 5 0 0
0 4 6 0
0 0 3 7
$ ./a.out⏎
Input COO matrix M:
0⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
0⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
-1⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
3 3 4⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
0 3 4⏎
The COO matrix M is:
3
4
0 0 1
0 3 4
1 1 2
2 2 3
Input COO matrix N:
0⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
0 3 4⏎
The COO matrix M is:
3
4
0 0 1
0 3 4
1 1 2
2 2 3
Input COO matrix N:
3⏎
0⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
0 3 4⏎
The COO matrix M is:
3
4
0 0 1
0 3 4
1 1 2
2 2 3
Input COO matrix N:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
3 3 4⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
0 3 4⏎
The COO matrix M is:
3
4
0 0 1
0 3 4
1 1 2
2 2 3
Input COO matrix N:
4⏎
5⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
3 3 4⏎
The COO matrix N is:
4
5
0 0 1
1 1 2
2 2 3
3 3 4
Invalid matrix.
Pseudo Code
#include <iostream>
#include <vector>
typedef struct coo_sparse_matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector<unsigned long> row_ind;
std::vector<unsigned long> col_ind;
std::vector<long> val;
} coo_sparse_matrix;
typedef struct matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector<std::vector<long> > val;
} matrix;
void sort_col_ind(coo_sparse_matrix &coo_matrix)
{
for (i = 0; i < coo_matrix.col_ind.size(); i++)
{
for (j = i + 1; j < coo_matrix.col_ind.size(); j++)
{
if (coo_matrix.col_ind[i] > coo_matrix.col_ind[j])
{
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_row_ind(coo_sparse_matrix &coo_matrix)
{
for (i = 0; i < coo_matrix.row_ind.size(); i++)
{
for (j = i + 1; j < coo_matrix.row_ind.size(); j++)
{
if (coo_matrix.row_ind[i] > coo_matrix.row_ind[j] ||
(coo_matrix.row_ind[i] == coo_matrix.row_ind[j] &&
coo_matrix.col_ind[i] > coo_matrix.col_ind[j]))
{
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_coo_matrix(coo_sparse_matrix &coo_matrix)
{
sort_col_ind(coo_matrix);
sort_row_ind(coo_matrix);
}
void insert_coo_matrix_element(
coo_sparse_matrix &coo_matrix,
unsigned long row,
unsigned long col,
long val);
void add_coo_matrix_element(
coo_sparse_matrix &coo_matrix,
unsigned long row,
unsigned long col,
long val);
void parse_coo_matrix(coo_sparse_matrix &coo_matrix);
void print_coo_matrix(const coo_sparse_matrix &coo_matrix);
coo_sparse_matrix add_coo_matrix(
const coo_sparse_matrix &coo_matrix_M,
const coo_sparse_matrix &coo_matrix_N);
matrix convert_coo_matrix_to_matrix(const coo_sparse_matrix &coo_matrix);
void print_matrix(const matrix &matrix);
int main()
{
coo_sparse_matrix coo_matrix_M, coo_matrix_N, coo_matrix_P;
std::cout << "Input COO matrix M:" << std::endl;
parse_coo_matrix(coo_matrix_M);
std::cout << "The COO matrix M is:" << std::endl;
print_coo_matrix(coo_matrix_M);
std::cout << "Input COO matrix N:" << std::endl;
parse_coo_matrix(coo_matrix_N);
std::cout << "The COO matrix N is:" << std::endl;
print_coo_matrix(coo_matrix_N);
coo_matrix_P = add_coo_matrix(coo_matrix_M, coo_matrix_N);
std::cout << "The COO matrix P = M + N is:" << std::endl;
print_coo_matrix(coo_matrix_P);
matrix matrix_P = convert_coo_matrix_to_matrix(coo_matrix_P);
std::cout << "The matrix P is:" << std::endl;
print_matrix(matrix_P);
return 0;
}
Reference: Sparse matrix - Wikipedia
TA version:
#include <iostream>
#include <vector>
using namespace std;
typedef struct coo_sparse_matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector<unsigned long> row_ind;
std::vector<unsigned long> col_ind;
std::vector<long> val;
} coo_sparse_matrix;
typedef struct matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector< std::vector<long> > val;
} matrix;
void sort_col_ind(coo_sparse_matrix &coo_matrix)
{
for (int i = 0; i < coo_matrix.col_ind.size(); i++)
{
for (int j = i + 1; j < coo_matrix.col_ind.size(); j++)
{
if (coo_matrix.col_ind[i] > coo_matrix.col_ind[j])
{
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_row_ind(coo_sparse_matrix &coo_matrix)
{
for (int i = 0; i < coo_matrix.row_ind.size(); i++)
{
for (int j = i + 1; j < coo_matrix.row_ind.size(); j++)
{
if (coo_matrix.row_ind[i] > coo_matrix.row_ind[j] ||
(coo_matrix.row_ind[i] == coo_matrix.row_ind[j] &&
coo_matrix.col_ind[i] > coo_matrix.col_ind[j]))
{
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_coo_matrix(coo_sparse_matrix &coo_matrix)
{
sort_col_ind(coo_matrix);
sort_row_ind(coo_matrix);
}
void insert_coo_matrix_element(
coo_sparse_matrix &coo_matrix,
unsigned long row,
unsigned long col,
long val);
void add_coo_matrix_element(
coo_sparse_matrix &coo_matrix,
unsigned long row,
unsigned long col,
long val);
void parse_coo_matrix(coo_sparse_matrix &coo_matrix);
void print_coo_matrix(const coo_sparse_matrix &coo_matrix);
coo_sparse_matrix add_coo_matrix(
const coo_sparse_matrix &coo_matrix_M,
const coo_sparse_matrix &coo_matrix_N);
matrix convert_coo_matrix_to_matrix(const coo_sparse_matrix &coo_matrix);
void print_matrix(const matrix &matrix);
int main()
{
coo_sparse_matrix coo_matrix_M, coo_matrix_N, coo_matrix_P;
std::cout << "Input COO matrix M:" << std::endl;
parse_coo_matrix(coo_matrix_M);
std::cout << "The COO matrix M is:" << std::endl;
print_coo_matrix(coo_matrix_M);
std::cout << "Input COO matrix N:" << std::endl;
parse_coo_matrix(coo_matrix_N);
std::cout << "The COO matrix N is:" << std::endl;
print_coo_matrix(coo_matrix_N);
coo_matrix_P = add_coo_matrix(coo_matrix_M, coo_matrix_N);
std::cout << "The COO matrix P = M + N is:" << std::endl;
print_coo_matrix(coo_matrix_P);
matrix matrix_P = convert_coo_matrix_to_matrix(coo_matrix_P);
std::cout << "The matrix P is:" << std::endl;
print_matrix(matrix_P);
return 0;
}
void parse_coo_matrix(coo_sparse_matrix &coo_matrix)
{
int n; // n lines of inputs
// Exceptions: rows and col can't be nonpositive, n can't be negative
for (int i = 0; i < 3; i++)
{
int temp;
cin >> temp;
if (i == 0 && temp > 0)
{
coo_matrix.rows = temp;
continue;
}
if (i == 1 && temp > 0)
{
coo_matrix.cols = temp;
continue;
}
if (i == 2 && temp >= 0)
{
n = temp;
continue;
}
cout << "Invalid matrix." << endl;
exit(0);
}
// Start of Parsing
while (n--)
{
unsigned long r, c;
long v;
cin >> r >> c >> v;
// Exceptions: Index Out of Range
if (r < 0 ||
r >= coo_matrix.rows ||
c < 0 ||
c >= coo_matrix.cols)
{
cout << "Invalid matrix." << endl;
exit(0);
}
// Store COO
bool flag = false;
for (int i = 0; i < coo_matrix.row_ind.size(); i++)
{
// Rewrite val if already exist
if (r == coo_matrix.row_ind[i] && c == coo_matrix.col_ind[i])
{
if (v == 0) // Erase if val = 0
{
coo_matrix.row_ind.erase(coo_matrix.row_ind.begin() + i);
coo_matrix.col_ind.erase(coo_matrix.col_ind.begin() + i);
coo_matrix.val.erase(coo_matrix.val.begin() + i);
}
else // Rewrite if val != 0
coo_matrix.val[i] = v;
flag = true;
break;
}
}
// Push_back if not exists yet and v is nonzero
if (!flag && v != 0)
{
coo_matrix.row_ind.push_back(r);
coo_matrix.col_ind.push_back(c);
coo_matrix.val.push_back(v);
}
}
// End of Parsing
// Sorting
sort_coo_matrix(coo_matrix);
}
matrix convert_coo_matrix_to_matrix(const coo_sparse_matrix &coo_matrix)
{
matrix mtrx;
mtrx.rows = coo_matrix.rows;
mtrx.cols = coo_matrix.cols;
for (unsigned long i = 0; i < mtrx.rows; i++)
mtrx.val.push_back(vector<long>(mtrx.cols, 0));
for (int i = 0; i < coo_matrix.row_ind.size(); i++)
{
unsigned long r = coo_matrix.row_ind[i], c = coo_matrix.col_ind[i];
long v = coo_matrix.val[i];
mtrx.val[r][c] = v;
}
return mtrx;
}
void print_matrix(const matrix &matrix)
{
for (unsigned long i = 0; i < matrix.rows; i++)
{
for (unsigned long j = 0; j < matrix.cols; j++)
{
cout << matrix.val[i][j] << ' ';
}
cout << endl;
}
}
void print_coo_matrix(const coo_sparse_matrix &coo_matrix)
{
cout << coo_matrix.rows << '\n'
<< coo_matrix.cols << endl;
for (unsigned long i = 0; i < coo_matrix.row_ind.size(); i++)
{
cout << coo_matrix.row_ind[i]
<< " "
<< coo_matrix.col_ind[i]
<< " "
<< coo_matrix.val[i]
<< endl;
}
}
void insert_coo_matrix_element(
coo_sparse_matrix &coo_matrix,
unsigned long row,
unsigned long col,
long val)
{
coo_matrix.row_ind.push_back(row);
coo_matrix.col_ind.push_back(col);
coo_matrix.val.push_back(val);
}
void add_coo_matrix_element(
coo_sparse_matrix &coo_matrix,
unsigned long row,
unsigned long col,
long val);
coo_sparse_matrix add_coo_matrix(
const coo_sparse_matrix &coo_matrix_M,
const coo_sparse_matrix &coo_matrix_N)
{
// Exception: Matrices are not addable
if (coo_matrix_M.rows != coo_matrix_N.rows ||
coo_matrix_M.cols != coo_matrix_N.cols)
{
cout << "Invalid matrix." << endl;
exit(0);
}
coo_sparse_matrix P;
P.rows = coo_matrix_M.rows;
P.cols = coo_matrix_M.cols;
int m = 0, n = 0;
while (m < coo_matrix_M.row_ind.size() &&
n < coo_matrix_N.row_ind.size())
{
if (coo_matrix_M.row_ind[m] > coo_matrix_N.row_ind[n] ||
(coo_matrix_M.row_ind[m] == coo_matrix_N.row_ind[n] &&
coo_matrix_M.col_ind[m] > coo_matrix_N.col_ind[n]))
{
insert_coo_matrix_element(
P,
coo_matrix_N.row_ind[n],
coo_matrix_N.col_ind[n],
coo_matrix_N.val[n]);
n++;
}
else if (coo_matrix_M.row_ind[m] < coo_matrix_N.row_ind[n] ||
(coo_matrix_M.row_ind[m] == coo_matrix_N.row_ind[n] &&
coo_matrix_M.col_ind[m] < coo_matrix_N.col_ind[n]))
{
insert_coo_matrix_element(
P,
coo_matrix_M.row_ind[m],
coo_matrix_M.col_ind[m],
coo_matrix_M.val[m]);
m++;
}
else
{
int sum = coo_matrix_M.val[m] + coo_matrix_N.val[n];
if (sum != 0)
insert_coo_matrix_element(
P,
coo_matrix_N.row_ind[n],
coo_matrix_N.col_ind[n],
sum);
m++;
n++;
}
}
// Insert the Remaining COO
while (m < coo_matrix_M.row_ind.size())
{
insert_coo_matrix_element(
P,
coo_matrix_M.row_ind[m],
coo_matrix_M.col_ind[m],
coo_matrix_M.val[m]);
m++;
}
while (n < coo_matrix_N.row_ind.size())
{
insert_coo_matrix_element(
P,
coo_matrix_N.row_ind[n],
coo_matrix_N.col_ind[n],
coo_matrix_N.val[n]);
n++;
}
sort_coo_matrix(P);
return P;
}
Lab 14-3: COO Sparse Matrix 相乘 (20%)
- 輸入:以整數 (符合
long
型態) 的兩矩陣 \(M\) 及 \(N\)- 矩陣 \(M\) 及 \(N\) 的維度為正整數 \(M_n\)、\(M_m\)、\(N_n\)、\(N_m\)。
- 矩陣的起始位置為 \(M[0][0]\)、\(N[0][0]\)。
- 欲輸入的元素的個數為 \(M_{elements}\)、\(N_{elements}\)。
- 元素紀錄的方式為
(i, j, v)
。 - 紀錄格式參考以下的範例。
- 輸出:顯示使用者輸入的兩 Sparse Matrix 及 \(P = M \times N\) 結果的 Sparse Matrix & 展開的 Matrix。
- 矩陣 \(P\) 的維度 \(P_n\) 與 \(P_m\)。
- 非
0
元素(i, j, v)
。- 需以
(0, 0), (0, 1), ... (0, n-1), (1, 0), ... (n-1, m-1)
的順序顯示。
- 需以
- 展開 matrix 輸出格式參考以下的範例。
- 檔名:lab14_3_<學號>.cpp (e.g. lab14_3_106062802.cpp)
Notice:
- 程式需提示輸入 Sparse Matrix \(M\) 及 \(N\),程式需分析輸入後顯示使用者輸入的矩陣資訊。
- 計算完 \(P\) 後,顯示矩陣 \(P\),及展開後的 \(P\)。
- 程式需檢查輸入的資料是否為合法矩陣,若不相符則顯示錯誤訊息
Invalid matrix.
並結束程式。- 若矩陣維度為非正整數,則該矩陣為非法矩陣。
- 若欲輸入的元素的個數為負整數,則該矩陣為非法矩陣。
- 如元素的索引值大於矩陣的維度則不算為合法元素。
- 若元素的索引值重複,則以最後一筆紀錄為主。
- 若元素的值為
0
則忽略不紀錄。
- 程式需檢查矩陣 \(N_m\) 的維度是否與矩陣 \(M_n\) 相同,若不相同則在輸入完矩陣 \(N\) 時顯示錯誤訊息
Invalid matrix.
並結束程式。 - 程式需使用 function 來處理輸入與輸出、比較、運算。
- 程式需在 30 秒之內執行完畢,所有測資皆不會超過 30 秒的執行時間。
Format
Input COO matrix M:
<matrix rows>⏎
<matrix columns>⏎
<number of input elements>⏎
<element index> <element index> <element value>⏎
<element index> <element index> <element value>⏎
...⏎
The COO matrix M is:
<matrix rows>
<matrix columns>
<element index> <element index> <element value>
<element index> <element index> <element value>
...
Input COO matrix N:
<matrix rows>⏎
<matrix columns>⏎
<number of input elements>⏎
<element index> <element index> <element value>⏎
<element index> <element index> <element value>⏎
...⏎
The COO matrix N is:
<matrix rows>
<matrix columns>
<element index> <element index> <element value>
<element index> <element index> <element value>
...
The COO matrix P = M * N is:
<matrix rows>
<matrix columns>
<element index> <element index> <element value>
<element index> <element index> <element value>
...
The matrix P is:
<P0_0> <P0_1> ... <P0_n>
<P1_0> <P1_1> ... <P1_n>
...
<Pm_0> <Pm_1> ... <Pm_n>
Example
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
0 3 4⏎
The COO matrix M is:
3
4
0 0 1
0 3 4
1 1 2
2 2 3
Input COO matrix N:
4⏎
3⏎
4⏎
0 1 5⏎
1 2 6⏎
2 2 7⏎
3 2 4⏎
The COO matrix N is:
4
3
0 1 5
1 2 6
2 2 7
3 2 4
The COO matrix P = M * N is:
3
3
0 1 5
0 2 16
1 2 12
2 2 21
The matrix P is:
0 5 16
0 0 12
0 0 21
$ ./a.out⏎
Input COO matrix M:
4⏎
3⏎
4⏎
0 0 1⏎
1 1 0⏎
2 2 3⏎
0 0 4⏎
The COO matrix M is:
4
3
0 0 4
2 2 3
Input COO matrix N:
3⏎
5⏎
4⏎
0 1 5⏎
1 2 6⏎
2 3 7⏎
1 1 4⏎
The COO matrix N is:
3
5
0 1 5
1 1 4
1 2 6
2 3 7
The COO matrix P = M * N is:
4
5
0 1 20
2 3 21
The matrix P is:
0 20 0 0 0
0 0 0 0 0
0 0 0 21 0
0 0 0 0 0
$ ./a.out⏎
Input COO matrix M:
0⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
0⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
-1⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
3 3 4⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
0 3 4⏎
The COO matrix M is:
3
4
0 0 1
0 3 4
1 1 2
2 2 3
Input COO matrix N:
0⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
0 3 4⏎
The COO matrix M is:
3
4
0 0 1
0 3 4
1 1 2
2 2 3
Input COO matrix N:
3⏎
0⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
0 3 4⏎
The COO matrix M is:
3
4
0 0 1
0 3 4
1 1 2
2 2 3
Input COO matrix N:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
3 3 4⏎
Invalid matrix.
$ ./a.out⏎
Input COO matrix M:
3⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
0 3 4⏎
The COO matrix M is:
3
4
0 0 1
0 3 4
1 1 2
2 2 3
Input COO matrix N:
5⏎
4⏎
4⏎
0 0 1⏎
1 1 2⏎
2 2 3⏎
3 3 4⏎
The COO matrix N is:
5
4
0 0 1
1 1 2
2 2 3
3 3 4
Invalid matrix.
Pseudo Code
#include <iostream>
#include <vector>
typedef struct coo_sparse_matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector<unsigned long> row_ind;
std::vector<unsigned long> col_ind;
std::vector<long> val;
} coo_sparse_matrix;
typedef struct matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector<std::vector<long> > val;
} matrix;
void sort_col_ind(coo_sparse_matrix &coo_matrix)
{
for (i = 0; i < coo_matrix.col_ind.size(); i++)
{
for (j = i + 1; j < coo_matrix.col_ind.size(); j++)
{
if (coo_matrix.col_ind[i] > coo_matrix.col_ind[j])
{
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_row_ind(coo_sparse_matrix &coo_matrix)
{
for (i = 0; i < coo_matrix.row_ind.size(); i++)
{
for (j = i + 1; j < coo_matrix.row_ind.size(); j++)
{
if (coo_matrix.row_ind[i] > coo_matrix.row_ind[j] ||
(coo_matrix.row_ind[i] == coo_matrix.row_ind[j] &&
coo_matrix.col_ind[i] > coo_matrix.col_ind[j]))
{
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_coo_matrix(coo_sparse_matrix &coo_matrix)
{
sort_col_ind(coo_matrix);
sort_row_ind(coo_matrix);
}
void parse_coo_matrix(coo_sparse_matrix &coo_matrix);
void print_coo_matrix(const coo_sparse_matrix &coo_matrix);
coo_sparse_matrix mult_coo_matrix(
const coo_sparse_matrix &coo_matrix_M,
const coo_sparse_matrix &coo_matrix_N);
matrix convert_coo_matrix_to_matrix(const coo_sparse_matrix &coo_matrix);
void print_matrix(const matrix &matrix);
int main()
{
coo_sparse_matrix coo_matrix_M, coo_matrix_N, coo_matrix_P;
std::cout << "Input COO matrix M:" << std::endl;
parse_coo_matrix(coo_matrix_M);
std::cout << "The COO matrix M is:" << std::endl;
print_coo_matrix(coo_matrix_M);
std::cout << "Input COO matrix N:" << std::endl;
parse_coo_matrix(coo_matrix_N);
std::cout << "The COO matrix N is:" << std::endl;
print_coo_matrix(coo_matrix_N);
coo_matrix_P = mult_coo_matrix(coo_matrix_M, coo_matrix_N);
std::cout << "The COO matrix P = M * N is:" << std::endl;
print_coo_matrix(coo_matrix_P);
matrix matrix_P = convert_coo_matrix_to_matrix(coo_matrix_P);
std::cout << "The matrix P is:" << std::endl;
print_matrix(matrix_P);
return 0;
}
Reference: Sparse matrix - Wikipedia
TA version:
#include <iostream>
#include <vector>
using namespace std;
typedef struct coo_sparse_matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector<unsigned long> row_ind;
std::vector<unsigned long> col_ind;
std::vector<long> val;
} coo_sparse_matrix;
typedef struct matrix_struct
{
unsigned long rows;
unsigned long cols;
std::vector< std::vector<long> > val;
} matrix;
void sort_col_ind(coo_sparse_matrix &coo_matrix)
{
for (int i = 0; i < coo_matrix.col_ind.size(); i++)
{
for (int j = i + 1; j < coo_matrix.col_ind.size(); j++)
{
if (coo_matrix.col_ind[i] > coo_matrix.col_ind[j])
{
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_row_ind(coo_sparse_matrix &coo_matrix)
{
for (int i = 0; i < coo_matrix.row_ind.size(); i++)
{
for (int j = i + 1; j < coo_matrix.row_ind.size(); j++)
{
if (coo_matrix.row_ind[i] > coo_matrix.row_ind[j] ||
(coo_matrix.row_ind[i] == coo_matrix.row_ind[j] &&
coo_matrix.col_ind[i] > coo_matrix.col_ind[j]))
{
swap(coo_matrix.row_ind[i], coo_matrix.row_ind[j]);
swap(coo_matrix.col_ind[i], coo_matrix.col_ind[j]);
swap(coo_matrix.val[i], coo_matrix.val[j]);
}
}
}
}
void sort_coo_matrix(coo_sparse_matrix &coo_matrix)
{
sort_col_ind(coo_matrix);
sort_row_ind(coo_matrix);
}
void parse_coo_matrix(coo_sparse_matrix &coo_matrix);
void print_coo_matrix(const coo_sparse_matrix &coo_matrix);
coo_sparse_matrix mult_coo_matrix(
const coo_sparse_matrix &coo_matrix_M,
const coo_sparse_matrix &coo_matrix_N);
matrix convert_coo_matrix_to_matrix(const coo_sparse_matrix &coo_matrix);
void print_matrix(const matrix &matrix);
int main()
{
coo_sparse_matrix coo_matrix_M, coo_matrix_N, coo_matrix_P;
std::cout << "Input COO matrix M:" << std::endl;
parse_coo_matrix(coo_matrix_M);
std::cout << "The COO matrix M is:" << std::endl;
print_coo_matrix(coo_matrix_M);
std::cout << "Input COO matrix N:" << std::endl;
parse_coo_matrix(coo_matrix_N);
std::cout << "The COO matrix N is:" << std::endl;
print_coo_matrix(coo_matrix_N);
coo_matrix_P = mult_coo_matrix(coo_matrix_M, coo_matrix_N);
std::cout << "The COO matrix P = M * N is:" << std::endl;
print_coo_matrix(coo_matrix_P);
matrix matrix_P = convert_coo_matrix_to_matrix(coo_matrix_P);
std::cout << "The matrix P is:" << std::endl;
print_matrix(matrix_P);
return 0;
}
void parse_coo_matrix(coo_sparse_matrix &coo_matrix)
{
int n; // n lines of inputs
/// Exceptions: rows and col can't be nonpositive, n can't be negative
for (int i = 0; i < 3; i++)
{
int temp;
cin >> temp;
if (i == 0 && temp > 0)
{
coo_matrix.rows = temp;
continue;
}
if (i == 1 && temp > 0)
{
coo_matrix.cols = temp;
continue;
}
if (i == 2 && temp >= 0)
{
n = temp;
continue;
}
cout << "Invalid matrix." << endl;
exit(0);
}
// Start of Parsing
while (n--)
{
unsigned long r, c;
long v;
cin >> r >> c >> v;
// Exceptions: Index Out of Range
if (r < 0 ||
r >= coo_matrix.rows ||
c < 0 ||
c >= coo_matrix.cols)
{
cout << "Invalid matrix." << endl;
exit(0);
}
// Store COO
bool flag = false;
for (int i = 0; i < coo_matrix.row_ind.size(); i++)
{
// Rewrite val if already exist
if (r == coo_matrix.row_ind[i] && c == coo_matrix.col_ind[i])
{
if (v == 0) // Erase if val = 0
{
coo_matrix.row_ind.erase(coo_matrix.row_ind.begin() + i);
coo_matrix.col_ind.erase(coo_matrix.col_ind.begin() + i);
coo_matrix.val.erase(coo_matrix.val.begin() + i);
}
else // Rewrite if val != 0
coo_matrix.val[i] = v;
flag = true;
break;
}
}
// Push_back if not exists yet
if (!flag && v != 0)
{
coo_matrix.row_ind.push_back(r);
coo_matrix.col_ind.push_back(c);
coo_matrix.val.push_back(v);
}
}
// End of Parsing
// Sorting
sort_coo_matrix(coo_matrix);
}
matrix convert_coo_matrix_to_matrix(const coo_sparse_matrix &coo_matrix)
{
matrix mtrx;
mtrx.rows = coo_matrix.rows;
mtrx.cols = coo_matrix.cols;
for (unsigned long i = 0; i < mtrx.rows; i++)
mtrx.val.push_back(vector<long>(mtrx.cols, 0));
for (int i = 0; i < coo_matrix.row_ind.size(); i++)
{
unsigned long r = coo_matrix.row_ind[i], c = coo_matrix.col_ind[i];
long v = coo_matrix.val[i];
mtrx.val[r][c] = v;
}
return mtrx;
}
void print_matrix(const matrix &matrix)
{
for (unsigned long i = 0; i < matrix.rows; i++)
{
for (unsigned long j = 0; j < matrix.cols; j++)
{
cout << matrix.val[i][j] << ' ';
}
cout << endl;
}
}
void print_coo_matrix(const coo_sparse_matrix &coo_matrix)
{
cout << coo_matrix.rows << '\n'
<< coo_matrix.cols << endl;
for (unsigned long i = 0; i < coo_matrix.row_ind.size(); i++)
{
cout << coo_matrix.row_ind[i]
<< " "
<< coo_matrix.col_ind[i]
<< " "
<< coo_matrix.val[i]
<< endl;
}
}
void insert_coo_matrix_element(
coo_sparse_matrix &coo_matrix,
unsigned long row,
unsigned long col,
long val)
{
coo_matrix.row_ind.push_back(row);
coo_matrix.col_ind.push_back(col);
coo_matrix.val.push_back(val);
}
coo_sparse_matrix mult_coo_matrix(
const coo_sparse_matrix &coo_matrix_M,
const coo_sparse_matrix &coo_matrix_N)
{
// Exception: Matrices are not multipliable
if (coo_matrix_M.cols != coo_matrix_N.rows)
{
cout << "Invalid matrix." << endl;
exit(0);
}
coo_sparse_matrix P;
P.rows = coo_matrix_M.rows;
P.cols = coo_matrix_N.cols;
// Iterate over all COO of M
int m, n;
for (m = 0; m < coo_matrix_M.row_ind.size();)
{
// Current row of P
int r = coo_matrix_M.row_ind[m];
// Iterate over all COO of N
for (n = 0; n < coo_matrix_N.row_ind.size();)
{
// current column of P
int c = coo_matrix_N.col_ind[n];
// Computing the (i, j) entry of P
int i = m, j = n, sum = 0;
while (i < coo_matrix_M.row_ind.size() &&
coo_matrix_M.row_ind[i] == r &&
j < coo_matrix_N.row_ind.size() &&
coo_matrix_N.col_ind[j] == c)
{
if (coo_matrix_M.col_ind[i] < coo_matrix_N.row_ind[j])
i++;
else if (coo_matrix_M.col_ind[i] > coo_matrix_N.row_ind[j])
j++;
else
sum += coo_matrix_M.val[i++] * coo_matrix_N.val[j++];
}
// Insert COO if sum is nonzero
if (sum != 0)
insert_coo_matrix_element(P, r, c, sum);
// To the next column of P
while (n < coo_matrix_N.row_ind.size() && coo_matrix_N.col_ind[n] == c)
n++;
}
// To the next row of P
while (m < coo_matrix_M.row_ind.size() && coo_matrix_M.row_ind[m] == r)
m++;
}
return P;
}
Lecture 15: Github Tutorial
Credit: Github Documention
Hello World
Creating a repository
A repository is usually used to organize a single project. Repositories can contain folders and files, images, videos, spreadsheets, and data sets -- anything your project needs. Often, repositories include a README
file, a file with information about your project. GitHub makes it easy to add one at the same time you create your new repository. It also offers other common options such as a license file.
Your hello-world
repository can be a place where you store ideas, resources, or even share and discuss things with others.
-
In the upper-right corner of any page, use the
drop-down menu, and select New repository.
-
In the Repository name box, enter
hello-world
. -
In the Description box, write a short description.
-
Select Add a README file.
-
Click Create repository.
Creating a branch
Branching lets you have different versions of a repository at one time.
By default, your repository has one branch named main
that is considered to be the definitive branch. You can use branches to experiment and make edits before committing them to main
.
When you create a branch off the main
branch, you're making a copy, or snapshot, of main
as it was at that point in time. If someone else made changes to the main
branch while you were working on your branch, you could pull in those updates.
This diagram shows:
- The
main
branch - A new branch called
feature
- The journey that
feature
takes before it's merged intomain
Have you ever saved different versions of a file? Something like:
story.txt
story-joe-edit.txt
story-joe-edit-reviewed.txt
Branches accomplish similar goals in GitHub repositories.
Here at GitHub, our developers, writers, and designers use branches for keeping bug fixes and feature work separate from our main
(production) branch. When a change is ready, they merge their branch into main
.
Create a branch
- Click the Code tab of your
hello-world
repository. - Click the drop down at the top of the file list that says main.
- Type a branch name,
readme-edits
, into the text box. - Click Create branch: readme-edits from main.
Now you have two branches, main
and readme-edits
. Right now, they look exactly the same. Next you'll add changes to the new branch.
Making and committing changes
When you created a new branch in the previous step, GitHub brought you to the code page for your new readme-edits
branch, which is a copy of main
.
You can make and save changes to the files in your repository. On GitHub, saved changes are called commits. Each commit has an associated commit message, which is a description explaining why a particular change was made. Commit messages capture the history of your changes so that other contributors can understand what you’ve done and why.
-
Click the
README.md
file. -
Click
to edit the file.
-
In the editor, write a bit about yourself.
-
In the Commit changes box, write a commit message that describes your changes.
-
Click Commit changes.
These changes will be made only to the README file on your readme-edits
branch, so now this branch contains content that's different from main
.
Opening a pull request
Now that you have changes in a branch off of main
, you can open a pull request.
Pull requests are the heart of collaboration on GitHub. When you open a pull request, you're proposing your changes and requesting that someone review and pull in your contribution and merge them into their branch. Pull requests show diffs, or differences, of the content from both branches. The changes, additions, and subtractions are shown in different colors.
As soon as you make a commit, you can open a pull request and start a discussion, even before the code is finished.
By using GitHub's @mention
feature in your pull request message, you can ask for feedback from specific people or teams, whether they're down the hall or 10 time zones away.
You can even open pull requests in your own repository and merge them yourself. It's a great way to learn the GitHub flow before working on larger projects.
-
Click the Pull requests tab of your
hello-world
repository. -
Click New pull request
-
In the Example Comparisons box, select the branch you made,
readme-edits
, to compare withmain
(the original). -
Look over your changes in the diffs on the Compare page, make sure they're what you want to submit.
-
Click Create pull request.
-
Give your pull request a title and write a brief description of your changes. You can include emojis and drag and drop images and gifs.
-
Click Create pull request.
Your collaborators can now review your edits and make suggestions.
Merging your pull request
In this final step, you will merge your readme-edits
branch into the main
branch.
- Click Merge pull request to merge the changes into
main
. - Click Confirm merge.
- Go ahead and delete the branch, since its changes have been incorporated, by clicking Delete branch.
Forking
Forking a repository
You might fork a project to propose changes to the upstream, or original, repository. In this case, it's good practice to regularly sync your fork with the upstream repository. To do this, you'll need to use Git on the command line. You can practice setting the upstream repository using the same octocat/Spoon-Knife repository you just forked.
- On GitHub.com, navigate to the octocat/Spoon-Knife repository.
- In the top-right corner of the page, click Fork.
Cloning your forked repository
Right now, you have a fork of the Spoon-Knife repository, but you don't have the files in that repository locally on your computer.
- On GitHub.com, navigate to your fork of the Spoon-Knife repository.
- Above the list of files, click
Code.
- To clone the repository using HTTPS, under "Clone with HTTPS", click
. To clone the repository using an SSH key, including a certificate issued by your organization's SSH certificate authority, click Use SSH, then click
. To clone a repository using GitHub CLI, click Use GitHub CLI, then click
.
- Open Terminal.
- Change the current working directory to the location where you want the cloned directory.
- Type
git clone
, and then paste the URL you copied earlier. It will look like this, with your GitHub username instead ofYOUR-USERNAME
:
$ git clone https://github.com/YOUR-USERNAME/Spoon-Knife
- Press Enter. Your local clone will be created.
$ git clone https://github.com/YOUR-USERNAME/Spoon-Knife
> Cloning into `Spoon-Knife`...
> remote: Counting objects: 10, done.
> remote: Compressing objects: 100% (8/8), done.
> remove: Total 10 (delta 1), reused 10 (delta 1)
> Unpacking objects: 100% (10/10), done.
Configuring Git to sync your fork with the original repository
When you fork a project in order to propose changes to the original repository, you can configure Git to pull changes from the original, or upstream, repository into the local clone of your fork.
- On GitHub.com, navigate to the octocat/Spoon-Knife repository.
- Above the list of files, click
Code.
- To clone the repository using HTTPS, under "Clone with HTTPS", click
. To clone the repository using an SSH key, including a certificate issued by your organization's SSH certificate authority, click Use SSH, then click
. To clone a repository using GitHub CLI, click Use GitHub CLI, then click
.
- Open Terminal.
- Change directories to the location of the fork you cloned.
- To go to your home directory, type just
cd
with no other text. - To list the files and folders in your current directory, type
ls
. - To go into one of your listed directories, type
cd your_listed_directory
. - To go up one directory, type
cd ..
.
- To go to your home directory, type just
- Type
git remote -v
and press Enter. You'll see the current configured remote repository for your fork.
$ git remote -v
> origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch)
> origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (push)
- Type
git remote add upstream
, and then paste the URL you copied in Step 2 and press Enter. It will look like this:
$ git remote add upstream https://github.com/octocat/Spoon-Knife.git
- To verify the new upstream repository you've specified for your fork, type
git remote -v
again. You should see the URL for your fork asorigin
, and the URL for the original repository asupstream
.
$ git remote -v
> origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch)
> origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (push)
> upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git (fetch)
> upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git (push)
Now, you can keep your fork synced with the upstream repository with a few Git commands. For more information, see "Syncing a fork."
Following GitHub flow
Create a branch
Create a branch in your repository. A short, descriptive branch name enables your collaborators to see ongoing work at a glance. For example, increase-test-timeout
or add-code-of-conduct
. For more information, see "Creating and deleting branches within your repository."
By creating a branch, you create a space to work without affecting the default branch. Additionally, you give collaborators a chance to review your work.
Make changes
On your branch, make any desired changes to the repository. For more information, see "Creating new files," "Editing files," "Renaming a file," "Moving a file to a new location," or "Deleting files in a repository."
Your branch is a safe place to make changes. If you make a mistake, you can revert your changes or push additional changes to fix the mistake. Your changes will not end up on the default branch until you merge your branch.
Commit and push your changes to your branch. Give each commit a descriptive message to help you and future contributors understand what changes the commit contains. For example, fix typo
or increase rate limit
.
Ideally, each commit contains an isolated, complete change. This makes it easy to revert your changes if you decide to take a different approach. For example, if you want to rename a variable and add some tests, put the variable rename in one commit and the tests in another commit. Later, if you want to keep the tests but revert the variable rename, you can revert the specific commit that contained the variable rename. If you put the variable rename and tests in the same commit or spread the variable rename across multiple commits, you would spend more effort reverting your changes.
By committing and pushing your changes, you back up your work to remote storage. This means that you can access your work from any device. It also means that your collaborators can see your work, answer questions, and make suggestions or contributions.
Continue to make, commit, and push changes to your branch until you are ready to ask for feedback.
Tip: Make a separate branch for each set of unrelated changes. This makes it easier for reviewers to give feedback. It also makes it easier for you and future collaborators to understand the changes and to revert or build on them. Additionally, if there is a delay in one set of changes, your other changes aren't also delayed.
Create a pull request
Create a pull request to ask collaborators for feedback on your changes. Pull request review is so valuable that some repositories require an approving review before pull requests can be merged. If you want early feedback or advice before you complete your changes, you can mark your pull request as a draft. For more information, see "Creating a pull request."
When you create a pull request, include a summary of the changes and what problem they solve. You can include images, links, and tables to help convey this information. If your pull request addresses an issue, link the issue so that issue stakeholders are aware of the pull request and vice versa. If you link with a keyword, the issue will close automatically when the pull request merges. For more information, see "Basic writing and formatting syntax" and "Linking a pull request to an issue."
In addition to filling out the body of the pull request, you can add comments to specific lines of the pull request to explicitly point something out to the reviewers.
Your repository may be configured to automatically request a review from specific teams or users when a pull request is created. You can also manually @mention or request a review from specific people or teams.
If your repository has checks configured to run on pull requests, you will see any checks that failed on your pull request. This helps you catch errors before merging your branch. For more information, see "About status checks."
Address review comments
Reviewers should leave questions, comments, and suggestions. Reviewers can comment on the whole pull request or add comments to specific lines. You and reviewers can insert images or code suggestions to clarify comments. For more information, see "Reviewing changes in pull requests."
You can continue to commit and push changes in response to the reviews. Your pull request will update automatically.
Merge your pull request
Once your pull request is approved, merge your pull request. This will automatically merge your branch so that your changes appear on the default branch. GitHub retains the history of comments and commits in the pull request to help future contributors understand your changes. For more information, see "Merging a pull request."
GitHub will tell you if your pull request has conflicts that must be resolved before merging. For more information, see "Addressing merge conflicts."
Branch protection settings may block merging if your pull request does not meet certain requirements. For example, you need a certain number of approving reviews or an approving review from a specific team. For more information, see "About protected branches."
Delete your branch
After you merge your pull request, delete your branch. This indicates that the work on the branch is complete and prevents you or others from accidentally using old branches. For more information, see "Deleting and restoring branches in a pull request."
Don't worry about losing information. Your pull request and commit history will not be deleted. You can always restore your deleted branch or revert your pull request if needed.
Which discussion tool should I use?
Scenarios for issues
- I want to keep track of tasks, enhancements and bugs.
- I want to file a bug report.
- I want to share feedback about a specific feature.
- I want to ask a question about files in the repository.
Issue example
This example illustrates how a GitHub user created an issue in our documentation open source repository to make us aware of a bug, and discuss a fix.
- A user noticed that the blue color of the banner at the top of the page in the Chinese version of the GitHub Docs makes the text in the banner unreadable.
- The user created an issue in the repository, stating the problem and suggesting a fix (which is, use a different background color for the banner).
- A discussion ensues, and eventually, a consensus will be reached about the fix to apply.
- A contributor can then create a pull request with the fix.
Scenarios for pull requests
- I want to fix a typo in a repository.
- I want to make changes to a repository.
- I want to make changes to fix an issue.
- I want to comment on changes suggested by others.
Pull request example
This example illustrates how a GitHub user created a pull request in our documentation open source repository to fix a typo.
In the Conversation tab of the pull request, the author explains why they created the pull request.
The Files changed tab of the pull request shows the implemented fix.
- This contributor notices a typo in the repository.
- The user creates a pull request with the fix.
- A repository maintainer reviews the pull request, comments on it, and merges it.
Scenarios for GitHub Discussions
- I have a question that's not necessarily related to specific files in the repository.
- I want to share news with my collaborators, or my team.
- I want to start or participate in an open-ended conversation.
- I want to make an announcement to my community.
GitHub Discussions example
This example shows the GitHub Discussions welcome post for the GitHub Docs open source repository, and illustrates how the team wants to collaborate with their community.
This community maintainer started a discussion to welcome the community, and to ask members to introduce themselves. This post fosters an inviting atmosphere for visitors and contributors. The post also clarifies that the team's happy to help with contributions to the repository.
Scenarios for team discussions
- I have a question that's not necessarily related to specific files in the repository.
- I want to share news with my collaborators, or my team.
- I want to start or participate in an open-ended conversation.
- I want to make an announcement to my team.
As you can see, team discussions are very similar to GitHub Discussions. For GitHub.com, we recommend using GitHub Discussions as the starting point for conversations. You can use GitHub Discussions to collaborate with any community on GitHub. If you are part of an organization, and would like to initiate conversations within your organization or team within that organization, you should use team discussions.
Team discussion example
This example shows a team post for the octo-team
team.
The octocat
team member posted a team discussion, informing the team of various things:
- A team member called Mona started remote game events.
- There is a blog post describing how the teams use GitHub Actions to produce their docs.
- Material about the April All Hands is now available for all team members to view.
Lecture 16: GoogleTest Tutorial
Credit: GoogleTest from Google, CNOCycle/cpp_tutorial by E. Chen
Installation
以下為安裝 GoogleTest 的懶人包,詳細安裝方式請參考 Quickstart: Building with CMake
Download GoogleTest
- 解壓縮到你的 vscode 目錄下,並打開 vscode
- 可以在
Explorer
內右鍵,選擇Reveal in File Explorer
或Reveal in Finder
來打開現在 vscode 的目錄
- 可以在
(Optional) Install CMake
如果自己的電腦上沒有安裝 CMake,可以從 Download | CMake 下載自己電腦的安裝檔後安裝。
- 若是 windows,要記得選 Add CMake to the system PATH for all users ,並重新開啟 vscode,才能讓 vscode 的 terminal 讀到 cmake 指令。
Build GoogleTest
打開 vscode 的 Terminal,輸入以下指令,將 GoogleTest 安裝到你的 vscode 目錄下
Windows (mingw)
cd googletest-release-1.11.0
mkdir build
cd build
cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX="../../gtest" ..
mingw32-make -j
mingw32-make -j install
Linux & WSL
sudo apt update
sudo apt install -y cmake
cd googletest-release-1.11.0
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../../gtest ..
make -j
make install
macOS
- Install brew first here
brew update
brew install cmake
cd googletest-release-1.11.0
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../../gtest ..
make -j
make install
Configure VSCode for GoogleTest
可以直接參考以下設定,設定好之後,就可以使用 VSCode 去編譯 GoogleTest
- Windows/Linux:
- Compile: Ctrl + Shift + B
- Debug: F5
- macOS:
- Compile: Cmd + Shift + B
- Debug: F5
Windows (MinGW)
.vscode/c_cpp_properties.json
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"./gtest/include"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"compilerPath": "C:\\Users\\user\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64\\bin\\gcc.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-gcc-x64",
"compilerArgs": []
}
],
"version": 4
}
.vscode/launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "g++.exe - Build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}\\${fileBasenameNoExtension}.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "C:\\Users\\user\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64\\bin\\gdb.exe",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "C/C++: g++.exe build active file"
}
]
}
.vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++.exe build active file",
"command": "C:\\Users\\user\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64\\bin\\g++.exe",
"args": [
"-fdiagnostics-color=always",
"-std=c++17",
"-I./gtest/include",
"-L./gtest/lib",
"-lgtest_main",
"-lgtest",
"-pthread",
"-g",
"${file}",
"-o",
"${fileDirname}\\${fileBasenameNoExtension}.exe"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "compiler: C:\\Users\\user\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64\\bin\\g++.exe"
}
]
}
Linux & WSL
.vscode/c_cpp_properties.json
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"./gtest/include"
],
"defines": [],
"compilerPath": "/usr/bin/g++",
"cStandard": "c17",
"cppStandard": "c++17"
}
],
"version": 4
}
.vscode/launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "g++ - Build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "C/C++: g++ build active file",
"miDebuggerPath": "/usr/bin/gdb"
}
]
}
.vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++ build active file",
"command": "/usr/bin/g++",
"args": [
"-fdiagnostics-color=always",
"-std=c++17",
"-I./gtest/include",
"-L./gtest/lib",
"-lgtest_main",
"-lgtest",
"-pthread",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "compiler: /usr/bin/g++"
}
]
}
macOS
.vscode/c_cpp_properties.json
{
"configurations": [
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**",
"./gtest/include"
],
"defines": [],
"macFrameworkPath": [
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks"
],
"compilerPath": "/usr/bin/clang++",
"cStandard": "c17",
"cppStandard": "c++17"
}
],
"version": 4
}
.vscode/launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "clang++ - build and debug active file",
"type": "lldb",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"args": [
"-arg1",
"-arg2"
],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "lldb",
"preLaunchTask": "C/C++: clang++ build active file"
}
]
}
.vscode/settings.json
{
"C_Cpp.default.cppStandard": "c++17"
}
.vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "C/C++: clang++ build active file",
"command": "/usr/bin/clang++",
"args": [
"-fdiagnostics-color=always",
"-std=c++17",
"-stdlib=libc++",
"-I./gtest/include",
"-L./gtest/lib",
"-lgtest_main",
"-lgtest",
"-pthread",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Compiler: /usr/bin/clang++"
}
]
}
Hello GoogleTest
Ref: Quickstart: Building with CMake
#include <gtest/gtest.h>
// Demonstrate some basic assertions.
TEST(HelloTest, BasicAssertions)
{
// Expect two strings not to be equal.
EXPECT_STRNE("hello", "world");
// Expect equality.
EXPECT_EQ(7 * 6, 42);
}
Running main() from /Users/stevenokm/waste/projects/googletest-release-1.11.0/googletest/src/gtest_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from HelloTest
[ RUN ] HelloTest.BasicAssertions
[ OK ] HelloTest.BasicAssertions (0 ms)
[----------] 1 test from HelloTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.
Code Structure
Ref: Testing Reference
TEST
TEST(TestSuiteName, TestName) { ... statements ... }
Defines an individual test named TestName
in the test suite
TestSuiteName
, consisting of the given statements.
Both arguments TestSuiteName
and TestName
must be valid C++ identifiers
and must not contain underscores (_
). Tests in different test suites can have
the same individual name.
The statements within the test body can be any code under test. Assertions used within the test body determine the outcome of the test.
Assert & Expect
GoogleTest 提供了兩個新的測試模式,Assert 和 Expect,用來檢查測試結果是否正確。
Assert
Assert 的意思是只要敘述結果錯誤,該測試就會立刻失敗,而不會繼續執行下去。
Expect
Expect 的意思是檢查敘述結果是否正確,如果不正確,該測試會失敗,但是繼續執行下去。
Example: different of Assert & Expect
#include <gtest/gtest.h>
// Demonstrate some Assert assertions.
TEST(HelloTest, AssertAssertions)
{
// Assert two strings to be equal.
ASSERT_STREQ("hello", "world");
// Assert non-equality.
ASSERT_NE(7 * 6, 42);
}
// Demonstrate some Expect assertions.
TEST(HelloTest, ExpectAssertions)
{
// Expect two strings to be equal.
EXPECT_STREQ("hello", "world");
// Expect non-equality.
EXPECT_NE(7 * 6, 42);
}
Running main() from /Users/stevenokm/waste/projects/googletest-release-1.11.0/googletest/src/gtest_main.cc
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from HelloTest
[ RUN ] HelloTest.AssertAssertions
/Users/stevenokm/waste/projects/test.cpp:7: Failure
Expected equality of these values:
"hello"
"world"
[ FAILED ] HelloTest.AssertAssertions (0 ms)
[ RUN ] HelloTest.ExpectAssertions
/Users/stevenokm/waste/projects/test.cpp:16: Failure
Expected equality of these values:
"hello"
"world"
/Users/stevenokm/waste/projects/test.cpp:18: Failure
Expected: (7 * 6) != (42), actual: 42 vs 42
[ FAILED ] HelloTest.ExpectAssertions (0 ms)
[----------] 2 tests from HelloTest (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 2 tests, listed below:
[ FAILED ] HelloTest.AssertAssertions
[ FAILED ] HelloTest.ExpectAssertions
2 FAILED TESTS
Types of Assertions
sample1.h
// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework.
#ifndef GOOGLETEST_SAMPLES_SAMPLE1_H_
#define GOOGLETEST_SAMPLES_SAMPLE1_H_
// Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int Factorial(int n)
{
int result = 1;
for (int i = 1; i <= n; i++)
{
result *= i;
}
return result;
}
// Returns true if and only if n is a prime number.
bool IsPrime(int n)
{
// Trivial case 1: small numbers
if (n <= 1)
return false;
// Trivial case 2: even numbers
if (n % 2 == 0)
return n == 2;
// Now, we have that n is odd and n >= 3.
// Try to divide n by every odd number i, starting from 3
for (int i = 3;; i += 2)
{
// We only have to try i up to the square root of n
if (i > n / i)
break;
// Now, we have i <= n/i < n.
// If n is divisible by i, n is not prime.
if (n % i == 0)
return false;
}
// n has no integer factor in the range (1, n), and thus is prime.
return true;
}
#endif // GOOGLETEST_SAMPLES_SAMPLE1_H_
sample1_unittest.cc
// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework.
// This sample shows how to write a simple unit test for a function,
// using Google C++ testing framework.
//
// Writing a unit test using Google C++ testing framework is easy as 1-2-3:
// Step 1. Include necessary header files such that the stuff your
// test logic needs is declared.
//
// Don't forget gtest.h, which declares the testing framework.
#include <limits.h>
#include "sample1.h"
#include "gtest/gtest.h"
namespace
{
// Step 2. Use the TEST macro to define your tests.
//
// TEST has two parameters: the test case name and the test name.
// After using the macro, you should define your test logic between a
// pair of braces. You can use a bunch of macros to indicate the
// success or failure of a test. EXPECT_TRUE and EXPECT_EQ are
// examples of such macros. For a complete list, see gtest.h.
//
// <TechnicalDetails>
//
// In Google Test, tests are grouped into test cases. This is how we
// keep test code organized. You should put logically related tests
// into the same test case.
//
// The test case name and the test name should both be valid C++
// identifiers. And you should not use underscore (_) in the names.
//
// Google Test guarantees that each test you define is run exactly
// once, but it makes no guarantee on the order the tests are
// executed. Therefore, you should write your tests in such a way
// that their results don't depend on their order.
//
// </TechnicalDetails>
// Tests Factorial().
// Tests factorial of negative numbers.
TEST(FactorialTest, Negative)
{
// This test is named "Negative", and belongs to the "FactorialTest"
// test case.
EXPECT_EQ(1, Factorial(-5));
EXPECT_EQ(1, Factorial(-1));
EXPECT_GT(Factorial(-10), 0);
// <TechnicalDetails>
//
// EXPECT_EQ(expected, actual) is the same as
//
// EXPECT_TRUE((expected) == (actual))
//
// except that it will print both the expected value and the actual
// value when the assertion fails. This is very helpful for
// debugging. Therefore in this case EXPECT_EQ is preferred.
//
// On the other hand, EXPECT_TRUE accepts any Boolean expression,
// and is thus more general.
//
// </TechnicalDetails>
}
// Tests factorial of 0.
TEST(FactorialTest, Zero)
{
EXPECT_EQ(1, Factorial(0));
}
// Tests factorial of positive numbers.
TEST(FactorialTest, Positive)
{
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
// Tests IsPrime()
// Tests negative input.
TEST(IsPrimeTest, Negative)
{
// This test belongs to the IsPrimeTest test case.
EXPECT_FALSE(IsPrime(-1));
EXPECT_FALSE(IsPrime(-2));
EXPECT_FALSE(IsPrime(INT_MIN));
}
// Tests some trivial cases.
TEST(IsPrimeTest, Trivial)
{
EXPECT_FALSE(IsPrime(0));
EXPECT_FALSE(IsPrime(1));
EXPECT_TRUE(IsPrime(2));
EXPECT_TRUE(IsPrime(3));
}
// Tests positive input.
TEST(IsPrimeTest, Positive)
{
EXPECT_FALSE(IsPrime(4));
EXPECT_TRUE(IsPrime(5));
EXPECT_FALSE(IsPrime(6));
EXPECT_TRUE(IsPrime(23));
}
} // namespace
// Step 3. Call RUN_ALL_TESTS() in main().
//
// We do this by linking in src/gtest_main.cc file, which consists of
// a main() function which calls RUN_ALL_TESTS() for us.
//
// This runs all the tests you've defined, prints the result, and
// returns 0 if successful, or 1 otherwise.
//
// Did you notice that we didn't register the tests? The
// RUN_ALL_TESTS() macro magically knows about all the tests we
// defined. Isn't this convenient?
Example: Postfix Calculator
lab12_3_include.h
#ifndef LAB12_3_INCLUDE_H
#define LAB12_3_INCLUDE_H
#include <iostream>
#include <string>
#include <vector>
using namespace std;
bool is_number(string str) // Check if a string is a number
{
for (int i = 0; i < str.length(); i++)
{
if (i == 0 && str[i] == '-' && str.length() > 1)
continue;
if (!isdigit(str[i]))
return false;
}
return true;
}
void parse_expression(const string &expression, vector<string> &tokens)
{
string temp;
for (int i = 0; i < expression.size(); i++)
{
if (expression[i] == ' ')
{
tokens.push_back(temp);
temp.clear();
}
else if (i == expression.size() - 1) // End of Input_Buffer
{
temp += expression.substr(i, 1);
tokens.push_back(temp);
}
else
temp += expression.substr(i, 1);
}
}
void print_nums(const vector<string> &tokens)
{
cout << "Operands:";
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i].length() != 1 || isdigit(tokens[i][0]))
cout << ' ' << tokens[i];
}
cout << endl;
}
void print_ops(const vector<string> &tokens)
{
cout << "Operators:";
for (int i = 0; i < tokens.size(); i++)
{
if (tokens[i].length() == 1 && !isdigit(tokens[i][0]))
cout << ' ' << tokens[i];
}
cout << endl;
}
void calculate_expression(
const vector<string> tokens, vector<long> &num_stack)
{
if (tokens.size() == 1)
{
num_stack.push_back(stol(tokens[0]));
return;
}
int now = 0;
while (now < tokens.size())
{
while (is_number(tokens[now]))
{
num_stack.push_back(stol(tokens[now]));
now++;
}
long a = num_stack[num_stack.size() - 2],
b = num_stack[num_stack.size() - 1];
num_stack.pop_back();
num_stack.pop_back();
if (tokens[now] == "+")
num_stack.push_back(a + b);
else if (tokens[now] == "-")
num_stack.push_back(a - b);
else if (tokens[now] == "*")
num_stack.push_back(a * b);
else if (tokens[now] == "/")
num_stack.push_back(a / b);
now++;
}
}
bool check_postfix_expression(const vector<string> &tokens)
{
int stack_count = 0;
for (int i = 0; i < tokens.size(); i++)
{
if (i > 0 && stack_count < 1)
return false;
if (is_number(tokens[i]))
stack_count++;
else if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/")
stack_count--;
else
return false;
}
return stack_count == 1;
}
#endif //LAB12_3_INCLUDE_H
lab12_3_main.cpp
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include "lab12_3_include.h"
using namespace std;
int main(int argc, char *argv[])
{
string input_buffer;
vector<string> tokens;
cout << "Please input the expression: ";
std::getline(cin, input_buffer);
parse_expression(input_buffer, tokens);
if (!check_postfix_expression(tokens))
{
cout << "Invalid expression." << endl;
return EXIT_FAILURE;
}
print_ops(tokens);
print_nums(tokens);
cout << "Result: ";
int last_pos = tokens.size() - 1;
vector<long> num_stack;
calculate_expression(tokens, num_stack);
cout << num_stack[0] << endl;
return EXIT_SUCCESS;
}
lab12_3_unittest.cpp
#include "lab12_3_include.h"
#include "gtest/gtest.h"
TEST(ParseExpression, Operands)
{
vector<string> tokens;
string input_buffer = "1 2 3 4 5 6 7 8 9";
parse_expression(input_buffer, tokens);
ASSERT_EQ(tokens.size(), 9);
for (int i = 0; i < tokens.size(); i++)
{
EXPECT_EQ(tokens[i], to_string(i + 1));
}
}
TEST(ParseExpression, Operators)
{
vector<string> tokens;
string input_buffer = "+ - * /";
parse_expression(input_buffer, tokens);
ASSERT_EQ(tokens.size(), 4);
EXPECT_EQ(tokens[0], "+");
EXPECT_EQ(tokens[1], "-");
EXPECT_EQ(tokens[2], "*");
EXPECT_EQ(tokens[3], "/");
}
TEST(ParseExpression, Mixed1)
{
vector<string> tokens;
string input_buffer = "1 2 3 4 * + -";
parse_expression(input_buffer, tokens);
ASSERT_EQ(tokens.size(), 7);
EXPECT_EQ(tokens[0], "1");
EXPECT_EQ(tokens[1], "2");
EXPECT_EQ(tokens[2], "3");
EXPECT_EQ(tokens[3], "4");
EXPECT_EQ(tokens[4], "*");
EXPECT_EQ(tokens[5], "+");
EXPECT_EQ(tokens[6], "-");
}
TEST(ParseExpression, Mixed2)
{
vector<string> tokens;
string input_buffer = "-1 -2 -3 -4 * + -";
parse_expression(input_buffer, tokens);
EXPECT_EQ(tokens.size(), 7);
EXPECT_EQ(tokens[0], "-1");
EXPECT_EQ(tokens[1], "-2");
EXPECT_EQ(tokens[2], "-3");
EXPECT_EQ(tokens[3], "-4");
EXPECT_EQ(tokens[4], "*");
EXPECT_EQ(tokens[5], "+");
EXPECT_EQ(tokens[6], "-");
}
TEST(ParseExpression, Mixed3)
{
vector<string> tokens;
string input_buffer = "-1 -2 -3 -4 * + %";
parse_expression(input_buffer, tokens);
EXPECT_EQ(tokens.size(), 7);
EXPECT_EQ(tokens[0], "-1");
EXPECT_EQ(tokens[1], "-2");
EXPECT_EQ(tokens[2], "-3");
EXPECT_EQ(tokens[3], "-4");
EXPECT_EQ(tokens[4], "*");
EXPECT_EQ(tokens[5], "+");
EXPECT_EQ(tokens[6], "%");
}
TEST(CheckPostfixExpression, Operand)
{
vector<string> tokens;
tokens.push_back("1");
EXPECT_TRUE(check_postfix_expression(tokens));
}
TEST(CheckPostfixExpression, Operator)
{
vector<string> tokens;
tokens.push_back("+");
EXPECT_FALSE(check_postfix_expression(tokens));
}
TEST(CheckPostfixExpression, Mixed)
{
vector<string> tokens;
tokens.push_back("1");
tokens.push_back("+");
EXPECT_FALSE(check_postfix_expression(tokens));
}
TEST(CheckPostfixExpression, SimpleExpression)
{
vector<string> tokens;
tokens.push_back("1");
tokens.push_back("2");
tokens.push_back("+");
EXPECT_TRUE(check_postfix_expression(tokens));
}
TEST(CheckPostfixExpression, ComplexExpression1)
{
vector<string> tokens;
tokens.push_back("1");
tokens.push_back("2");
tokens.push_back("3");
tokens.push_back("4");
tokens.push_back("*");
tokens.push_back("+");
tokens.push_back("-");
EXPECT_TRUE(check_postfix_expression(tokens));
}
TEST(CheckPostfixExpression, ComplexExpression2)
{
vector<string> tokens;
tokens.push_back("-1");
tokens.push_back("-2");
tokens.push_back("-3");
tokens.push_back("-4");
tokens.push_back("*");
tokens.push_back("+");
tokens.push_back("-");
EXPECT_TRUE(check_postfix_expression(tokens));
}
TEST(CheckPostfixExpression, ComplexExpression3)
{
vector<string> tokens;
tokens.push_back("-1");
tokens.push_back("-2");
tokens.push_back("*");
tokens.push_back("-3");
tokens.push_back("+");
tokens.push_back("-4");
tokens.push_back("-");
EXPECT_TRUE(check_postfix_expression(tokens));
}
TEST(CheckPostfixExpression, ComplexExpression4)
{
vector<string> tokens;
tokens.push_back("1");
tokens.push_back("2");
tokens.push_back("-");
tokens.push_back("2");
tokens.push_back("1");
tokens.push_back("+");
tokens.push_back("/");
EXPECT_TRUE(check_postfix_expression(tokens));
}
TEST(CheckPostfixExpression, NotExpression1)
{
vector<string> tokens;
tokens.push_back("-1");
tokens.push_back("-2");
tokens.push_back("-3");
tokens.push_back("-4");
tokens.push_back("*");
tokens.push_back("+");
EXPECT_FALSE(check_postfix_expression(tokens));
}
TEST(CheckPostfixExpression, NotExpression2)
{
vector<string> tokens;
tokens.push_back("-1");
tokens.push_back("-2");
tokens.push_back("-3");
tokens.push_back("*");
tokens.push_back("+");
tokens.push_back("-");
EXPECT_FALSE(check_postfix_expression(tokens));
}
TEST(CheckPostfixExpression, NotExpression3)
{
vector<string> tokens;
tokens.push_back("-1");
tokens.push_back("-2");
tokens.push_back("-3");
tokens.push_back("-4");
tokens.push_back("*");
tokens.push_back("+");
tokens.push_back("%");
EXPECT_FALSE(check_postfix_expression(tokens));
}
TEST(CheckPostfixExpression, NotExpression4)
{
vector<string> tokens;
tokens.push_back("-1");
tokens.push_back("*");
tokens.push_back("-2");
tokens.push_back("+");
tokens.push_back("-3");
tokens.push_back("-");
tokens.push_back("-4");
EXPECT_FALSE(check_postfix_expression(tokens));
}
TEST(CalculateExpression, Operand)
{
vector<string> tokens;
vector<long> num_stack;
tokens.push_back("1");
calculate_expression(tokens, num_stack);
ASSERT_EQ(num_stack.size(), 1);
EXPECT_EQ(num_stack[0], 1);
}
TEST(CalculateExpression, SimpleExpression)
{
vector<string> tokens;
vector<long> num_stack;
tokens.push_back("1");
tokens.push_back("2");
tokens.push_back("+");
calculate_expression(tokens, num_stack);
ASSERT_EQ(num_stack.size(), 1);
EXPECT_EQ(num_stack[0], 3);
}
TEST(CalculateExpression, ComplexExpression1)
{
vector<string> tokens;
vector<long> num_stack;
//1 2 3 4 * + -
tokens.push_back("1");
tokens.push_back("2");
tokens.push_back("3");
tokens.push_back("4");
tokens.push_back("*");
tokens.push_back("+");
tokens.push_back("-");
calculate_expression(tokens, num_stack);
ASSERT_EQ(num_stack.size(), 1);
EXPECT_EQ(num_stack[0], -13);
}
TEST(CalculateExpression, ComplexExpression2)
{
vector<string> tokens;
vector<long> num_stack;
//-1 -2 -3 -4 * + -
tokens.push_back("-1");
tokens.push_back("-2");
tokens.push_back("-3");
tokens.push_back("-4");
tokens.push_back("*");
tokens.push_back("+");
tokens.push_back("-");
calculate_expression(tokens, num_stack);
ASSERT_EQ(num_stack.size(), 1);
EXPECT_EQ(num_stack[0], -11);
}
TEST(CalculateExpression, ComplexExpression3)
{
vector<string> tokens;
vector<long> num_stack;
//-1 -2 * -3 + -4 -
tokens.push_back("-1");
tokens.push_back("-2");
tokens.push_back("*");
tokens.push_back("-3");
tokens.push_back("+");
tokens.push_back("-4");
tokens.push_back("-");
calculate_expression(tokens, num_stack);
ASSERT_EQ(num_stack.size(), 1);
EXPECT_EQ(num_stack[0], 3);
}
TEST(CalculateExpression, ComplexExpression4)
{
vector<string> tokens;
vector<long> num_stack;
//1 2 - 2 1 + /
tokens.push_back("1");
tokens.push_back("2");
tokens.push_back("-");
tokens.push_back("2");
tokens.push_back("1");
tokens.push_back("+");
tokens.push_back("/");
calculate_expression(tokens, num_stack);
ASSERT_EQ(num_stack.size(), 1);
EXPECT_EQ(num_stack[0], 0);
}
Running main() from /Users/stevenokm/waste/projects/googletest-release-1.11.0/googletest/src/gtest_main.cc
[==========] Running 23 tests from 3 test suites.
[----------] Global test environment set-up.
[----------] 5 tests from ParseExpression
[ RUN ] ParseExpression.Operands
[ OK ] ParseExpression.Operands (0 ms)
[ RUN ] ParseExpression.Operators
[ OK ] ParseExpression.Operators (0 ms)
[ RUN ] ParseExpression.Mixed1
[ OK ] ParseExpression.Mixed1 (0 ms)
[ RUN ] ParseExpression.Mixed2
[ OK ] ParseExpression.Mixed2 (0 ms)
[ RUN ] ParseExpression.Mixed3
[ OK ] ParseExpression.Mixed3 (0 ms)
[----------] 5 tests from ParseExpression (0 ms total)
[----------] 12 tests from CheckPostfixExpression
[ RUN ] CheckPostfixExpression.Operand
[ OK ] CheckPostfixExpression.Operand (0 ms)
[ RUN ] CheckPostfixExpression.Operator
[ OK ] CheckPostfixExpression.Operator (0 ms)
[ RUN ] CheckPostfixExpression.Mixed
[ OK ] CheckPostfixExpression.Mixed (0 ms)
[ RUN ] CheckPostfixExpression.SimpleExpression
[ OK ] CheckPostfixExpression.SimpleExpression (0 ms)
[ RUN ] CheckPostfixExpression.ComplexExpression1
[ OK ] CheckPostfixExpression.ComplexExpression1 (0 ms)
[ RUN ] CheckPostfixExpression.ComplexExpression2
[ OK ] CheckPostfixExpression.ComplexExpression2 (0 ms)
[ RUN ] CheckPostfixExpression.ComplexExpression3
[ OK ] CheckPostfixExpression.ComplexExpression3 (0 ms)
[ RUN ] CheckPostfixExpression.ComplexExpression4
[ OK ] CheckPostfixExpression.ComplexExpression4 (0 ms)
[ RUN ] CheckPostfixExpression.NotExpression1
[ OK ] CheckPostfixExpression.NotExpression1 (0 ms)
[ RUN ] CheckPostfixExpression.NotExpression2
[ OK ] CheckPostfixExpression.NotExpression2 (0 ms)
[ RUN ] CheckPostfixExpression.NotExpression3
[ OK ] CheckPostfixExpression.NotExpression3 (0 ms)
[ RUN ] CheckPostfixExpression.NotExpression4
[ OK ] CheckPostfixExpression.NotExpression4 (0 ms)
[----------] 12 tests from CheckPostfixExpression (0 ms total)
[----------] 6 tests from CalculateExpression
[ RUN ] CalculateExpression.Operand
[ OK ] CalculateExpression.Operand (0 ms)
[ RUN ] CalculateExpression.SimpleExpression
[ OK ] CalculateExpression.SimpleExpression (0 ms)
[ RUN ] CalculateExpression.ComplexExpression1
[ OK ] CalculateExpression.ComplexExpression1 (0 ms)
[ RUN ] CalculateExpression.ComplexExpression2
[ OK ] CalculateExpression.ComplexExpression2 (0 ms)
[ RUN ] CalculateExpression.ComplexExpression3
[ OK ] CalculateExpression.ComplexExpression3 (0 ms)
[ RUN ] CalculateExpression.ComplexExpression4
[ OK ] CalculateExpression.ComplexExpression4 (0 ms)
[----------] 6 tests from CalculateExpression (0 ms total)
[----------] Global test environment tear-down
[==========] 23 tests from 3 test suites ran. (0 ms total)
[ PASSED ] 23 tests.
Final Exam 1: Command Line Floating Point Calculator
Final Exam 1-1: Display Operators & Operands (35%)
- Inputs:
- The expression to be evaluated.
- Each operand and operator is separated line by line.
- The operands are in
double
format. - The operators are in
std::string
format. - The expressiion is ended by
=
.
- The valid operators are
+
,-
,*
,/
- The
/
opeartor is only valid if the second operand is not zero.
- The
- The expression to be evaluated.
- Outputs:
- The list of operands and operators in the expression.
- The format is shown below.
- The operands are in defualt
double
format instd::cout
.
- If the expression is invalid, the output is
Invalid Expression
and exit.- If the number of operands is not equal to the number of operators plus one.
- The invalid operator occurs.
- The list of operands and operators in the expression.
- File name:
final1_1_<student_id>.cpp
(e.g.final1_1_106062802.cpp
)
Notice:
- The program will not print any user prompts.
- The program should use functions to process the input and output.
- The program should be finished within 30 seconds. Any test cases will garuntee the program is finished within 30 second
Format
<operand_1>⏎
<operator_1>⏎
...
<operand_n>⏎
=⏎
Operand: <operand_1> ... <operand_n>
Operator: <operator_1> ... <operator_n-1>
Example
$ ./a.out⏎
0.0⏎
=⏎
Operands: 0
Operators:
$ ./a.out⏎
0.0⏎
/⏎
1.0⏎
=⏎
Operands: 0 1
Operators: /
$ ./a.out⏎
1.0⏎
*⏎
2.0⏎
+⏎
3.0⏎
-⏎
4.0⏎
=⏎
Operands: 1 2 3 4
Operators: * + -
$ ./a.out⏎
-1.234e-5⏎
*⏎
-5.678e-6⏎
+⏎
9.876e-7⏎
-⏎
5.432e-8⏎
=⏎
Operands: -1.234e-05 -5.678e-06 9.876e-07 5.432e-08
Operators: * + -
$ ./a.out⏎
-1.234e-5⏎
*⏎
-5.678e-6⏎
+⏎
9.876e-7⏎
5.432e-8⏎
=⏎
Invalid Expression
$ ./a.out⏎
-1.234e-5⏎
*⏎
-5.678e-6⏎
+⏎
9.876e-7⏎
-⏎
=⏎
Invalid Expression
$ ./a.out⏎
-1.234e-5⏎
*⏎
-5.678e-6⏎
+⏎
9.876e-7⏎
%⏎
5.432e-8⏎
=⏎
Invalid Expression
$ ./a.out⏎
-1.234e-5⏎
*⏎
-5.678e-6⏎
+⏎
9.876e-7⏎
/⏎
0.0⏎
=⏎
Invalid Expression
Pseudo code
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// parse_expression
// Parse the expression from cin and store the operator and operand in
// operator_vector and operand_vector respectively.
// Hint: there is a function like atoi but it convert char[] to double.
// Hint 2: to convert string to char[] use .c_str() function.
void parse_expression(vector<string> &operator_vector,
vector<double> &operand_vector);
// check_expression
// Check if the expression is valid.
// Return true if the expression is valid, otherwise return false.
bool check_expression(const vector<string> &operator_vector,
const vector<double> &operand_vector);
// print_operator
// Print the operator in operator_vector.
void print_operator(const vector<string> &operator_vector);
// print_operand
// Print the operand in operand_vector.
// Use the default cout format for double.
void print_operand(const vector<double> &operand_vector);
int main(int argc, char *argv[])
{
vector<string> operator_vector;
vector<double> operand_vector;
parse_expression(operator_vector, operand_vector);
if (!check_expression(operator_vector, operand_vector))
{
cout << "Invalid Expression" << endl;
return EXIT_FAILURE;
}
print_operand(operand_vector);
print_operator(operator_vector);
return EXIT_SUCCESS;
}
Reference Code:
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// parse_expression
// Parse the expression from cin and store the operator and operand in
// operator_vector and operand_vector respectively.
// Hint: there is a function like atoi but it convert char[] to double.
// Hint 2: to convert string to char[] use .c_str() function.
void parse_expression(vector<string> &operator_vector,
vector<double> &operand_vector);
// check_expression
// Check if the expression is valid.
// Return true if the expression is valid, otherwise return false.
bool check_expression(const vector<string> &operator_vector,
const vector<double> &operand_vector);
// print_operator
// Print the operator in operator_vector.
void print_operator(const vector<string> &operator_vector);
// print_operand
// Print the operand in operand_vector.
// Use the default cout format for double.
void print_operand(const vector<double> &operand_vector);
int main(int argc, char *argv[])
{
vector<string> operator_vector;
vector<double> operand_vector;
parse_expression(operator_vector, operand_vector);
if (!check_expression(operator_vector, operand_vector))
{
cout << "Invalid Expression" << endl;
return EXIT_FAILURE;
}
print_operand(operand_vector);
print_operator(operator_vector);
return EXIT_SUCCESS;
}
void parse_expression(vector<string> &operator_vector,
vector<double> &operand_vector)
{
for (string temp; cin >> temp;)
{
if (temp == "=")
{
break;
}
else if(isnumber(temp[0]))
{
operand_vector.push_back(atof(temp.c_str()));
}
else
{
operator_vector.push_back(temp);
}
}
}
bool check_expression(const vector<string> &operator_vector,
const vector<double> &operand_vector)
{
for (unsigned i = 0; i < operator_vector.size(); i++)
{
if (operator_vector[i] == "/" && operand_vector[i + 1] == 0)
{
return false;
}
else if (operator_vector[i] != "+" && operator_vector[i] != "-" &&
operator_vector[i] != "*" && operator_vector[i] != "/")
{
return false;
}
}
return (operator_vector.size() == operand_vector.size() - 1);
}
void print_operator(const vector<string> &operator_vector)
{
cout << "Operators:";
for (int i = 0; i < operator_vector.size(); i++)
cout << ' ' << operator_vector[i];
cout << endl;
}
void print_operand(const vector<double> &operand_vector)
{
cout << "Operands:";
for (int i = 0; i < operand_vector.size(); i++)
cout << ' ' << operand_vector[i];
cout << endl;
}
Final Exam 1-2: Calculator (20%)
- Inputs:
- The expression to be evaluated.
- Each operand and operator is separated line by line.
- The operands are in
double
format. - The operators are in
std::string
format. - The expressiion is ended by
=
.
- The valid operators are
+
,-
,*
,/
- The
/
opeartor is only valid if the second operand is not zero.
- The
- The expression to be evaluated.
- Outputs:
- The list of operands and operators in the expression.
- The format is shown below.
- The operands are in defualt
double
format instd::cout
.
- The result of the expression.
- The calculation is done line by line.
- The result is in defualt
double
format.
- If the expression is invalid, the output is
Invalid Expression
and exit.- If the number of operands is not equal to the number of operators plus one.
- The invalid operator occurs.
- The list of operands and operators in the expression.
- File name:
final1_2_<student_id>.cpp
(e.g.final1_2_106062802.cpp
)
Notice:
- The program will not print any user prompts.
- The program should use functions to process the input and output.
- The program should be finished within 30 seconds. Any test cases will garuntee the program is finished within 30 second
Format
<operand_1>⏎
<operator_1>⏎
...
<operand_n>⏎
=⏎
Operand: <operand_1> ... <operand_n>
Operator: <operator_1> ... <operator_n-1>
Result: <result>
Example
$ ./a.out⏎
0.0⏎
=⏎
Operands: 0
Operators:
Result: 0
$ ./a.out⏎
0.0⏎
/⏎
1.0⏎
=⏎
Operands: 0 1
Operators: /
Result: 0
$ ./a.out⏎
1.0⏎
*⏎
2.0⏎
+⏎
3.0⏎
-⏎
4.0⏎
=⏎
Operands: 1 2 3 4
Operators: * + -
Result: 1
$ ./a.out⏎
-1.234e-5⏎
*⏎
-5.678e-6⏎
+⏎
9.876e-7⏎
-⏎
5.432e-8⏎
=⏎
Operands: -1.234e-05 -5.678e-06 9.876e-07 5.432e-08
Operators: * + -
Result: 9.3335e-07
$ ./a.out⏎
-1.234e-5⏎
*⏎
-5.678e-6⏎
+⏎
9.876e-7⏎
5.432e-8⏎
=⏎
Invalid Expression
$ ./a.out⏎
-1.234e-5⏎
*⏎
-5.678e-6⏎
+⏎
9.876e-7⏎
-⏎
=⏎
Invalid Expression
$ ./a.out⏎
-1.234e-5⏎
*⏎
-5.678e-6⏎
+⏎
9.876e-7⏎
%⏎
5.432e-8⏎
=⏎
Invalid Expression
$ ./a.out⏎
-1.234e-5⏎
*⏎
-5.678e-6⏎
+⏎
9.876e-7⏎
/⏎
0.0⏎
=⏎
Invalid Expression
Pseudo code
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// parse_expression
// Parse the expression from cin and store the operator and operand in
// operator_vector and operand_vector respectively.
// Hint: there is a function like atoi but it convert char[] to double.
// Hint 2: to convert string to char[] use .c_str() function.
void parse_expression(vector<string> &operator_vector,
vector<double> &operand_vector);
// check_expression
// Check if the expression is valid.
// Return true if the expression is valid, otherwise return false.
bool check_expression(const vector<string> &operator_vector,
const vector<double> &operand_vector);
// print_operator
// Print the operator in operator_vector.
void print_operator(const vector<string> &operator_vector);
// print_operand
// Print the operand in operand_vector.
// Use the default cout format for double.
void print_operand(const vector<double> &operand_vector);
// calculate_expression
// Calculate the expression store in operator_vector and operand_vector
// and return the result in double.
// User should check the expression is valid before calling this function.
double calculate_expression(const vector<string> &operator_vector,
const vector<double> &operand_vector);
int main(int argc, char *argv[])
{
vector<string> operator_vector;
vector<double> operand_vector;
parse_expression(operator_vector, operand_vector);
if (!check_expression(operator_vector, operand_vector))
{
cout << "Invalid Expression" << endl;
return EXIT_FAILURE;
}
print_operand(operand_vector);
print_operator(operator_vector);
cout << "Result: " << calculate_expression(operator_vector, operand_vector)
<< endl;
return EXIT_SUCCESS;
}
Reference Code:
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// parse_expression
// Parse the expression from cin and store the operator and operand in
// operator_vector and operand_vector respectively.
// Hint: there is a function like atoi but it convert char[] to double.
// Hint 2: to convert string to char[] use .c_str() function.
void parse_expression(vector<string> &operator_vector,
vector<double> &operand_vector);
// check_expression
// Check if the expression is valid.
// Return true if the expression is valid, otherwise return false.
bool check_expression(const vector<string> &operator_vector,
const vector<double> &operand_vector);
// print_operator
// Print the operator in operator_vector.
void print_operator(const vector<string> &operator_vector);
// print_operand
// Print the operand in operand_vector.
// Use the default cout format for double.
void print_operand(const vector<double> &operand_vector);
// calculate_expression
// Calculate the expression store in operator_vector and operand_vector
// and return the result in double.
// User should check the expression is valid before calling this function.
double calculate_expression(const vector<string> &operator_vector,
const vector<double> &operand_vector);
int main(int argc, char *argv[])
{
vector<string> operator_vector;
vector<double> operand_vector;
parse_expression(operator_vector, operand_vector);
if (!check_expression(operator_vector, operand_vector))
{
cout << "Invalid Expression" << endl;
return EXIT_FAILURE;
}
print_operand(operand_vector);
print_operator(operator_vector);
cout << "Result: " << calculate_expression(operator_vector, operand_vector)
<< endl;
return EXIT_SUCCESS;
}
void parse_expression(vector<string> &operator_vector,
vector<double> &operand_vector)
{
for (string temp; cin >> temp;)
{
if (temp == "=")
{
break;
}
else if(isnumber(temp[0]))
{
operand_vector.push_back(atof(temp.c_str()));
}
else
{
operator_vector.push_back(temp);
}
}
}
bool check_expression(const vector<string> &operator_vector,
const vector<double> &operand_vector)
{
for (unsigned i = 0; i < operator_vector.size(); i++)
{
if (operator_vector[i] == "/" && operand_vector[i + 1] == 0)
{
return false;
}
else if (operator_vector[i] != "+" && operator_vector[i] != "-" &&
operator_vector[i] != "*" && operator_vector[i] != "/")
{
return false;
}
}
return (operator_vector.size() == operand_vector.size() - 1);
}
void print_operator(const vector<string> &operator_vector)
{
cout << "Operators:";
for (int i = 0; i < operator_vector.size(); i++)
cout << ' ' << operator_vector[i];
cout << endl;
}
void print_operand(const vector<double> &operand_vector)
{
cout << "Operands:";
for (int i = 0; i < operand_vector.size(); i++)
cout << ' ' << operand_vector[i];
cout << endl;
}
double calculate_expression(const vector<string> &operator_vector,
const vector<double> &operand_vector)
{
double result = operand_vector[0];
for (unsigned i = 0; i < operator_vector.size(); i++)
{
if (operator_vector[i] == "+")
{
result += operand_vector[i + 1];
}
else if (operator_vector[i] == "-")
{
result -= operand_vector[i + 1];
}
else if (operator_vector[i] == "*")
{
result *= operand_vector[i + 1];
}
else if (operator_vector[i] == "/")
{
result /= operand_vector[i + 1];
}
}
return result;
}
Final Exam 2: Geometry Object Computer
Final Exam 2-1: Display Geometry Objects (30%)
- Inputs: The gemoetry object.
- The first line of the input contains the type of the object and the number of points.
- The type of the object is one of the following:
point
: a point.circle
: a circle, which is defined by two points, center of the circle and a point on the circle.triangle
: a triangle, which is defined by three points.rectangle
: a rectangle, which is defined by two points.
- The number of points is an
int
between (1) and (100).
- The type of the object is one of the following:
- The remaining lines of the input contain the coordinates of the points.
- The coordinates of the points are
double
between (-100) and (100).
- The coordinates of the points are
- The first line of the input contains the type of the object and the number of points.
- Outputs:
- The type of the object, the number of points, and the coordinates of the points.
- The format is shown below.
- The coordinates of the points are in defualt
double
format instd::cout
.
- If the object is invalid, the output is
Invalid Object
and exit.- If the coordinates of the points are out of the range.
- If the number of points is not equal to the defined number.
- If the type of the object is not one of the defined types.
- The type of the object, the number of points, and the coordinates of the points.
- File name:
final2_1_<student_id>.cpp
(e.g.final2_1_106062802.cpp
)
Notice:
- The program will not print any user prompts.
- The program should use functions to process the input and output.
- The program should define structures to store the data of the objects.
- The program should be finished within 30 seconds. Any test cases will garuntee the program is finished within 30 second
Format
<type> <number_of_points>⏎
<x_1> <y_1>⏎
...
<x_n> <y_n>⏎
Object Type: <type>
Number of Points: <number_of_points>
Points: (<x_1>, <y_1>), ..., (<x_n>, <y_n>)
Example
$ ./a.out⏎
point 1⏎
0.0 0.0⏎
Object Type: point
Number of Points: 1
Points: (0, 0)
$ ./a.out⏎
circle 2⏎
0.0 0.0⏎
1.0 1.0⏎
Object Type: circle
Number of Points: 2
Point: (0, 0), (1, 1)
$ ./a.out⏎
triangle 3⏎
0.0 0.0⏎
2.0 0.0⏎
1.0 1.0⏎
Object Type: triangle
Number of Points: 3
Point: (0, 0), (2, 0), (1, 1)
$ ./a.out⏎
rectangle 2⏎
0.0 0.0⏎
1.0 2.0⏎
Object Type: rectangle
Number of Points: 2
Point: (0, 0), (1, 2)
$ ./a.out⏎
rectangle 3⏎
Invalid Object
$ ./a.out⏎
circle -1⏎
Invalid Object
$ ./a.out⏎
circle 2⏎
-100.1 -100.1⏎
1.0 1.0⏎
Invalid Object
$ ./a.out⏎
point 101⏎
Invalid Object
$ ./a.out⏎
square 2⏎
Invalid Object
Pseudo code
#include <iostream>
#include <vector>
#include <string>
using namespace std;
typedef struct Point_t
{
double x;
double y;
} Point;
typedef struct Triangle_t
{
Point p[3];
} Triangle;
typedef struct Circle_t
{
Point p[2];
} Circle;
typedef struct Rectangle_t
{
Point p[2];
} Rectangle;
// parse_header
// Parses the first line of the input.
void parse_header(string &type, int &number_of_points);
// parse_geometry
// Parses the list of points based on the type of geometry.
// Hint: check the number of points and the coordinates.
void parse_geometry(Point &p, const int &number_of_points);
void parse_geometry(Triangle &t, const int &number_of_points);
void parse_geometry(Circle &c, const int &number_of_points);
void parse_geometry(Rectangle &r, const int &number_of_points);
// check_geometry
// Checks if the geometry is valid.
bool check_geometry(const Point &p);
bool check_geometry(const Triangle &t);
bool check_geometry(const Circle &c);
bool check_geometry(const Rectangle &r);
// print_geometry
// Prints the geometry.
void print_geometry(const Point &p);
void print_geometry(const Triangle &t);
void print_geometry(const Circle &c);
void print_geometry(const Rectangle &r);
// print_point
// Prints the coordinates of a point.
void print_point(const Point &p);
int main()
{
string type;
int number_of_points;
parse_header(type, number_of_points);
if (type == "point")
{
Point p;
parse_geometry(p, number_of_points);
print_geometry(p);
}
else if (type == "triangle")
{
Triangle t;
parse_geometry(t, number_of_points);
print_geometry(t);
}
else if (type == "circle")
{
Circle c;
parse_geometry(c, number_of_points);
print_geometry(c);
}
else if (type == "rectangle")
{
Rectangle r;
parse_geometry(r, number_of_points);
print_geometry(r);
}
else
{
cout << "Invalid Object" << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Reference Code:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
typedef struct Point_t
{
double x;
double y;
} Point;
typedef struct Triangle_t
{
Point p[3];
} Triangle;
typedef struct Circle_t
{
Point p[2];
} Circle;
typedef struct Rectangle_t
{
Point p[2];
} Rectangle;
// parse_header
// Parses the first line of the input.
void parse_header(string &type, int &number_of_points);
// parse_geometry
// Parses the list of points based on the type of geometry.
// Hint: check the number of points and the coordinates.
void parse_geometry(Point &p, const int &number_of_points);
void parse_geometry(Triangle &t, const int &number_of_points);
void parse_geometry(Circle &c, const int &number_of_points);
void parse_geometry(Rectangle &r, const int &number_of_points);
// check_geometry
// Checks if the geometry is valid.
bool check_geometry(const Point &p);
bool check_geometry(const Triangle &t);
bool check_geometry(const Circle &c);
bool check_geometry(const Rectangle &r);
// print_geometry
// Prints the geometry.
void print_geometry(const Point &p);
void print_geometry(const Triangle &t);
void print_geometry(const Circle &c);
void print_geometry(const Rectangle &r);
// print_point
// Prints the coordinates of a point.
void print_point(const Point &p);
int main()
{
string type;
int number_of_points;
parse_header(type, number_of_points);
if (type == "point")
{
Point p;
parse_geometry(p, number_of_points);
print_geometry(p);
}
else if (type == "triangle")
{
Triangle t;
parse_geometry(t, number_of_points);
print_geometry(t);
}
else if (type == "circle")
{
Circle c;
parse_geometry(c, number_of_points);
print_geometry(c);
}
else if (type == "rectangle")
{
Rectangle r;
parse_geometry(r, number_of_points);
print_geometry(r);
}
else
{
cout << "Invalid Object" << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
void parse_header(string &type, int &number_of_points)
{
cin >> type >> number_of_points;
}
void parse_geometry(Point &p, const int &number_of_points)
{
if (number_of_points != 1)
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
for (int i = 0; i < number_of_points; i++)
{
cin >> p.x >> p.y;
}
if (!check_geometry(p))
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
}
void parse_geometry(Triangle &t, const int &number_of_points)
{
if (number_of_points != 3)
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
for (int i = 0; i < number_of_points; i++)
{
cin >> t.p[i].x >> t.p[i].y;
}
if (!check_geometry(t))
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
}
void parse_geometry(Circle &c, const int &number_of_points)
{
if (number_of_points != 2)
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
for (int i = 0; i < number_of_points; i++)
{
cin >> c.p[i].x >> c.p[i].y;
}
if (!check_geometry(c))
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
}
void parse_geometry(Rectangle &r, const int &number_of_points)
{
if (number_of_points != 2)
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
for (int i = 0; i < number_of_points; i++)
{
cin >> r.p[i].x >> r.p[i].y;
}
if (!check_geometry(r))
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
}
bool check_geometry(const Point &p)
{
if (p.x < -100.0 || p.y < -100.0 || p.x > 100.0 || p.y > 100.0)
{
return false;
}
return true;
}
bool check_geometry(const Triangle &t)
{
for (int i = 0; i < 3; i++)
{
if (!check_geometry(t.p[i]))
{
return false;
}
}
return true;
}
bool check_geometry(const Circle &c)
{
if (!check_geometry(c.p[0]) || !check_geometry(c.p[1]))
{
return false;
}
return true;
}
bool check_geometry(const Rectangle &r)
{
if (!check_geometry(r.p[0]) || !check_geometry(r.p[1]))
{
return false;
}
return true;
}
void print_geometry(const Point &p)
{
cout << "Object Type: point" << endl;
cout << "Number of Points: 1" << endl;
cout << "Point: ";
print_point(p);
cout << endl;
}
void print_geometry(const Triangle &t)
{
cout << "Object Type: triangle" << endl;
cout << "Number of Points: 3" << endl;
cout << "Point: ";
for (int i = 0; i < 3; i++)
{
if (i != 0)
{
cout << ", ";
}
print_point(t.p[i]);
}
cout << endl;
}
void print_geometry(const Circle &c)
{
cout << "Object Type: circle" << endl;
cout << "Number of Points: 2" << endl;
cout << "Point: ";
for (int i = 0; i < 2; i++)
{
if (i != 0)
{
cout << ", ";
}
print_point(c.p[i]);
}
cout << endl;
}
void print_geometry(const Rectangle &r)
{
cout << "Object Type: rectangle" << endl;
cout << "Number of Points: 2" << endl;
cout << "Point: ";
for (int i = 0; i < 2; i++)
{
if (i != 0)
{
cout << ", ";
}
print_point(r.p[i]);
}
cout << endl;
}
void print_point(const Point &p)
{
cout << "(" << p.x << ", " << p.y << ")";
}
Final Exam 2-2: Compute Object Area (15%)
- Inputs: The gemoetry object.
- The first line of the input contains the type of the object and the number of points.
- The type of the object is one of the following:
point
: a point.circle
: a circle, which is defined by two points, center of the circle and a point on the circle.triangle
: a triangle, which is defined by three points.rectangle
: a rectangle, which is defined by two points.
- The number of points is an
int
between (1) and (100).
- The type of the object is one of the following:
- The remaining lines of the input contain the coordinates of the points.
- The coordinates of the points are
double
between (-100) and (100). - The order of the points is always counter-clockwise or down to up.
- The coordinates of the points are
- The first line of the input contains the type of the object and the number of points.
- Outputs:
- The type of the object, the number of points, the coordinates of the points, and the area of the object.
- The format is shown below.
- The coordinates of the points are in defualt
double
format instd::cout
. - The area of the object is in
double
format instd::cout
.
- If the object is invalid, the output is
Invalid Object
and exit.- If the coordinates of the points are out of the range.
- If the number of points is not equal to the defined number.
- If the type of the object is not one of the defined types.
- If the coordinates of the points are not in the order of counter-clockwise.
- The type of the object, the number of points, the coordinates of the points, and the area of the object.
- File name:
final2_2_<student_id>.cpp
(e.g.final2_2_106062802.cpp
)
Notice:
- The program will not print any user prompts.
- The program should use functions to process the input and output.
- The program should define structures to store the data of the objects.
- The program should be finished within 30 seconds. Any test cases will garuntee the program is finished within 30 second
Format
<type> <number_of_points>⏎
<x_1> <y_1>⏎
...
<x_n> <y_n>⏎
Object Type: <type>
Number of Points: <number_of_points>
Points: (<x_1>, <y_1>), ..., (<x_n>, <y_n>)
Area: <area>
Example
$ ./a.out⏎
point 1⏎
0.0 0.0⏎
Object Type: point
Number of Points: 1
Point: (0, 0)
Area: 0
$ ./a.out⏎
circle 2⏎
0.0 0.0⏎
1.0 1.0⏎
Object Type: circle
Number of Points: 2
Point: (0, 0), (1, 1)
Area: 6.28319
$ ./a.out⏎
triangle 3⏎
0.0 0.0⏎
2.0 0.0⏎
1.0 1.0⏎
Object Type: triangle
Number of Points: 3
Point: (0, 0), (2, 0), (1, 1)
Area: 1
$ ./a.out⏎
rectangle 2⏎
0.0 0.0⏎
1.0 2.0⏎
Object Type: rectangle
Number of Points: 2
Point: (0, 0), (1, 2)
Area: 2
$ ./a.out⏎
rectangle 3⏎
Invalid Object
$ ./a.out⏎
circle -1⏎
Invalid Object
$ ./a.out⏎
circle 2⏎
-100.1 -100.1⏎
1.0 1.0⏎
Invalid Object
$ ./a.out⏎
point 101⏎
Invalid Object
$ ./a.out⏎
square 2⏎
Invalid Object
$ ./a.out⏎
circle 2⏎
1.0 1.0⏎
-100.0 -100.0⏎
Invalid Object
$ ./a.out⏎
triangle 3⏎
0.0 0.0⏎
1.0 1.0⏎
2.0 0.0⏎
Invalid Object
Pseudo code
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
using namespace std;
typedef struct Point_t
{
double x;
double y;
} Point;
typedef struct Triangle_t
{
Point p[3];
} Triangle;
typedef struct Circle_t
{
Point p[2];
} Circle;
typedef struct Rectangle_t
{
Point p[2];
} Rectangle;
const double PI = 3.14159265358979323846;
// parse_header
// Parses the first line of the input.
void parse_header(string &type, int &number_of_points);
// parse_geometry
// Parses the list of points based on the type of geometry.
// Hint: check the number of points and the coordinates.
void parse_geometry(Point &p, const int &number_of_points);
void parse_geometry(Triangle &t, const int &number_of_points);
void parse_geometry(Circle &c, const int &number_of_points);
void parse_geometry(Rectangle &r, const int &number_of_points);
// check_geometry
// Checks if the geometry is valid.
bool check_geometry(const Point &p);
bool check_geometry(const Triangle &t);
bool check_geometry(const Circle &c);
bool check_geometry(const Rectangle &r);
// print_geometry
// Prints the geometry.
void print_geometry(const Point &p);
void print_geometry(const Triangle &t);
void print_geometry(const Circle &c);
void print_geometry(const Rectangle &r);
// print_point
// Prints the coordinates of a point.
void print_point(const Point &p);
// area_geometry
// Calculates the area of the geometry.
double area_geometry(const Point &p);
double area_geometry(const Triangle &t);
double area_geometry(const Circle &c);
double area_geometry(const Rectangle &r);
int main()
{
string type;
int number_of_points;
parse_header(type, number_of_points);
if (type == "point")
{
Point p;
parse_geometry(p, number_of_points);
print_geometry(p);
}
else if (type == "triangle")
{
Triangle t;
parse_geometry(t, number_of_points);
print_geometry(t);
}
else if (type == "circle")
{
Circle c;
parse_geometry(c, number_of_points);
print_geometry(c);
}
else if (type == "rectangle")
{
Rectangle r;
parse_geometry(r, number_of_points);
print_geometry(r);
}
else
{
cout << "Invalid Object" << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Reference Code:
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
using namespace std;
typedef struct Point_t
{
double x;
double y;
} Point;
typedef struct Triangle_t
{
Point p[3];
} Triangle;
typedef struct Circle_t
{
Point p[2];
} Circle;
typedef struct Rectangle_t
{
Point p[2];
} Rectangle;
const double PI = 3.14159265358979323846;
// parse_header
// Parses the first line of the input.
void parse_header(string &type, int &number_of_points);
// parse_geometry
// Parses the list of points based on the type of geometry.
// Hint: check the number of points and the coordinates.
void parse_geometry(Point &p, const int &number_of_points);
void parse_geometry(Triangle &t, const int &number_of_points);
void parse_geometry(Circle &c, const int &number_of_points);
void parse_geometry(Rectangle &r, const int &number_of_points);
// check_geometry
// Checks if the geometry is valid.
bool check_geometry(const Point &p);
bool check_geometry(const Triangle &t);
bool check_geometry(const Circle &c);
bool check_geometry(const Rectangle &r);
// print_geometry
// Prints the geometry.
void print_geometry(const Point &p);
void print_geometry(const Triangle &t);
void print_geometry(const Circle &c);
void print_geometry(const Rectangle &r);
// print_point
// Prints the coordinates of a point.
void print_point(const Point &p);
// area_geometry
// Calculates the area of the geometry.
double area_geometry(const Point &p);
double area_geometry(const Triangle &t);
double area_geometry(const Circle &c);
double area_geometry(const Rectangle &r);
int main()
{
string type;
int number_of_points;
parse_header(type, number_of_points);
if (type == "point")
{
Point p;
parse_geometry(p, number_of_points);
print_geometry(p);
}
else if (type == "triangle")
{
Triangle t;
parse_geometry(t, number_of_points);
print_geometry(t);
}
else if (type == "circle")
{
Circle c;
parse_geometry(c, number_of_points);
print_geometry(c);
}
else if (type == "rectangle")
{
Rectangle r;
parse_geometry(r, number_of_points);
print_geometry(r);
}
else
{
cout << "Invalid Object" << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
void parse_header(string &type, int &number_of_points)
{
cin >> type >> number_of_points;
}
void parse_geometry(Point &p, const int &number_of_points)
{
if (number_of_points != 1)
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
for (int i = 0; i < number_of_points; i++)
{
cin >> p.x >> p.y;
}
if (!check_geometry(p))
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
}
void parse_geometry(Triangle &t, const int &number_of_points)
{
if (number_of_points != 3)
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
for (int i = 0; i < number_of_points; i++)
{
cin >> t.p[i].x >> t.p[i].y;
}
if (!check_geometry(t))
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
}
void parse_geometry(Circle &c, const int &number_of_points)
{
if (number_of_points != 2)
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
for (int i = 0; i < number_of_points; i++)
{
cin >> c.p[i].x >> c.p[i].y;
}
if (!check_geometry(c))
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
}
void parse_geometry(Rectangle &r, const int &number_of_points)
{
if (number_of_points != 2)
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
for (int i = 0; i < number_of_points; i++)
{
cin >> r.p[i].x >> r.p[i].y;
}
if (!check_geometry(r))
{
cout << "Invalid Object" << endl;
exit(EXIT_FAILURE);
}
}
bool check_geometry(const Point &p)
{
if (p.x < -100.0 || p.y < -100.0 || p.x > 100.0 || p.y > 100.0)
{
return false;
}
return true;
}
bool check_geometry(const Triangle &t)
{
for (int i = 0; i < 3; i++)
{
if (!check_geometry(t.p[i]))
{
return false;
}
}
double ccw = (t.p[1].x - t.p[0].x) * (t.p[2].y - t.p[0].y)
- (t.p[2].x - t.p[0].x) * (t.p[1].y - t.p[0].y);
return (ccw > 0);
}
bool check_geometry(const Circle &c)
{
if (!check_geometry(c.p[0]) || !check_geometry(c.p[1]))
{
return false;
};
return (c.p[1].y > c.p[0].y);
}
bool check_geometry(const Rectangle &r)
{
if (!check_geometry(r.p[0]) || !check_geometry(r.p[1]))
{
return false;
}
return (r.p[1].y > r.p[0].y);
}
void print_geometry(const Point &p)
{
cout << "Object Type: point" << endl;
cout << "Number of Points: 1" << endl;
cout << "Point: ";
print_point(p);
cout << endl;
cout << "Area: " << area_geometry(p) << endl;
}
void print_geometry(const Triangle &t)
{
cout << "Object Type: triangle" << endl;
cout << "Number of Points: 3" << endl;
cout << "Point: ";
for (int i = 0; i < 3; i++)
{
if (i != 0)
{
cout << ", ";
}
print_point(t.p[i]);
}
cout << endl;
cout << "Area: " << area_geometry(t) << endl;
}
void print_geometry(const Circle &c)
{
cout << "Object Type: circle" << endl;
cout << "Number of Points: 2" << endl;
cout << "Point: ";
for (int i = 0; i < 2; i++)
{
if (i != 0)
{
cout << ", ";
}
print_point(c.p[i]);
}
cout << endl;
cout << "Area: " << area_geometry(c) << endl;
}
void print_geometry(const Rectangle &r)
{
cout << "Object Type: rectangle" << endl;
cout << "Number of Points: 2" << endl;
cout << "Point: ";
for (int i = 0; i < 2; i++)
{
if (i != 0)
{
cout << ", ";
}
print_point(r.p[i]);
}
cout << endl;
cout << "Area: " << area_geometry(r) << endl;
}
void print_point(const Point &p)
{
cout << "(" << p.x << ", " << p.y << ")";
}
double area_geometry(const Point &p)
{
return 0.0;
}
double area_geometry(const Triangle &t)
{
double area = 0.0;
for (int i = 0; i < 3; i++)
{
area += t.p[i].x * t.p[(i + 1) % 3].y - t.p[i].y * t.p[(i + 1) % 3].x;
}
return abs(area / 2.0);
}
double area_geometry(const Circle &c)
{
double x_diff = c.p[1].x - c.p[0].x;
double y_diff = c.p[1].y - c.p[0].y;
double radius = sqrt(x_diff * x_diff + y_diff * y_diff);
return PI * radius * radius;
}
double area_geometry(const Rectangle &r)
{
return abs(r.p[0].x - r.p[1].x) * abs(r.p[0].y - r.p[1].y);
}
C++ References
- unzip
html-book-20201016.zip
. - open
html-book-20201016/reference/en/index.html
. - Find what you want.