C311 第一次作業(yè) 收獲/扯淡 by hugzh

單元測(cè)試

寫(xiě)測(cè)試的好處是不用盯著屏幕看了,需要跑幾十個(gè)用例的時(shí)候,用眼看是不是符合預(yù)期是非常二的行為。(商用的東西有幾萬(wàn)個(gè)測(cè)試都很正常。。。)
即便是自己開(kāi)發(fā),也應(yīng)該寫(xiě)一些非常簡(jiǎn)單的、預(yù)期函數(shù)結(jié)果的那種測(cè)試。
當(dāng)然只有測(cè)試是不夠的,關(guān)鍵是要對(duì)自己寫(xiě)下的東西有自信,相信它覆蓋了足夠多的情況。甚至可以稱(chēng)之為“對(duì)的”(在所有情況下都表現(xiàn)出正確的行為)
正如課程要求所說(shuō)

The objective is not simply to write programs that get the correct answers; it is to write answers in the style of programs written in class.

提前寫(xiě)出測(cè)試,然后寫(xiě)實(shí)現(xiàn)的做法叫做測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD),我覺(jué)著 TDD 用在小的函數(shù)層次還是很管用的。畢竟一個(gè)函數(shù)越小,它的行為就越容易預(yù)測(cè),吧。

不只是方便驗(yàn)證結(jié)果。直接拿測(cè)試用例那一行,把對(duì)應(yīng)的實(shí)參改成形參的時(shí)候,往往能想出更好的參數(shù)名字。

Racket 自帶的測(cè)試框架叫 rackunit
http://docs.racket-lang.org/rackunit/api.html

這個(gè)作業(yè)自帶的測(cè)試大概長(zhǎng)這樣,具體實(shí)現(xiàn)請(qǐng)自行研究。

    (test-suite "countdown"
      (test-equal-if-defined countdown
        ((countdown 5) '(5 4 3 2 1 0))))

還有就是測(cè)試結(jié)果有 failure 和 error 的區(qū)分。比如一個(gè)函數(shù)沒(méi)定義,認(rèn)為是failure,確實(shí)是函數(shù)邏輯錯(cuò)了,認(rèn)為是error。

Natural Recursion

Natural Recursion ,或者說(shuō)naturally recursive,直接理解就是自然而然的遞歸。
這個(gè)詞我搜了很久都沒(méi)找到確切的意思

參考了這個(gè)http://web.mit.edu/6.005/www/fa15/classes/10-recursion/

Think about several ways to break down the problem, and try to write the recursive steps. You want to find the one that produces the simplest, most natural recursive step.
而且所有的問(wèn)題都不允許用循環(huán),甚至不讓用 accumulator 存儲(chǔ)中間結(jié)果。

姑且認(rèn)為是用分治法來(lái)解決問(wèn)題,通過(guò)把原問(wèn)題轉(zhuǎn)化為更小的子問(wèn)題,以及trivial情況下返回顯然的解。來(lái)解題。
或者反過(guò)來(lái)說(shuō),在給定更小子問(wèn)題的解的前提下,如何得到更大問(wèn)題的解。
也就是所謂遞推公式、數(shù)學(xué)歸納。
舉個(gè)例子

(define (countdown n)
  (if (= n -1)
      '()
      (cons n (countdown (- n 1)))))

(countdown N) 返回從 N到0 的列表。
我這里的實(shí)現(xiàn)是說(shuō),n==-1時(shí),trivial case,直接返回空表
否則,返回 把 N cons 到 (countdown (- n 1)) 的結(jié)果

很多數(shù)據(jù)類(lèi)型都可以看成遞歸數(shù)據(jù)類(lèi)型,
lisp 的 list,其實(shí)就是個(gè)單鏈表,鏈表可以是空表,也可以是Head元素指向另一個(gè)鏈表。這就是所謂遞歸定義。

字符串,字符串有很多種切碎的方法,
可以看作是首元素+字符串
或者 字符串+末尾元素
或者中間切碎 字符串 + 。。。 + 字符串

自然數(shù) 是
0
或者 某個(gè)自然數(shù)的后繼 (所謂后繼就是數(shù)數(shù),0的后繼是1,1的后繼是2(0的后繼的后繼),以此類(lèi)推)

關(guān)于分治法可以去補(bǔ)補(bǔ)算法課,MIT 6.006/6.046 都可以的。

我一直覺(jué)著分治和動(dòng)態(tài)規(guī)劃幾乎是一回事。。。
都是轉(zhuǎn)成子問(wèn)題,然后base case直接有解。

BrainTeaser /Just Dessert

這兩個(gè)單元都是難題,會(huì)遇到各種各樣奇怪又好玩的東西。有的太難我根本不會(huì)。感嘆人要有自知之明啊,如果跟這些東西死磕太久,怕是要耗費(fèi)大把時(shí)光還一無(wú)所得。

collatz

(define collatz
  (letrec
      ([odd-case
        (lambda (recur)
          (lambda (x)
            (cond 
              ((and (positive? x) (odd? x)) (collatz (add1 (* x 3)))) 
              (else (recur x)))))]
       [even-case
        (lambda (recur)
          (lambda (x)
            (cond 
              ((and (positive? x) (even? x)) (collatz (/ x 2))) 
              (else (recur x)))))]
       [one-case
        (lambda (recur)
          (lambda (x)
            (cond
              ((zero? (sub1 x)) 1)
              (else (recur x)))))]
       [base
        (lambda (x)
          (error 'error "Invalid value ~s~n" x))])
    (one-case (odd-case (even-case base)));; this should be a single line, without lambda
    ))

這次的BrainTeaser叫 collatz,是個(gè)很有意思的程序,注意到每個(gè)case都是先拿一個(gè)參數(shù)作為出口,然后再返回一個(gè) number->number 。 也就是
(number->number)->(number->number)只有base是 number->number

然后就可以把每個(gè)case組合起來(lái),得到一個(gè)考慮到所有case的number->number

base:number->number
odd-case (number->number)->(number->number)
(even-case base) : (number->number)

后面以此類(lèi)推

quine

quine是這樣一種程序,你對(duì)它求值,返回程序原代碼本身。也就是說(shuō),把得到的東西再求職,還得到原代碼本身。然后作業(yè)是讓你自己構(gòu)造個(gè)quine出來(lái)。。。
并不會(huì)啊。。。
quine參考鏈接
https://en.wikipedia.org/wiki/Quine_%28computing%29
http://www.nyx.net/~gthompso/quine.htm

后記/扯淡

厚顏無(wú)恥的貼個(gè)自己的答案https://github.com/hgztheyoung/c311/blob/master/c311recap/a1.rkt
Racket大法好啊。 #;可以注釋掉接下來(lái)的一對(duì)括號(hào)內(nèi)的內(nèi)容。語(yǔ)義級(jí)別的注釋真好用啊
注釋可以加圖片。多好的功能啊。(當(dāng)然不能在作業(yè)里加,會(huì)和測(cè)試框架沖突。)
Racket的文檔質(zhì)量真的高。遇到程序里不懂的東西,比如match啊,letrec啊, 直接右鍵,查看文檔就好。當(dāng)然直接看 the little schemer/the seasoned schemer也可以。
DrRacket有重構(gòu)表達(dá)式的功能,很好用,右鍵,重命名XXX,即可。不過(guò)要求整個(gè)文件都沒(méi)有錯(cuò)誤,比如重復(fù)定義,語(yǔ)法錯(cuò)誤等等。
縮進(jìn),全選,Tab。即可。
關(guān)于Racket 的( ) 、[ ] 和 { }
一個(gè)意思, [ ] 起到美觀的作用。我的慣用法是

(let ((x 2)
      (y 3))
  (+ x y))
(cond
    ((eq? #t #t) 1)
    (else 2))
(match x
    (1  2)
    (#t 3))

寫(xiě)成

(let ([x 2]
      [y 3])
  (+ x y))
(cond
    [(eq? #t #t) 1]
    [else 2])
(match x
    [1 2]
    [#t 3])

這破文又扯了好多沒(méi)用的啊。。。

自己認(rèn)真做作業(yè)才會(huì)有收獲的。
工作好難找啊。

我在行乞發(fā)廣告擺攤賣(mài)藝尋求捐贈(zèng)。
唯一指定郵箱是 hgz92929@sina.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容