我國中還真的當過數學小老師,有件事令我印象深刻

猶記得是冬天,冷颼颼地,身上穿著學校發的冬季外套

或許因為接近大考,當時每天數學課就是考試考試考試,所以那陣子我的任務很簡單,時間到就去找老師拿考卷。某天,覺得每次都這樣例行公事未免太無聊,決定捉弄一下同學,當時找老師拿完考卷後把外套拉鍊拉開、將考卷放進去並夾著、再把拉鍊拉起來,行走時還練習一下,讓自己走起來很自然,並且一派輕鬆地走回教室,當我踏進教室那一刻大家看著我,手上沒有考卷

「跟大家說個好消息」我走上台神情自若地說著

停頓一陣子欣賞大家的反應,果然,原本安靜的教室吵雜聲此起彼落

「什麼什麼」、「該不會不用考試吧」、「太爽了」、「他手上沒拿考卷該不會不用考吧」、「爽啦」、「耶」

我不疾不徐地接著說:

「好消息是,今天還是一樣有考試^^」語畢,手緩緩伸入外套拿出深藏的考卷

「……」、「@#$%^&*!!!!!!」

當天考什麼不記得了,只記得同學們譙到不行,還有人丟寶特瓶上來,這真的是我當數學小老師最快樂的回憶之一,不愧對燦爛的中二時光

結果在寫技術筆記前還不小心打了一段廢文,趕緊進入主題

以下這些是把最近練習 JS 遇到的相關數學語法整理一下,目前還不需要處理什麼很高深的東西,都是基礎簡易語法,初學者可以參考,不過需要注意的是,仔細去想會有些眉角,例如引數若為無限大會怎麼計算之類,語法都有弄超連結可以連到 MDN,有興趣的歡迎點進去瞭解更多,而因為這篇只有先列一些所以有點難分主題區塊,就簡單硬分一下

簡單的

⭐ 找最小值 Math.min()、找最大值 Math.max()

這兩個在陣列處理相關題目滿常用到的,很實用,還記得當初第一次看到這個語法就覺得自己很好笑,用一些土方法加加減減只為了找到最大最小值,結果人家一個語法就結束了,沒關係,成長總是苦澀的,就跟原本以為不用考試結果看到小老師從外套拿出考卷時的心情一樣

用法:

Math.min(1, 2, 3)
// 1

Math.max(1, 2, 3)
// 3

要注意,若括號裡傳入的值是陣列記得要用展開運算子(Spread Operator)把陣列展開:

let array = [1, 2, 3]
Math.max(...array)
// 3

⭐ 開根號 Math.sqrt()、次方 Math.pow()

第一次寫 JS 時還以為次方可以像 Excel 裡一樣直接用「^」,結果發現不行。這兩個語法用法:

Math.sqrt(9)
// 3

Math.sqrt(2)
// 1.4142135623730951

Math.pow(2, 3)
// 8

要注意,Math.pow() 有第二個參數,代表要計算的次方

⭐ 絕對值 Math.abs()

Math.abs(-2)
// 2

若字串本身內容為數字則可以把字串變成數字,若不是,則回傳 NaN(Not a Number):

Math.abs("-1")
// 1

Math.abs("string")
// NaN

繼續閱讀 | 回到目錄

跟整數或轉換有關

⭐ 確認是否為整數 Number.isInteger()

Number.isInteger(1)
// true

Number.isInteger(0.1)
// false

⭐ 把字串轉為整數 parseInt()

這個比較麻煩些,簡單說有兩個參數,第一個參數是要轉換的字串,若原本不是字串則會先被轉成字串,第二個參數則是進位制,例如 10 進位、2 進位,如果是一般用法其實很單純:

parseInt("3", 10)
// 3,因為字串 "3" 用 10 進位表達成整數就是 3

進一步講,轉換過程很神奇,會以字串內容從頭開始計算,直到遇到非數字就停止,然後把這中間的數字轉成整數,例如:

parseInt("123a456", 10)
// 123,因為遇到 a 發現不是數字所以 a 後面的就不管它了,因此就轉換 123

那如果第二個參數不是 10 呢?例如:

parseInt("1010", 10)
// 將 1010 以 10 進位轉成整數就是 1010

parseInt("1010", 2)
// 將 1010 以 2 進位轉成整數就是 10

那如果第一個參數放的根本與數字八竿子打不著呢?

parseInt("Hello", 10)
// NaN,不意外

其實第二個參數可以不用寫,若沒寫則有很大機率會預設為 10 進位,等一下,為什麼說「有很大機率」?因為其實少部份狀況會有預想不到的事發生,例如:

parseInt("0X10")
// 16

parseInt("0X1a")
// 26

啥???

是的,如果第二個參數沒寫,剛好字串又是 0x 或 0X 開頭,那 parseInt() 會自動預設為 16 進位,並且是把 0x 或 0X 之後的內容去轉換,就是這麼截然不同,若忘記 16 進位可以複習一下維基,除此之外還有些小細節,不過,在我的觀念裡數字處理不允許機率這種事,我的意思是,明明想表達 1,但卻有機會寫出來變成 2,即使機率是 0.001% 我也無法接受,想像自己是電商老闆結果工程師說價格有微小機率會標錯你會多崩潰XD,那怎麼避免?一律寫清楚第二個參數就好了咩

另外,因為 parseInt() 轉換過程有時也會有神奇的事發生,例如位數太多、以科學記號表達的數字等轉換狀況就會跟我們想的不一樣(想知道哪裡不一樣再附一下 MDN 給你),所以如果想轉換有小數點的可以用 parseFloat(),想把更多種非數字的內容轉成數字或減少奇怪狀況發生則可以用 Number(),以下接著簡單介紹一下這兩個

⭐ 把字串轉為浮點數 parseFloat()

關於浮點數的坑之前有研究過,之後瞭解更完整再來寫一篇,這邊就不談被浮點數雷的事,只簡單講字串轉成浮點數的用法:

parseFloat("3.14")
// 3.14

parseFloat() 只有一個參數,就是想轉換的字串,不用管進位制,一律設定是 10 進位

⭐ 把東西轉為數字 Number()

如果只是轉數字,其實用起來跟 parseInt() 一樣:

Number("123")
// 123

Number("3.14")
// 3.14

不過如果字串裡的數字夾雜奇怪的東西,那就會回傳 NaN:

Number("123a456")
// NaN

Number() 只有一個參數,所以不太需要管進位,除非有意為之:

Number("0x11")
// 17,0x 開頭直接用 16 進位算,而且是算 0x 之後的數字

Number("0b11")
// 3,0b 開頭則用 2 進位算,一樣算 0b 之後數字

Number("0o11")
// 9,0o 開頭則用 8 進位算,一樣算 0o 之後數字

Number() 也可以轉換布林值與 null:

Number(true)
// 1

Number(false)
// 0

Number(null)
// 0

看到這會不會覺得,那 Number() 跟 parseInt() 還有 parseFloat() 差在哪?

以初學者角度,我認為主要三個重點:

  • parseInt() 適合字串轉整數、parseFloat() 適合字串轉浮點數、Number() 適合更多種類的資料型態轉數字
  • Number() 如果括號內的引數值包含了非數字的雜質,則會直接回傳 NaN,除非有其他進位的考量
  • 依情境使用,例如若只想傳入的值(引數)為字串,那就不要用 Number(),或是想明確指定進位制則使用 parseInt(),之類的

繼續閱讀 | 回到目錄

跟小數點有關

⭐ 四捨五入成整數 Math.round()

Math.round(3.14)
// 3

Math.round(3.49)
// 3

那如果想要四捨五入到小數點後 2 位、3 位、或更多呢?先乘上想要的位數四捨五入後再除以那個位數,例如:

Math.round(3.149*100)/100
// 3.15,假設想四捨五入到小數點後 2 位,就先將 3.149 乘上 100,變成 314.9
// 再四捨五入變成 315,接著除以 100,就變成 3.15 了

⭐ 無條件捨去 Math.floor()、無條件進位 Math.ceil()

邏輯差不多,看語法名稱就很好理解,一個地板一個天花板:

Math.floor(3.14)
// 3

Math.ceil(3.14)
// 4

⭐顯示小數點後幾位 toFixed()

很直白,強迫顯示小數點後幾位數字(即使是 0.00 也能顯示):

3.14.toFixed(1)
// 3.1

3.149.toFixed(2)
// 3.15

請注意,這邊也會常踩到浮點數的坑,例如,測試 2.55.toFixed(1) 看會發生什麼事XD

繼續閱讀 | 回到目錄

稍微小複雜

⭐ 陣列方法 reduce()

雖然跟陣列有關,但處理陣列數字有時會用到,順便看一下。用法可以很多元,最簡單的用法就是把陣列裡的數字都加起來,在這種用法下主要會用到三個參數(可以用的不只三個),典型寫法如下:

reduce((accumulator, currentValue) => { /* … */ }, initialValue)

參數意思如下

  • initialValue:雖然位置在最後面但先從它開始講,因為計算會先用到,意思是要先以什麼數字為基底,再把所有陣列裡的數字一個個加上去,而該數字不一定要是陣列裡的數字,可以任意指定
  • accumulator:累積者,顧名思義就是我現在加到多少了
  • currentValue:哪個數字是我正要加到 accumulator 上的

看不懂很正常,看例子就知道在幹嘛了:

let arr = [1, 2, 3, 4, 5];

let reduceArr = arr.reduce((accumulator, currentValue) => {
console.log(accumulator); // 10, 11, 13, 16, 20, 25
console.log(currentValue); // 1, 2, 3, 4, 5
return accumulator + currentValue
}, 10);

console.log(reduceArr) // reduceArr 最後的值是25

中間那兩個 console.log 是為了方便講解,你也可以直接把這整段貼到 Replit 之類的地方,自行修改看要印出什麼後,看跑出來的結果。

accumulator 就是累積數,若未指定起始值(initialValue),accumulator 預設從 arr[0] 開始(也就是1),currentValue則從 arr[1] 開始(也就是2),然而,若有指定起始值,例如上面在 reduce() 最後面有個 10,意思就是從 10 開始,則 accumulator 從 10 起算,而 currentValue 則變成從 arr[0] 開始(原本是 arr[1]),也就是1

因此,把整個語法進行的邏輯白話說明就是,我要先從 10 起算,然後一個個把陣列裡的數字加上去,所以10 + 1 + 2 +3 + 4 + 5 = 25

而使用 reduce() 要注意,若陣列是空陣列,而剛好又沒指定 initialValue,那就會出錯,為什麼?因為如果沒指定就代表函式會把陣列的第 0 個元素當作 accumulator,可是此時陣列裡是空的,沒有元素可以當 accumulator,那程式就會出錯

繼續閱讀 | 回到目錄

練習題

看了這些語法,來實際練習一道題看看吧,這題是在 Codewars 看到的,不知道 Codewars 的話可以看這篇

題目創作者:kphurley

題目:Find the next perfect square!

描述:

You might know some pretty large perfect squares. But what about the NEXT one?

Complete the findNextSquare method that finds the next integral perfect square after the one passed as a parameter. Recall that an integral perfect square is an integer n such that sqrt(n) is also an integer.

If the parameter is itself not a perfect square then -1 should be returned. You may assume the parameter is non-negative.

Examples:(Input → Output)

121 → 144
625 → 676
114 → -1 since 114 is not a perfect square

想到怎麼解了嗎?

可以練習看看再繼續往下看

可以這樣寫:

function findNextSquare(sq) {
return Number.isInteger(Math.sqrt(sq)) ? Math.pow(Math.sqrt(sq) + 1, 2) : -1
}

用三元運算子(Ternary Operator)判斷,若該數字開根號後仍為整數(代表它是完全平方數),則回傳該整數 +1 後再平方的值,也就是下一個完全平方數,如果一開始判斷該數字的平方根非整數則直接回傳 -1

這個寫法可以一次練習 Number.isInteger()、Math.sqrt()、Math.pow(),還不賴

看到另一個解法也滿有趣的,與你分享:

function findNextSquare(sq) {
return Math.sqrt(sq) % 1 ? -1 : Math.pow(Math.sqrt(sq) + 1, 2);
}

概念差不多,但比較簡潔,直接用是否能被 1 整除來判斷是否為整數。例如,若 64 開根號後為 8,8 除以 1 的餘數為 0,所以為 false,回傳冒號右邊的值(也就是 8 +1 後再平方,81),若傳入值為 65,開根號後有小數點,則該數除以 1 的餘數為小數點,有東西,所以布林值為 true,回傳 -1

以上就是最近用到的數學語法,整理一下希望對初學者有幫助 😃

文章到底囉 | 回到目錄

--

--

Johnny Fang

把 Medium 當 Notion 用,寫一下 coding 學習筆記 | email: johnny781222@gmail.com | LinkedIn: www.linkedin.com/in/johnny-fang-9356b2156 | Discord 使用者名稱:johnnyfang