Coursera課程 Programming Languages, Part B 總結
阿新 • • 發佈:2017-12-30
pos 計算 但是 for 我們 org 情況 再計算 fec
為了解決這個問題,e2 和 e3 在判斷 e1 之前不能被計算。
在 Racket 語言中,我們直接在 e2 和 e3 外面分別套一個 lambda表達式 即可達到這個目的。
- Programming Languages, Part A
- Programming Languages, Part B
- Part A 筆記
碎言碎語
- 很多沒有寫過 Lisp 程序的人都會對 Lisp 中的括號產生偏見(包括曾經第一次看到 Lisp 程序的我),事實上,括號賦予 Lisp 的是嚴謹的程序結構,Lisp 中的括號是極其嚴格的,往往既缺一不可,也不能畫蛇添足。括號往往意味著表達式的計算,例如
(e)
表示 e 表達式的計算,那麽一旦 e 是一個確定的值,則會出現問題,(3)
,在 Racket 中會報錯:application: not a procedure;
。 - 這門課最值得一提的就是 week 2 的作業,在 Racket 語言中實現自己的語言 MUPL(Made Up Programming Language),Racket 作為解釋器去執行用自己的語言寫成的代碼。當然話是這麽說,其實介紹作業的材料已經解決了從 0 到 1 的困難了,從定義語言的基本語法,到擴展這門語言,再到用這門語言編寫函數,都有著詳細的指導。
- Racket 作為解釋器,如果要執行我們的語言,簡單情況下,只需要編寫一個 eval 函數,裏面充滿著各種判斷語句來解析我們的語言(語法)。另外,我們可以在自己的語言中定義宏的寫法,其實這裏宏可以就是一個 Racket 函數,這個函數會返回一個 MUPL 表達式,在 eval 函數執行的時候可以很自然的替換,這不是很類似 Racket 語言中的宏嗎?檢測宏是否運行正常只要看它生成的代碼是否正確就行了。
- 從類型檢查談起,並討論了弱類型語言以及強類型語言的區別(如 C 的數組越界),並在某些方面比較了動態類型和靜態類型的優缺點。在 Racket 中我們僅僅使用 list 就定義出很多數據結構,通過 car cdr 可以很容易的使用(如最基本的樹,也可以實現 set 和 map),但是定義一個新的類型,既可以更快地幫助類型測試捕捉到這個錯誤,也提高了代碼的可讀性。
- 如果對 Part B 的內容還意猶未盡的話,還有 SICP 可以食用,前三章會看的很輕松(或許)。
筆記
1. 惰性求值(Lazy Evaluation)
- call-by-value:在調用函數時,表達式先被求值再作為參數。
- call-by-need:在需要的時候再計算。
在大多數編程語言中都是第一種方式,在一些語言中我們可以用一種既巧妙又簡單的方式實現第二種方式,即惰性求值(Lazy Evaluation)。
下面舉得例子並不是非常恰當,但是很直觀。
Racket 中的 if 表達式是這樣的:
(if e1 e2 e3)
也就是如果 e1 為真,執行 e2 否則執行 e3。很顯然它接受三個參數,那麽有一個問題,如果應用的是第一種方式,e1 e2 e3 先被計算再判斷 e1 真假的話,一旦 e2 表達式產生了 side effect (例如打印函數),則會導致程序的不理想輸出,甚至於有時會發生無限遞歸。
在 Racket 語言中,我們直接在 e2 和 e3 外面分別套一個 lambda表達式 即可達到這個目的。
(define (bad-if e1 e2 e3)
(if e1 e2 e3))
(bad-if (= 1 2) (write 1) (write 3)) ; 13
(define (new-if e1 e2 e3)
(if e1 (e2) (e3)))
(new-if (= 1 2) (lambda () (write 1)) (lambda () (write 3))) ; 3
所以,如果我們想要延遲求值,把要延遲求值的表達式放到一個 lambda表達式 中即可,在需要用到時對函數求值即可。
這樣問題是解決了,但是顯得異常麻煩,有幾個參數就要寫幾個 lambda表達式,有沒有什麽簡潔的方法呢?
這個時候就要用到 宏(macro) 了。
宏的替換發生在任何表達式的計算之前。
(define-syntax good-if
(syntax-rules ()
[(new-if e1 e2 e3)
(new-if e1 (lambda () e2) (lambda () e3))]))
(define (new-if e1 e2 e3)
(if e1 (e2) (e3)))
(good-if (= 1 2) (write 1) (write 3)) ; 3
雖然這個例子毫無意義,但是思想還是能體現不少的。
惰性求值一個常見的應用就是 流(stream) 了。
具體可參考 SICP 。
2. macro
模擬 for 循環。
(define-syntax for
(syntax-rules (to do)
[(for lo to hi do body)
(let ([l lo]
[h hi])
(letrec ([loop (lambda (it)
(cond [(= it h) body]
[(< it h) (begin body (loop (+ it 1)))]))])
(loop l)))]))
(for 1 to 5 do (write "H"))
Coursera課程 Programming Languages, Part B 總結