Day 8:JaveScript - 2D 陣列與矩陣操作;模組化函式設計;Vitest Snapshot 快照測試

2D 陣列與矩陣操作

JavaScript 沒有原生矩陣功能(很合理,畢竟是為了瀏覽器操作行為制定的語言,不是 MatLab 一開始就是為了算矩陣和線性代數🤣)

最接近的做法是可以用長度相同的巢狀陣列處理:

matrix = [
	[a1, a2, a3], // row 0
	[b1, b2, b3], // row 1
	[c1, c2, c3], // row 2
];

以 JS 原生語法來說

  • matrix:一個一維陣列裡面包了三個元素 [A, B, C],A, B, C 分別都是三個元素的陣列

  • matrix[0]:取第一個元素 A 當作第一列 row

  • matrix[0].length:A 的長度,就可以當作矩陣的 column 數

  • matrix.length:當作矩陣的 row 數

  • length只會回傳「陣列有幾個元素」,JS 原生語法沒有區分 row 和 column 的功能,只能用資料結構設定繞過去,當作「準」二維陣列使用

要確認每列長度是否都一樣就要用 some 檢查

const width = matrix[0].length;
const isInvalid = matrix.some((row) => row.length !== width);

若 isInvalid 為 true 代表這不是規則矩陣

所以操作方法都要用兩層巢狀分別遍歷處理 raw 和 column 每個元素

矩陣轉置 (transpose)

const transpose = matrix[0].map((_, c) =>
  matrix.map((_, r) => matrix[r][c])
);
  • matrix[0].map((_, c) => ...):用第一列的長度,當成「有多少欄位」的依據,所以外層 map 跑的是「欄 index c」

  • matrix.map((_, r) => matrix[r][c]):對每一列(row)取出同一個欄 c 的值,收集成新的一列

繼承與原型鏈、method chaining

發現矩陣轉置的寫法是用"."連續呼叫陣列方法而不用每個步驟宣告暫存變數

今天練習也都模仿這個寫法處理巢狀陣列

之後再來補上這個語法設計的核心邏輯觀念

參考文件連結: 繼承與原型鏈 - JavaScript | MDN

模組化函式設計

今天開始閱讀《Clean Craftsmanship 無暇的程式碼 軟體工匠篇》,目前到第一章開頭,從 TDD 核心邏輯思考程式碼怎麼寫

以 TDD 原則設計 function 任務分工

以練習 ASCII 簡單圖片旋轉為例,把整個任務拆分成:

用一棵應景#的聖誕樹練習

     #     
    ###    
   #####   
  #######  
 ######### 
###########
    ###    
    ###    
   #####   
   #####   
  • 輸入轉換:字串陣列 → 數值矩陣(toMatrix)

  • 純運算核心:矩陣轉置、reverse 或合併兩種方法,看題目要求的旋轉方向和角度(toRotate)

    • 向右轉 90 度:transpose+reverse row

    • 向左轉 90 度:transpose+reverse column 或 reverse row+transpose

    • 轉 180 度:reverse row 反轉 row 順序 + reverse column 反轉每一列的內容

// 一種作法:先反轉整個矩陣的 row,再反轉每一 row 內的元素
const rotate180 = (matrix) =>
  matrix
    .slice()       // 避免就地修改原矩陣時可以先複製
    .reverse()     // row 順序顛倒
    .map((row) => row.reverse()); // 每列內容顛倒
  • 輸出轉換:數值矩陣 → 字串陣列(toArt)

  • 整體流程/對外 API rotateMap

字串陣列 → 數值矩陣按照自己定義的 mapping 規則轉換並且雙向一致即可,例:空格是 0;# 是1

測試時各步驟函數分別單元測試(toMatrix, toRotate, toArt)

整體流程函數整合測試(rotateMap包含輸入驗證和呼叫三個步驟的單元函數)

不用追求要精簡步驟在一個函數一次完成任務

初步理解 API 的概念

以 PAPAYA電腦教室 YT頻道的比喻「API可以想成是提供服務的櫃檯」來想

只要我打包好的 function 模組可以被正確的輸入參數呼叫,並透過內部實作方法輸出預期結果,在這個 Node.js 執行環境裡面就是一個程式碼模組內部的 API

未來前端實作 call 後端 API,會是透過 https 等網路通訊協定,輸入參數或取得授權 token後,取得 JSON 等格式資料

Vitest Snapshot 快照測試

用 it, expect API,先用 toMatchSnapshot 讓 Vitest 建立第一次測試結果快照後,每次執行測試時比對跟前一次快照結果是否相同

想要視覺化呈現的話可以用 toMatchInlineSnapshot ,Vitest 會直接把快照結果以字串形式直接呈現在測試程式檔文件中(會改動測試程式文件內容),這樣可以即時檢視測試結果不用反覆切換檔案或終端機視窗

參考文件連結: Snapshot | Guide | Vitest