沖動
Saturday, December 13th, 2008近來腸胃不適,腹瀉不止,苦不堪言。吃了幾天醫生開的藥,未見多大療效。忽生學中醫的想法。既抱恙在身,不能復如平日讀書,唯可略翻傳記、學述。翻《學海圖南錄:文學史家錢仲聯》,觀錢萼孫如此生猛,大驚,又生考公務員之念。
近來腸胃不適,腹瀉不止,苦不堪言。吃了幾天醫生開的藥,未見多大療效。忽生學中醫的想法。既抱恙在身,不能復如平日讀書,唯可略翻傳記、學述。翻《學海圖南錄:文學史家錢仲聯》,觀錢萼孫如此生猛,大驚,又生考公務員之念。
加了根512的內存,總內存1G。Mac OS X下風火輪的確少出現了。
最近大便的時候無意中發現潔雲草紙的slogan換了,改成“聰明的選擇”,原來是“聰明女人選潔雲”。
list的限制是成員的類型必須相同。如果我們有一些不同類型的東西想把它們整合在一起,list就無能為力了。這時,就需要tuple出場了。
tuple可以容納不同類型的成員。和list一樣,成員間用“,”隔開,但首尾用“(”和“)”標識。例如:
(True, 1)
("Hello world", False)
(4, 5, "Six", True, 'b')
上例中,第一個tuple有2個成員,我們稱為2-tuple(也叫pair(對))。第二個有3個成員,稱為3-tuple(也叫triple)。第三個有5個成員,稱之為5-tuple。一般而言,有n個成員的tuple就叫做n-tuple。
和list不同的是,tuple的成員的個數是固定的。如果一個list是由一些Integer組成的,我們再cons一個Integer上去,這個list的類型仍然保持不變,但是pair和triple的類型則完全不同。
數學的集合論中,list和tuple的含義是差不多的。對於一個集合A,從A中抽取一些元素(允許重復取),組成一個有序的list。如果該list含有k個條目,就叫做k-tuple。但在Haskell中,list和tuple則有不同的用處。list在成員的類型上有限制,而tuple則在成員的數目上有限制。
練習
9.1 有一個3-tuple包含三個成員,第1個是4,第2個是"hello",第3個是True。寫出此tuple。
9.2 下面哪些tuple可以成立?
9.3 我們可以通過cons成員的方式構建list。但沒有與之相應的構建tuple的方法,為什麽?假設有這樣一個函數,如果你cons某個成員到一個tuple上,你會得到什麽?
答案
8.1 1和2不能成立,3可以。
8.2 都能成立。
8.3 可以。比如: [[[1],[2],[3]],[[4],[5],[6]],[[7],[8],[9]]]。
8.4
list可以包含任何東西,只要這些東西的類型相同。好的,回味一下這句話:任何東西!所以list也可以包含list!不信可以嘗試一下:
Prelude> let listOfLists = [[1,2],[3,4],[5,6]] Prelude> listOfLists [[1,2],[3,4],[5,6]]
Lists of lists can be pretty tricky sometimes, because a list of things does not have the same type as a thing all by itself. Let's sort through these implications with a few exercises:
包含list的list有時比較奧妙。list和list的成員的類型有關,但不相等!具體請通過做下面的習題體會。
練習
8.1 下面哪些list可以成立?哪些不能?為什麽?用cons到空表的形式改寫以下list。
8.2 下面哪些list可以成立?哪些不能?為什麽?用"[]"和","的形式改寫。
8.3 Haskell允許由包含list的list組成的list嗎?為什麽?
8.4 為何以下list是不合法的?
答案
7.1
7.2 不成立。3是Integer,True, False是Bool。
7.3
Prelude> let cons8 list = 8:list
7.4
Prelude> let myCons list thing = thing : list
很多情況下,我們通過在舊的(已經構造好的)list上增加新成員來構造一個新的list。在Haskell中,我們稱這為把成員cons到list上。我們使用“:”這個控制符來表示這個操作。
Prelude> let numbers = [1,2,3,4] Prelude> numbers [1,2,3,4] Prelude> 0:numbers [0,1,2,3,4]
cons所得的結果仍然是一個list,所以我們可以繼續cons成員上去。例如:
Prelude> 1:0:numbers [1,0,1,2,3,4] Prelude> 2:1:0:numbers [2,1,0,1,2,3,4] Prelude> 5:4:3:2:1:0:numbers [5,4,3,2,1,0,1,2,3,4]
事實上,listnumbers本身(即[1,2,3,4])就可以寫成1:2:3:4:[]的形式。也就是說,所有list都是通過把成員cons到一個空list(用[]表示)上得到的。所以,[成員1,成員2]的表示list的形式并不是不可缺少的,僅僅起到表示起來更方便、看起來更清楚之類的作用,我們把這類表達叫做“語法糖衣”(Syntax Sugar)。
練習
7.1 將6.1中的list改寫成cons到空表的形式。
7.2 3:[True,False]這樣的表達成立嗎?為什麽?
7.3 寫一個函數cons8,將8 cons到輸入的list上。用以下例子測試你的函數:
cons8 [] cons8 [1,2,3] cons8 [True,False] let foo = cons8 [1,2,3] cons8 foo
7.4 寫一個函數,以一個list和一個成員作為輸入,將輸入的成員cons到輸入的list上。
答案
函數是構建Haskell程序的兩大基本部件之一,另一個則是list(列表)。
list的表示方法很簡單,首先列舉list的各成員,然後以“,”分隔各成員,最後用“[”和“]”標記list的開頭和結尾。比如,[1,2,3,4]。
為了便於以後的稱說,我們通常給list“起個名字”(指定一個符號代表它),例如:
Prelude> let numbers = [1,2,3,4] Prelude> let truths = [True, False, False] Prelude> let strings = ["here", "are", "some", "strings"]
特別要當心的是,list中的成員必須擁有相同的類型。在上面的三個例子中,numberslist的成員都是Integer(整數),truthslist的成員都是Bool(布爾值),stringslist的成員都是String(字符串)(字符串用""括起來)。不同類型的成員無法在一個list中共存。比如,如果輸入:
Prelude> let mixed = [1,True]
GHCi就會報錯:
<interactive>:1:13:
No instance for (Num Bool)
arising from the literal `1' at <interactive>:1:13
Possible fix: add an instance declaration for (Num Bool)
In the expression: 1
In the expression: [1, True]
In the definition of `mixed': mixed = [1, True]
這是因為1的類型是Integer,而True的類型是Bool,兩者類型不同。
練習
6.1 以下哪些list可以成立,哪些不可以,為什麽?
答案
5.1 25
Prelude> areaSquare 5 25
5.2
Prelude> let areaCircle r = pi * r ^ 2 Prelude> let volCylinder r h = areaCircle r * h
5.3
Prelude> let areaRect l w = l * w Prelude> let areaSquare s = areaRect s s Prelude> let areaCircle r = pi * r ^ 2 Prelude> let diffOfCnS r = areaCircle r - areaSquare r
定義函數時,可以使用其它已經定義好的函數。比如,求正方形的面積。正方形是矩形的一個特例,所以定義求正方形面積的函數時,可以利用求矩形面積的函數。
Prelude> let areaRect l w = l * w Prelude> let areaSquare s = areaRect s s
練習
5.1 利用areaSquare求邊長為5的正方形的面積。
5.2 寫一個求圓柱體體積的函數(利用求圓面積的函數)
5.3 寫一個函數解決以下問題:給定圓的半徑,且已知有一正方形的邊長和該半徑相等,求該圓與該正方形面積之差。
答案
4.1 areaTriangle b h = (b * h) / 2
4.2 areaCube l w h = l * w * h
f r = pi * r ^ 2
這裏f代表function(函數),表達的信息量很有限。如果我們的程序裏有很多這樣的函數,就很不方便。你得讀完整行纔知道這是一個求圓的函數。為了增加可讀性(或者說方便偷懶,不用讀完函數的整個定義就知道函數是幹啥的),寫成類似以下的形式好一些:
areaCycle r = pi * r ^ 2
好了,求了圓的面積,求了圓的周長(練習3.1),下面我們來求矩形的面積areaRect。和圓不一樣,長方形的面積取決於長和寬。那該怎麽寫呢?很簡單,以空格隔開,依次列出就可以了。
Prelude> let areaRect l w = l * w
那麽,長為5,寬為10的矩形的面積為:
Prelude> areaRect 5 10 50
練習
4.1 寫一個求三角形面積的函數。
4.2 寫一個求長方體體積的函數。
答案:
3.1
g r = 2 * pi * r
例子很多,只举一个,xiong,看上去是齐齿呼,其实是撮口呼。