4 軟體開發
這一篇章的重點不在語言,而在學習如同 racket 熟手一般的真正開發的技巧
4.1 撰寫好程式
這個章節討論 racket 常見的慣例、風格、傳統與陷阱,學習 racket 程式的最佳實踐。
4.1.1 格式
4.1.1.1 避免過長的單行
(define (filter pred? lst) (cond [(null? lst) '()] [(pred? (car lst)) (cons (car lst) (filter pred? (cdr lst)))] [else (filter pred? (cdr lst))]))
適當的折行有助於閱讀
(define (filter pred? lst) (cond [(null? lst) '()] [(pred? (car lst)) (cons (car lst) (filter pred? (cdr lst)))] [else (filter pred? (cdr lst))]))
4.1.1.2 不要拆散結尾括號
以下的程式是非常好的 racket 風格
(define (factorial n) (if (zero? n) 1 (* n (factorial (- n 1)))))
不要寫成
(define (factorial n) (if (zero? n) 1 (* n (factorial (- n 1))) ) )
4.1.1.3 子表達式的縮排要一致
以下是好的案例
(list (foo) (bar) (baz))
以下是例外,當子表達式是 body 時,我們給它兩個空格做縮排
(let ((pi 3.14) (r 120)) (* pi r r))
4.1.1.4 折一個就全部折
假設你有
(+ 1 foo bar baz)
折行時就應該寫
(+ 1 foo bar baz)
4.1.2 共通的最佳實踐
好程式不僅僅是縮排正確而已,還有一些守則需要遵循。
4.1.2.1 慎重的命名
好的命名可能比你想象的還要更重要也更困難。命名應該要含有變數或是函數所做的事情,並且要考慮其被使用的上下文中是否適合。除了命名要含有資訊之外,racket 還有一些慣例可以幫助你的程式變得更易讀,例如回傳 boolean 的函數可以用 ? 結尾,有副作用的函數用 ! 結尾。
4.1.2.2 為程式加上註解
沒有註解勝過錯誤的註解、有註解勝過沒有註解、簡短的註解勝過冗長的註解。
4.1.2.3 保持函數簡短
一個函數應該盡可能保持在一個螢幕可以看完整個定義的大小,大約在 60 行左右,拆分更多函數有助於保持這點。
4.1.2.4 避免重複的程式
把重複的程式抽成函數是個好習慣。
4.1.2.5 避免重複的計算
善用快取避免重複的計算。
4.1.2.6 使用高階函數
用高階函數如 filter、map 可以減少很多重複的基本工作,並且具備相當的描述能力。
4.1.3 測試
在 Racket 裡編寫測試只需要在檔案中寫下:
(module+ test (require rackunit) (check-equal? actual expected))
就可以了,用 raco test path/to.rkt 就可以看到測試是否有通過
4.1.4 模組
Racket 中每個檔案都是一個模組,其中還可以用 module 定義子模組(就像上一節提到的 (module+ test))。每個 collection 自帶一個 info.rkt、一個 main.rkt,collection 本身被飲用時視為一個模組,而其中的檔案就被視為 collection 的子模組。
Racket 的風格是保持精簡,無論是模組、類別、函數或是方法。一萬行的模組就太多了、一千行免強可以接受、五百行內是適合的大小。
模組有適當的組織方式,應當按照:匯出(provide)、引入(require)、定義,這樣的順序編寫。
如果 collection 是一個可執行程式,那麼可以在 main.rkt 中寫:
(module+ main )
這樣一來執行:racket -l collection-name 時,其中的內容就會被執行。
4.2 閱讀好程式
帶你看各種優秀的 racket 專案,學習自己發掘程式的設計。
4.3 發佈好程式
討論如何發佈寫好的程式庫。