Haskell筆記1

前言

這個(gè)學(xué)期開始學(xué)習(xí)Haskelll(主要關(guān)于Codeworld和ghci),感覺很多東西和OOP不一樣,最近感覺也應(yīng)該開始記錄點(diǎn)東西,以備日后可以查看,提高學(xué)習(xí)的效率。


1.animationOf:: (Double?->?Picture)

這個(gè)函數(shù)接收一個(gè)?Double?->?Picture?型的函數(shù)作為參數(shù),并把此函數(shù)中的double變量視作時(shí)間變量,時(shí)間變量名字通常情況下命名為t,但也可以用其它任意名稱。

2.函數(shù)作用的先后順序

以下代碼

import CodeWorld

main::IO()

main = drawingOf scene

scene? :: Picture

scene = (colored red (solidRectangle 2 2))& (colored blue (solidRectangle 3 3))

可以看到藍(lán)色的矩形被紅色矩形蓋住了一部分,如果把他們的順序?qū)φ{(diào)即最后一句改為

scene = (colored blue (solidRectangle 2 2))&(colored red (solidRectangle 3 3))

就變成了紅色被藍(lán)色矩形蓋住了一部分。

類似的還有

import CodeWorld

main::IO()

main = animationOf scene

scene :: Double->Picture

scene t = rotated t?(translated 2 0 ((circle 1)&polyline [(0,-1),(0,1)]))&coordinatePlane

此處可以看到球以原點(diǎn)為圓心,2為半徑旋轉(zhuǎn)。

如果將rotated和transalated對(duì)調(diào)即

scene t = translated 2 0 (rotated t ((circle 1)&polyline [(0,-1),(0,1)]))&coordinatePlane

可以看到圓在(2,0)處自轉(zhuǎn)。

為了更好的說(shuō)明,rotated函數(shù)在codeworld中的解釋如下

rotated?:: HasCallStack => Double ->?Picture?->?Picture#

A picture rotated by this angle.

Angles are in radians.

它接收一個(gè)double型變量,并以這個(gè)變量作為弧度,以原點(diǎn)為圓心做旋轉(zhuǎn)。正如第一個(gè)例子,我們對(duì)處于(2,0)的一個(gè)圓進(jìn)行旋轉(zhuǎn),它就會(huì)圍繞著原點(diǎn)轉(zhuǎn)。但如果我們先構(gòu)造了一個(gè)旋轉(zhuǎn)的圓,之后對(duì)他進(jìn)行平移,那么它將不再表現(xiàn)得與之前一樣。這是因?yàn)閞otated函數(shù)以作用完畢,它返回的Picture變量作為了translated的參數(shù)。

通過(guò)上面兩個(gè)例子可以知道,函數(shù)的作用是在返回的那一刻就結(jié)束了,所以通過(guò)不同的順序調(diào)用函數(shù),會(huì)產(chǎn)生不同的效果。

3.應(yīng)用List繪制多個(gè)圖形

以下代碼展示了繪制多個(gè)圖形的一個(gè)方法

import CodeWorld

primes :: [Integer]

primes = sieve [2..]

? where sieve cs =

? ? ? ? ? let p = head cs

? ? ? ? ? in [ p ] ++ sieve [ c | c <- tail cs, c `mod` p /= 0 ]

g::[a]->[b]->[(a,b)]

g (x:xs) (y:ys) = (x,y):(zip xs ys)

g _ [] = []

g [] _ = []


f::[(Integer,Color)]->[Picture]

f ((a,b):as) = (translated (fromIntegral (fst(a,b))) 0.0 (colored (snd(a,b)) (solidRectangle 5 5))):(f as)

f [] = []

scene :: Int -> Picture

scene n = pictures(f(reverse (g (take n primes) (take n assortedColors))))

main :: IO ()

main = drawingOf (coordinatePlane & (scene 6))


重點(diǎn)為這一句

f ((a,b):as) = (translated (fromIntegral (fst(a,b))) 0.0 (colored (snd(a,b)) (solidRectangle 5 5))):(f as)

更直觀的來(lái)說(shuō),針對(duì)繪制多個(gè)圖形我們可以用如下幾種方式

import CodeWorld

scene :: Int -> Picture

scene n = pictures[translated (fromIntegral x) 0.0 (rectangle 5 5)|x<-[0,3..3*n]]

main :: IO ()

main = drawingOf (coordinatePlane & (scene 6))

繪制了平移的圖形,其中在List里,每一個(gè)x都會(huì)創(chuàng)造一個(gè)新的矩形。

為了讓矩形區(qū)別的更加明顯,我們給矩形涂上顏色。

import CodeWorld

scene :: Picture

scene = pictures[translated (fromIntegral x) 0.0 (colored y (solidRectangle 5 5))

? ? ? ? ? ? ? ? |x<-[0,3..12],y<-[red,green,purple,orange]]

main :: IO ()

main = drawingOf (coordinatePlane & scene)

有意思的是,如果用如上代碼繪制圖形,你可能期待它應(yīng)該為4個(gè)矩形涂上4中不同的顏色,但卻只看到了紅色的矩形。

這是因?yàn)?,每個(gè)x和y都會(huì)生成一個(gè)新的矩形,上面的代碼實(shí)際上是在每個(gè)位置生成了4種不同顏色的矩形,由于遮擋關(guān)系,你只會(huì)看到第一個(gè)紅色的矩形!

那么如何讓每一個(gè)x和y產(chǎn)生對(duì)應(yīng)關(guān)系呢?我們可以選擇tuple來(lái)處理

上面代碼修改為

scene = pictures[translated (fromIntegral x) 0.0 (colored y (solidRectangle 5 5))

? ? ? ? ? ? ? ? |(x,y)<-zip [0,3..12] [red,green,purple,orange]]

通過(guò)規(guī)定了x,y的對(duì)應(yīng)關(guān)系,我們?yōu)槊恳粋€(gè)x涂上了顏色。

4.Recursion

由于Haskell是函數(shù)式編程,我們?cè)诤瘮?shù)定義中使用如i=i+1,n=n+f(x)之類的語(yǔ)句進(jìn)行迭代。為了處理這種情況,我們需要使用另一種形式的recursion。

例如階乘我們可以寫成

f::Integer->Integer

f 0 = 1

f n = n * f n-1

由于我們定義了f 0的值,所以當(dāng)?shù)M(jìn)行到0是將賦入1完成n*(n-1)*..*1的計(jì)算。

我們考慮另外一個(gè)問(wèn)題:

{- A block digit sum combines several digits before summing up,

- beginning with the last position of the original number.

- For example, the 3-block digit sum of 1234567 is the number

- 1 + 234 + 567 = 802.

- An alternating digit sum switches between addition and

- subtraction, here in a fashion such that altogether no

- negative number is obtained. For example, the alternating

- 3-block digit sum of 1234567 is the number 1 - 234 + 567 = 334,

- while the alternating 2-block digit sum of 54321 is obtained

- as -5 + 43 - 21 = 17.

-

- Write a function that computes such generalized digit sums.

- The function is controlled by arguments for the block length

- and (as a Boolean value) the information whether or not an

- alternating digit sum is to be computed.

- Thus, for example:

-

-? genDigsum 3 False 1234567 = 802

-? genDigsum 3 True? 1234567 = 334

-? genDigsum 2 True? 54321? = 17

-}

對(duì)于分離成幾個(gè)數(shù)字塊,我們可以使用`quotRem`方法,它返回(q,r),q為商,r為余數(shù)。例如,1234`quotRem`我們將會(huì)得到(12,34),此時(shí)我們將它分離了一次,再對(duì)12進(jìn)行一次處理即可得到(0,12)。至此,我們將1234分離為,12與34.

對(duì)應(yīng)的數(shù)字塊之間的加法我們應(yīng)該如何實(shí)現(xiàn)?由于調(diào)用的函數(shù)在返回值之后就不再進(jìn)行運(yùn)算,我們需要對(duì)函數(shù)進(jìn)行recursion處理。一個(gè)基本的OOP思路可以是在loop中進(jìn)行運(yùn)算,用一個(gè)參數(shù)存儲(chǔ)所需要的中間值。但是由于Haskell不支持類似的操作。我們可以用一下迭代來(lái)實(shí)現(xiàn):

normalDigsum::Integer->Integer->Integer

normalDigsum 0 p = 0

normalDigsum n p = r + (normalDigsum q p)

? ? ? ? ? ? ? ? where (q,r) = n`quotRem`(10^p)

仔細(xì)來(lái)看,normalDigsum將求和變?yōu)榱藃0+(r1+(r2+..)),這是可行的。但是對(duì)于alternat類型來(lái)說(shuō)它卻變成了r0+(r1-(r2+..))負(fù)號(hào)是錯(cuò)誤的,那么我們?cè)撊缫?guī)避?

我們可以為alternat型設(shè)立一個(gè)額外的參數(shù),它專門用于存儲(chǔ)結(jié)果,可以避免此類情況的發(fā)生,我喜歡叫它為“記憶參數(shù)”。實(shí)現(xiàn)代碼如下:

alternatDigsum::Integer->Integer->Bool->Integer->Integer

alternatDigsum 0 p b c = c

alternatDigsum n p b c = if b==True

? ? ? ? ? ? ? ? ? ? then alternatDigsum q p (not b) (c+r)

? ? ? ? ? ? ? ? ? ? else alternatDigsum q p (not b) (c-r)

? ? ? ? ? ? ? ? ? ? where (q,r) = n`quotRem`(10^p)

當(dāng)數(shù)字塊源為0時(shí)代表結(jié)束,我們可以返回c。在運(yùn)算過(guò)程中,針對(duì)每一次不同的運(yùn)算,在記憶參數(shù)中進(jìn)行加法或者減法,由于每次調(diào)用函數(shù)時(shí)都會(huì)直接傳遞給它上一次的結(jié)果,因而保證了負(fù)號(hào)的準(zhǔn)確性。

5.Indexitis與wholemeal programming

Indexitis是一個(gè)混合的自創(chuàng)詞匯,Index+itis可以譯作索引炎。它的意思是指需要對(duì)索引進(jìn)行繁瑣的操作,尤其會(huì)在修改時(shí)因?yàn)樗饕疱e(cuò)誤。

wholemeal programming就是它的反意,在編程時(shí)對(duì)整個(gè)所需要處理的數(shù)據(jù)進(jìn)行限定,在haskell中即利用List來(lái)行使loop職能。由于List修改時(shí)的簡(jiǎn)易,它會(huì)減少因?yàn)閕ndex錯(cuò)誤而引發(fā)的種種問(wèn)題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,246評(píng)論 0 38
  • 第2章 基本語(yǔ)法 2.1 概述 基本句法和變量 語(yǔ)句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,556評(píng)論 0 13
  • CHAPTER 1: INTRODUCTION 第一章:簡(jiǎn)介 In this chapter, we discus...
    哈小奇閱讀 1,098評(píng)論 2 1
  • 一、進(jìn)程和線程 1.什么是進(jìn)程 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序 每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專...
    ZYZZZ閱讀 311評(píng)論 1 1
  • 311 如果你足夠優(yōu)秀,一定要去找那些和你一樣優(yōu)秀或比你優(yōu)秀的人才,如果你不夠優(yōu)秀,是很難吸引到優(yōu)秀人才的。再說(shuō)了...
    e1f1b6c637ae閱讀 280評(píng)論 0 0

友情鏈接更多精彩內(nèi)容