## Titan
### Q2
命名用縮寫:例如rl?
建議封裝成function,可叫做"askQuestion"或是"getUserInput"之類的名稱
要去釐清意圖:主程式單純為了使用者輸入,我的主程式會看起來複雜
rl.question是callback,容易有callback hell,練習用promise >> 再用async await
驗證部份:
```javascript=
isEmpty(input);
isInteger(input);
isNonNegativeInteger(input);
//Titan建議 尚未理解...
function(input,[isEmpty,isInteger,isNonNegativeInteger])
```
isNonNegativeInteger(input)的命名,取名變成反向?
每一題驗證客製錯誤訊息(看個人)
可以有字串驗證器,數字驗證器
驗證器方向:
* 預設驗證器 >> 可以共用
* 客製化驗證器 >>用在每一題客製
```javascript!
export function compareRemainder([firstNumber, secondNumber]) {
const divisor = 3;
if (firstNumber % divisor === secondNumber % divisor) {
return `餘數相同`;
} else {
return `餘數不同`;
}
}
//建議餘數相同、餘數不同不要寫死,並用布林值方式傳出去給main
//divisor可以當作參數
```
[function signature函數簽名](https://developer.mozilla.org/zh-CN/docs/Glossary/Signature/Function):定義函數的輸入輸出
我的函式會因為外部傳入的參數而配合改變
### Q5module
複用
例如:(n-2)*2重複,宣告變數存放,之後好維護
建議都用const宣告
## 季芳
#### Q2
```javascript!
//原本寫法
//在全域宣告一個answer的空陣列存放,容易被改動到
const array = [];
main();
function main() {
rl.question(`輸入第1個整數數字:`, (inputNumber) => {
try {
isEmpty(inputNumber);
isInteger(inputNumber);
isZeroAndPositiveInteger(inputNumber);
const integerOne = Number(inputNumber);
array.push(integerOne);
askSecondQuestion(integerOne);
function askSecondQuestion(integerOne) {
rl.question(`輸入第2個整數數字:`, (inputNumberTwo) => {
try {
isEmpty(inputNumberTwo);
isInteger(inputNumberTwo);
isZeroAndPositiveInteger(inputNumberTwo);
const integerTwo = Number(inputNumberTwo);
array.push(integerTwo);
const result = compareRemainder(integerOne, integerTwo, 3);
console.log(result);
rl.close();
} catch (error) {
console.log(error.message);
askSecondQuestion(integerOne);
}
});
}
} catch (error) {
console.log(error.message);
main();
}
});
}
```
```javascript!
//修改成把數字存放在askSecondQuestion,function的參數,不使用array
//把askSecondQuestion拉出來跟第一個rl.question同層
// const array = [];
main();
function main() {
rl.question(`輸入第1個整數數字:`, (inputNumber) => {
try {
isEmpty(inputNumber);
isInteger(inputNumber);
isZeroAndPositiveInteger(inputNumber);
const integerOne = Number(inputNumber);
// array.push(integerOne);
askSecondQuestion(integerOne);
} catch (error) {
console.log(error.message);
main();
}
});
function askSecondQuestion(integerOne) {
rl.question(`輸入第2個整數數字:`, (inputNumberTwo) => {
try {
isEmpty(inputNumberTwo);
isInteger(inputNumberTwo);
isZeroAndPositiveInteger(inputNumberTwo);
const integerTwo = Number(inputNumberTwo);
// array.push(integerTwo);
const result = compareRemainder(integerOne, integerTwo, 3);
console.log(result);
rl.close();
} catch (error) {
console.log(error.message);
askSecondQuestion(integerOne);
}
});
}
}
```
* 主程式main巢狀,建議改用Promise >> 再用async await
```javascript!
//answer.length<2,我的2寫死,也有另一種寫法Array.fill做出陣列
if (answer.length < 2) {
main();
} else {
const result = compareRemainder(answer, divisor);
console.log(result);
rl.close();
}
```
* 如果是+0、-0、科學記號,我的驗證都會過
* 都用const,有要修改再改成let
寫糙code >> 再來調整:
1. 重構
2. 語法
3. 排版
4. 寫完看學長姐code,看看別人的邏輯,可以多學習
## Voss
程式是由上到下,會建議main()寫在function main之後
```javascript!
function main(){
...
}
main();
```
* 宣告的位置要注意,會考慮作用域、效能等
* function命名一般都是動詞開頭,例如:askQuestion、getPrice等
* 如要註解用git commit幫你紀錄
* let和const的選擇,會先選用const,別人看到你用let會想說是不是等等哪裡會更動到
#### 驗證包裝問題
```javascript!
//原本這些驗證不知道怎麼包入validator裡面,因為isMoreThanNumber有兩個參數
isEmpty(userInput);
isInteger(userInput);
isPositiveNumber(userInput);
isEven(userInput);
isMoreThanNumber(userInput, 4);
//橘子修改
//使用柯里化方式,把原本isMoreThanNumber(input,number)兩個參數拆分,function return一個function
function isMoreThanNumber(number) {
return function (input) {
if (Number(input) < number) {
throw new Error(`請輸入大於${number}的數字`);
}
};
}
//isMoreThanNumber就能帶一個參數跑驗證了
validator(userInput, [isInteger,isPositiveNumber,isEven,isMoreThanNumber(4)]);
//驗證器
function validator(input, otherValidator) {
isEmpty(input); //共同,其他驗證用陣列跑forEach呼叫
otherValidator.forEach((item) => {
item(input);
});
}
```
#### return
```javascript!
//return 盡量都不要再有計算,就傳出要的東西就好
//原本
function listFomula(n) {
if (number === 4) {
return String(number - 2) + "*" + String(number);
} else {
return (
listFomula(number - 2) + "+" + String(number - 2) + "*" + String(number)
);
}
}
//修改成樣板字面值
function listFomula(n) {
let list = "";
if (n === 4) {
list = `${n - 2}*${n}`;
return list;
} else {
list = `${listFomula(n - 2)}+${n - 2}*${n}`;
return list;
}
}
```
for loop初始值,永遠都是從==0==開始
## Chris
### Q2
1. stdin(standard in) 一般是keyboard,stdout(standard out) 一般是screen
2. 如果有轉型的地方,用[匈牙利命名法](https://zh.wikipedia.org/zh-tw/%E5%8C%88%E7%89%99%E5%88%A9%E5%91%BD%E5%90%8D%E6%B3%95),一眼就能看出變數型別,其他沒轉型的地方就不使用
```javascript!
//原本寫法
const inputNubmer = Number(userInput);
//修改
const numberUserInput = Number(strUserInput);
```
3. try...catch...finally,[finally](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch#the_finally_block)多加運用(控制流退出之前都會執行finally裡面的語法)
```javascript!
//Chris修改Q2
const answers = [];
function keyboardInput(question, valid, answers) {
return new Promise((resolve, reject) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question(question, (input) => {
try {
validator(input, valid);
resolve([...answers, Number(input)]);
} catch (error) {
console.log(error.message);
reject(answers);
} finally {
rl.close();
}
});
});
}
function initial(answer) {
return new Promise((resolve) => {
resolve(answer);
});
}
initial(answers)
//這邊是輸入
.then(ask("請輸入第1個數字:", [isNumber, isInteger, isNonNegativeInteger]))
.then(ask("請輸入第2個數字:", [isNumber, isInteger, isNonNegativeInteger]))
//這邊是邏輯-->要單元測試的地方
.then((answers) => {
// const array = compareRemainder(answers);
// const result = isTheSameRemainder(array);
// return resultToMessage(result);
//驗證放來這邊 測試then裡面的邏輯
return [compareRemainder, isTheSameRemainder, resultToMessage].reduce(
(result, fn) => fn(result),
answers
);
})
//.then裡面函式可以不用加參數,因為是為了註冊進去
//.then(console.log)
// .then(compareRemainder)
// .then(isTheSameRemainder)
// .then(resultToMessage)
//這邊是輸出
.then((result) => console.log(result));
function isTheSameRemainder([firstNumber, secondNumber]) {
return firstNumber === secondNumber;
}
function resultToMessage(result) {
return result ? `餘數相同` : `餘數不同`;
}
function ask(question, valid) {
return (result) => {
return keyboardInput(question, valid, result).catch(ask(question, valid));
};
}
//原本寫法
function askQuestion(question, answers) {
return new Promise((resolve) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question(question, (input) => {
try {
validator(input, [isNumber, isInteger, isNonNegativeInteger]);
const inputNumber = Number(input);
answers.push(inputNumber);
rl.close();
resolve(answers);
} catch (error) {
console.log(error.message);
rl.close(); //錯了先關掉,再開接口
askQuestion(question, answers).then(resolve);
// console.log(askQuestion((question, answers)));
}
});
});
}
askQuestion("請輸入第1個數字:", answers)
.then((result) => askQuestion("請輸入第2個數字:", result))
.then((result) => (compareRemainder(result) ? `餘數相同` : `餘數不同`))
.then((finalResult) => console.log(finalResult));
```
4. 使用[functional programming函數式程式設計(FP)](https://ken-chen.medium.com/functional-programming-%E6%98%AF%E4%BB%80%E9%BA%BC-%E6%88%91%E5%8F%AA%E8%81%BD%E9%81%8E-oop-ce222618bc05):透過最小化增進程式碼可讀性
5. validtor可改成reduce跑
6. new Error的new一定要寫嗎?他自己是沒有寫
7. 變數轉型完,再放入function跑(不在驗證裡面轉型)
```javascript!
//Chris寫法
.then(resultToMessage)
.then(console.log);
//原本
.then((result)=>resultToMessage(result))
.then((result) => console.log(result));
```
### Q5
1. 數字太大要擋(會跳出max stack)
2. test要測正常和不正常
3. 使用new Function把數列字串直接計算值出來
```javascript!
//原本寫法
function umleven(n) {
if (n === 4) {
return (n - 2) * n;
} else {
return umleven(n - 2) + (n - 2) * n;
}
}
//列數列
function listFomula(n) {
if (n === 4) {
return `${n - 2}*${n}`;
} else {
return `${listFomula(n - 2)}+${n - 2}*${n}`;
}
}
//修改後
//雖然較少使用new Function,但在字串直接計算值很方便
function umleven(n) {
return new Function(`return ${listFomula(n)}`)();
}
```
### validator.js
```javascript=
//檢查是否為數字
export function isNumber(input) {
if (Number.isNaN(Number(input))) {
throw new Error("請勿輸入文字");
}
if (input.startsWith("0b")) {
throw new Error("此為二進制,請輸入十進制格式");
}
if (input.startsWith("0o")) {
throw new Error("此為八進制,請輸入十進制格式");
}
if (input.startsWith("0x")) {
throw new Error("此為十六進制,請輸入十進制格式");
}
//Chris不使用正規的寫法,來判斷科學記號
if (
input.includes("e") &&
input.split("e").length === 2 &&
input.split("e").every((item) => !Number.isNaN(Number(item)))
) {
throw new Error("此為科學記號,請輸入十進制格式");
}
}
```