1. 程式人生 > >Coursera課程 Programming Languages, Part B 總結

Coursera課程 Programming Languages, Part B 總結

pos 計算 但是 for 我們 org 情況 再計算 fec

  • Programming Languages, Part A
  • Programming Languages, Part B
  • Part A 筆記

碎言碎語

  1. 很多沒有寫過 Lisp 程序的人都會對 Lisp 中的括號產生偏見(包括曾經第一次看到 Lisp 程序的我),事實上,括號賦予 Lisp 的是嚴謹的程序結構,Lisp 中的括號是極其嚴格的,往往既缺一不可,也不能畫蛇添足。括號往往意味著表達式的計算,例如 (e)表示 e 表達式的計算,那麽一旦 e 是一個確定的值,則會出現問題,(3),在 Racket 中會報錯:application: not a procedure;
  2. 這門課最值得一提的就是 week 2 的作業,在 Racket 語言中實現自己的語言 MUPL(Made Up Programming Language),Racket 作為解釋器去執行用自己的語言寫成的代碼。當然話是這麽說,其實介紹作業的材料已經解決了從 0 到 1 的困難了,從定義語言的基本語法,到擴展這門語言,再到用這門語言編寫函數,都有著詳細的指導。
  3. Racket 作為解釋器,如果要執行我們的語言,簡單情況下,只需要編寫一個 eval 函數,裏面充滿著各種判斷語句來解析我們的語言(語法)。另外,我們可以在自己的語言中定義宏的寫法,其實這裏宏可以就是一個 Racket 函數,這個函數會返回一個 MUPL 表達式,在 eval 函數執行的時候可以很自然的替換,這不是很類似 Racket 語言中的宏嗎?檢測宏是否運行正常只要看它生成的代碼是否正確就行了。
  4. 從類型檢查談起,並討論了弱類型語言以及強類型語言的區別(如 C 的數組越界),並在某些方面比較了動態類型和靜態類型的優缺點。在 Racket 中我們僅僅使用 list 就定義出很多數據結構,通過 car cdr 可以很容易的使用(如最基本的樹,也可以實現 set 和 map),但是定義一個新的類型,既可以更快地幫助類型測試捕捉到這個錯誤,也提高了代碼的可讀性。
  5. 如果對 Part B 的內容還意猶未盡的話,還有 SICP 可以食用,前三章會看的很輕松(或許)。

筆記

1. 惰性求值(Lazy Evaluation)

  1. call-by-value:在調用函數時,表達式先被求值再作為參數。
  2. call-by-need:在需要的時候再計算。

在大多數編程語言中都是第一種方式,在一些語言中我們可以用一種既巧妙又簡單的方式實現第二種方式,即惰性求值(Lazy Evaluation)。

下面舉得例子並不是非常恰當,但是很直觀。

Racket 中的 if 表達式是這樣的:

(if e1 e2 e3)

也就是如果 e1 為真,執行 e2 否則執行 e3。很顯然它接受三個參數,那麽有一個問題,如果應用的是第一種方式,e1 e2 e3 先被計算再判斷 e1 真假的話,一旦 e2 表達式產生了 side effect (例如打印函數),則會導致程序的不理想輸出,甚至於有時會發生無限遞歸。

為了解決這個問題,e2 和 e3 在判斷 e1 之前不能被計算。
在 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 總結