###### tags: `程式基礎`
# [HW] 第二週(06/22 ~ 06/28):程式基礎(上)
> 掌握 JavaScript 基礎
# 作業
備註:以下題目請看清楚題目要求,如果要你「印出」,代表你要在 function 內使用`console.log`印出正確答案,如果要你「回傳」,則是用`return`把正確的值傳回去。
在提交作業以前麻煩把自己份內的事情做好,確認我範例給的 input 你都輸出了正確的 output 才提交。
## hw1:印出星星
給定 n(1<=n<=30),依照規律「印出」正確圖形
```
printStars(1)
正確輸出:
*
```
```
printStars(3)
正確輸出:
*
*
*
```
```
printStars(6)
正確輸出:
*
*
*
*
*
*
```
解法:
```
function printStars(n) {
for (var i = 1; i <= n; i++) {
console.log('*')
}
}
printStars(6)
```
解:
```
function printStars(n) {
for (let i = 0; i < n; i += 1) {
console.log('*');
}
}
```
## hw2:首字母大寫
給定一字串,把第一個字轉成大寫之後「回傳」,若第一個字不是英文字母則忽略。
```
capitalize('nick')
正確回傳值:Nick
capitalize('Nick')
正確回傳值:Nick
capitalize(',hello')
正確回傳值:,hello
```
解法:
```
function capitalize(str) {
var answer = ''
if (str[0] >= 'a' && str[0] <= 'z') {
answer = String.fromCharCode(str.charCodeAt(0) - 32)
for (var i = 1; i < str.length; i++){
answer += str[i]
}
} else {
answer = str
}
return answer
}
```
解:
有很多人都會用 charAt 或是先把字串用 split 變成陣列再來做這題,但其實在 JS 裡面你本來就可以用 str[0] 取到第一個字,不需要用 chatAt。
另一個常見問題是會檢查第一個字是否是小寫再轉,但內建的 toUpperCase 如果本來就是大寫,轉完也還是大寫,想一下之後會發現根本不需要檢查。
```
function capitalize(str) {
return str[0].toUpperCase() + str.slice(1);
}
```
## hw3:反轉字串
給定一個字串,請「印出」反轉之後的樣子(不能使用內建的 `reverse` 函式)
```
reverse('yoyoyo')
正確輸出:oyoyoy
reverse('1abc2')
正確輸出:2cba1
reverse('1,2,3,2,1')
正確輸出:1,2,3,2,1
```
解法:
```
function reverse(str) {
var answer = ''
for (var i = 1; i <= str.length; i++) {
answer += str[str.length-i]
}
console.log(answer)
}
```
解:
迴圈倒著做就好
```
function reverse(str) {
let result = '';
for (let i = str.length - 1; i >= 0; i -= 1) {
result += str[i];
}
console.log(result);
}
```
## hw4:印出因數
先幫大家複習一下數學,給定一個數字 n,因數就是所有小於等於 n 又可以被 n 整除的數,所以最明顯的例子就是 1 跟 n,這兩個數一定是 n 的因數。現在請寫出一個函式來「印出」所有的因數
```
printFactor(10)
正確輸出:
1
2
5
10
```
```
printFactor(7)
正確輸出:
1
7
```
解法:
```
function printFactor(n) {
for (var i=1; i<=n; i++) {
if (n % i === 0) {
console.log(i)
}
}
}
```
解:
```
function printFactor(n) {
for (let i = 1; i <= n; i += 1) {
if (n % i === 0) {
console.log(i);
}
}
}
```
## hw5:自己的函式自己寫
其實仔細思考的話,你會發現那些陣列內建的函式你其實都寫得出來,因此這一題就是要讓你自己動手實作那些函式!
我們要實作的函式有兩個:join 以及 repeat。(再次強調,這一題要你自己實作這些函式,所以你不會用到內建的`join`以及`repeat`)
join 會接收兩個參數:一個陣列跟一個字串,會在陣列的每個元素中間插入一個字串,最後回傳合起來的字串。
repeat 的話就是回傳重複 n 次之後的字串。
```
join([1, 2, 3], ''),正確回傳值:123
join(["a", "b", "c"], "!"),正確回傳值:a!b!c
join(["a", 1, "b", 2, "c", 3], ','),正確回傳值:a,1,b,2,c,3
repeat('a', 5),正確回傳值:aaaaa
repeat('yoyo', 2)正確回傳值:yoyoyoyo
```
解法:
```
// join
function join(arr, concatStr) {
var result = arr[0]
for (var i = 1; i < arr.length; i++) {
result += concatStr + arr[i]
}
return result
}
```
解:
這一題最常見的錯誤是最後面多了一個連接的字串。
另外,當陣列是空的的時候可以特別處理,在這情形下應該要回傳空字串
```
function join(arr, concatStr) {
if (arr.length === 0) { // special case
return '';
}
let result = arr[0];
for (let i = 1; i < arr.length; i += 1) {
result += concatStr + arr[i];
}
return result;
}
```
```
// repeat
function repeat(str, times) {
var answer = ''
for (var i = 1; i <= times; i++) {
answer += str
}
return answer
}
```
解:
repeat 比較容易,就迴圈一下
```
function repeat(str, times) {
let result = '';
for (let i = 0; i < times; i += 1) {
result += str;
}
return result;
}
```
## hw6:簡答題
在學程式的時候有一個能力很重要,你必須靜下心來一行一行看這個程式到底在幹嘛,並且在腦中模擬出這個程式執行的樣子,意思就是你要假裝自己就是 JS 引擎。
這是一個非常實用的技能,我來舉個例子。
``` js
for(var i=59; i<=61; i++) {
if(i === 60) {
console.log('剛好及格')
} else if (i < 60) {
console.log('不及格')
} else {
console.log('及格')
}
}
```
1. 執行第 1 行,設定變數 i 是 59,檢查 i 是否 <= 61,是,繼續執行,開始進入第一圈迴圈
2. 執行第 2 行,判斷 i 是否等於 60,不是,繼續往下
3. 執行第 4 行,判斷 i 是否小於 60,是
4. 執行第 5 行,log 不及格
5. 第一圈迴圈結束,跑回第一行,i++,i 變成 60,檢查是否 <= 61,是,繼續執行
6. 執行第 2 行,判斷 i 是否等於 60,是
7. 執行第 3 行,log 剛好及格
8. 第二圈迴圈結束,跑回第一行,i++,i 變成 61,檢查是否 <= 61,是,繼續執行
9. 執行第 2 行,判斷 i 是否等於 60,不是,繼續往下
10. 執行第 4 行,判斷 i 是否小於 60,不是,繼續往下
11. 執行第 6 行並進入到第 7 行,log 及格
12. 第三圈迴圈結束,跑回第一行,i++,i 變成 62,檢查是否 <= 61,否
13. 執行完畢
寫起來非常冗長,但每寫一步都會讓你對這個程式怎麼運作的變得更清晰,對程式思維非常有幫助。而且一但你習慣了這樣的方式,很快地對於一些簡單的情形你就不需要寫下來了,你可以直接用大腦模擬出程式執行的樣子。
現在,請假裝自己是電腦,像是上面示範的那樣,一步步寫下底下這個程式的執行流程,並且試著猜猜看它在做什麼:
``` js
function isValid(arr) {
for(var i=0; i<arr.length; i++) {
if (arr[i] <= 0) return 'invalid'
}
for(var i=2; i<arr.length; i++) {
if (arr[i] !== arr[i-1] + arr[i-2]) return 'invalid'
}
return 'valid'
}
isValid([3, 5, 8, 13, 22, 35])
```
請將答案寫在 [hw6.md](hw6.md)。
1. 執行第 11 行,呼叫函式 isValid(arr) 並進入第 1 行,設定引數為 [3, 5, 8, 13, 22, 35]
2. 執行第 2 行,設定變數 i 是 0,檢查 i 是否小於陣列長度,是,繼續執行,開始進入第一圈迴圈
3. 執行第 3 行,判斷 arr 第 i 個值是否 <= 0,不是
4. 第一圈迴圈結束,跑回第 2 行,i++,i 變成 1,檢查 i 是否小於陣列長度,是,繼續執行
5. 執行第 3 行,判斷 arr 第 i 個值是否 <= 0,不是
6. 第二圈迴圈結束,跑回第 2 行,i++,i 變成 2,檢查 i 是否小於陣列長度,是,繼續執行
7. 執行第 3 行,判斷 arr 第 i 個值是否 <= 0,不是
8. 第三圈迴圈結束,跑回第 2 行,i++,i 變成 3,檢查 i 是否小於陣列長度,是,繼續執行
9. 執行第 3 行,判斷 arr 第 i 個值是否 <= 0,不是
10. 第四圈迴圈結束,跑回第 2 行,i++,i 變成 4,檢查 i 是否小於陣列長度,是,繼續執行
11. 執行第 3 行,判斷 arr 第 i 個值是否 <= 0,不是
12. 第五圈迴圈結束,跑回第 2 行,i++,i 變成 5,檢查 i 是否小於陣列長度,是,繼續執行
13. 執行第 3 行,判斷 arr 第 i 個值是否 <= 0,不是
14. 第六圈迴圈結束,跑回第 2 行,i++,i 變成 6,檢查 i 是否小於陣列長度,否,繼續往下
15. 執行第 5 行,設定變數 i 是 2,檢查 i 是否小於陣列長度,是,繼續執行,開始進入第一圈迴圈
16. 執行第 6 行,判斷 arr 第 i 個值是否 !== arr 第 i-1 個值 + arr 第 i-2 個值,不是
17. 第一圈迴圈結束,跑回第 5 行,i++,i 變成 3,檢查 i 是否小於陣列長度,是,繼續執行
18. 執行第 6 行,判斷 arr 第 i 個值是否 !== arr 第 i-1 個值 + arr 第 i-2 個值,不是
19. 第二圈迴圈結束,跑回第 5 行,i++,i 變成 4,檢查 i 是否小於陣列長度,是,繼續執行
20. 執行第 6 行,判斷 arr 第 i 個值是否 !== arr 第 i-1 個值 + arr 第 i-2 個值,是,回傳字串 invalid
21. 執行完畢
由執行流程猜測,函式 isValid 能夠接收一個陣列,並進行下列兩種判斷:
1. 陣列中數字是否均大於零:是
2. 從陣列第三個數字開始,是否與該數字的前兩個數字總和相等:否,因為 arr[4] 不等於 arr[3] + arr[2],因此回傳 invalid
執行流程就不多講了,這個當作人體 debugger 一行一行寫出來就好,而這個函式做的檢查是:確定陣列前兩項的和是不是等於下一項,是的話就回傳 valid,如果不是或者陣列裡面有元素 <=0 的話,也回傳 invalid。
對於這種「前兩項的和要等於下一項」的數列,有個專有名詞叫做費氏數列,有興趣的朋友們可以去查查看,這個在電腦科學界裡面非常有名。