Haskell學習筆記

《learn you a Haskell》這書的結構與常見的語言入門教材完全不一樣。事實上,即使學到第八章,你還是寫不出正常的程序…因為到現在為止還沒告訴你入口點模塊怎么寫,IO部分也留在了最后幾章才介紹。最重要的是,沒有系統的總結數據類型、操作符、語句,這些知識被零散的介紹在1-8章的例子中,換句話來說,這書其實不算是很合格的教材(代碼大全那種結構才更適合),不過它重點強調了FP與其他語言的思想差異,對于已經有其他語言基礎的人來說,讀這本書能夠很快領悟到FP的妙處,對于那些并不用它來做項目(估計也很難有公司用這個做項目,太難了)的人來說,這本書很適合作為拓展閱讀的小冊子。另外一本《Real World Haskell》也相當有名,我還沒有看,這本看完后再著手閱讀吧。

第八章 自定義類型和類型類

關鍵字data用來構建類型,格式為

data classname = definition

左端是類型的名字,右端是該類型的取值,又稱為構造子。例如:

data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)

這里表示類型Shape可以取值為Circle或Rectangle,這兩個類型的構造子分別是3個、4個Float。構造子本質是一種函數,取幾個參數返回一種類型。比如這里的Circle :: Float->Float->Float->Shape。關鍵字deriving表示派生于某類型類,即具有該類型類的性質。這里面有一些類似于面向對象中的封裝與繼承的概念,比如可以認為Shape是基類,Circle和Rectangle是派生類。如果某個函數對Shape通用,那么在模式匹配中就將兩種派生類的實現都寫出來,否則只匹配部分類即可。注意Circle和Rectangle并不是自定義類型,并不能直接使用,只有在需要Shape時,用構造子進行匹配來使用。如果構造子只有一個,可以讓data類型與構造子同名,這樣更加清晰。

如果說第七章的module相當于C++中的namespace,那么本章的data就相當于class,不同的是module封裝的是function,而Haskell中class就是function,所以module、data和function就構成了Haskell中的用戶層次。

導出格式:

module Shape

(Shape(…)

,fun1

,fun2

)

其中Shape(…)表示導出全部構造子。

Record Syntax

我們知道C++中提倡軟件工程中的封裝概念,盡量不讓內置數據類型暴露給使用者,一般要寫一大堆setxxx,const getxxx函數,雖然這些小函數往往只有一句話,但是寫多了也很煩。Record Syntax是Haskell中一種用來簡化此類函數書寫的語法結構。在聲明data時,直接標明其參數名稱和對應的數據類型:

data Shape=Circle { x::Float,y::Float,radix::Float} | Rectangle { x1::Float,y1::Float,x2::Float,y2::Float} deriving (Show)

這里的語法有點像C中的位域,但是它實際的意思其實仍然是前面學過的類型解釋符。Haskell會自動生成已經注明名稱的參數的函數。另外在調用構造子時,也要遵從這種結構,不過x::Float換成x=1.0(即Circle {x=1.0,y=2.2,radix=3.1})。

類型參數

前面學習了值構造子,這里介紹類型構造子。值構造子顯然需要明確值的類型,而類型構造子則寬松的多,比如向量,我們只需要向量的參數類型一致即可,不必明確具體的類型。換句話說,類型參數是一種"泛型"語法支持(類似C++的模板)。

比如 data Vector a = Vector a a a deriving (Show),這就是一個三維向量。類型參數a對后面的值構造子產生約束。也就是C++中的:

template <class a>

class Vector

{

????Vector(a first,a second, a last);

}

顯然我們在寫函數的時候需要對類型參數進行實例化,換句話說,需要對函數進行類型約束。記住,不要在data聲明中添加類型約束,而是在函數中添加,因為具體執行操作的是函數,而數據類型需要比函數更加抽象。這里按著C++中寫模板的思路來就很容易理解什么時候用類型構造子。

派生實例

本節介紹了從類型類從派生類型的方法。前面已經介紹過類型類本質上是一種接口要求,它描述了類型的行為一致性。在我們創建類型時,可以在值構造子后面加上deriving (Ord,Show,Read,…),來給創建的類添加接口。一旦加上了指定的類型類接口,就給予所創建的類對應的行為特性。如果加了Ord,就可以直接比較兩個類(根據值構造子和參數),如果加了Show,那么就可以顯示該類的參數(如果使用了syntax record,就顯示出"名稱=值"的格式。)

派生實例是很有用的特性,在我們設計類的時候需要明確該類支持的行為特性。這看起來有些像C++中的重載操作符,但是不需要我們自己去實現。Haskell會自動推斷應該怎樣實現聲明的行為。

注意Haskell中True/False與C中意義完全不同【C語言中沒有bool,只是單純認為0為false,非0為true】,可以認為 data Bool = False | True deriving (Ord),所以True > False是成立的。同理Nothing總是小于Just a。

類型別名

類似typedef的語法,在Haskell中格式為

type Newtype=OldType,與typedef相似的是一般用來簡化書寫或者明晰概念。與typedef不同的是,type也支持Haskell的不全調用。

這里舉了Either a b作為例子,Either a b是用來代替Maybe a的,當函數的返回類型可能有多種,或者函數需要根據情況返回不同的信息時,經常使用Either a b作為返回類型。

data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)

Either a b有兩個值構造子,如果用了Left構造子,其返回類型就是a,否則是b,換句話說,Haskell可以返回多種類型的結果,而不像C++那樣只能用結構來封裝。

遞歸數據結構

【我本以為第8章到這就完了呢…結果后面又發現是翻譯的大哥翻到一半就終止了,第八章后面還有不少,這部分是后來添補的】

考慮list,如果我們需要自己定義list類型,應該如何聲明?應該這樣:

data List a = Empty | Cons a (List a) deriving (Show,Read,Eq,Ord)

這里Cons相當于運算符 " : ",顯然這里的List定義是遞歸的——等號的左右兩端都存在List。這樣,我們就可以像使用:一樣使用Cons來構造List,如 3 `Cons` 4 `Cons` Empty。

也可以自定義操作符,使用infixr來確定操作符的優先級和左、右結合性,注意這里可以定義任意操作符,這一點和C++中的重載操作符有本質的不同。書中以操作符 :-: 為例介紹了使用方法。

下面介紹了一個二叉搜索樹的生成作為例子:

data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)

一個二叉樹是一個空樹,或者一個由根節點和其左子樹與右子樹構成的樹。下面是一些常用操作函數的實現,這里從略。

TypeClass102

本節介紹如何自定義類型類,類型類與常見的過程式或者面向對象模型的編程語言并無相似之處,不過可以和C++中的重載操作符對比參考。

我們使用關鍵字class來定義類型類,使用關鍵字instance來定義類型類的實例。

class Eq a where

(==) :: a->a->Bool

(/=) :: a->a->Bool

x == y = not x /= y

x /= y = not x==y

instance Eq TrafficLight where

instance下面就是詳細解釋該接口的實現。在不想使用默認的從類型類派生得到的行為時,必須使用instance來自定義數據對于該接口的行為方式。對于Maybe類型的實例,格式是

instance (Eq m) => Eq (Maybe m) where

注意這里需要對m添加類型約束。

可以在GHCI中使用:info 得到類型類、類型和類型構造子的詳細信息。

A yes-no typeclass

本節講了一個類型類的實例,它用來完成各種類型向Bool的缺省轉換(如C的非零為True,0為False)。

class YesNo a where

????yesno :: a -> Bool

實現:

instance YesNo Int where

????yesno 0 = False

????yesno _ = True

instance YesNo [a] where

????yesno [] = False

????yesno _ = True

instance YesNo Bool where

????yesno = id

…其他略

注意id是標準庫函數。

函數子類型類

函數子類型類(Functor class)指的是可以被被映射的函數滿足的接口。

class Functor f where

????fmap :: (a->b) -> f a -> f b

注意定義中的f并非類型,而是類型構造子。換言之,函數子類型類的實例參數必須是類型類構造子而不能是具體的類型,那么可見這個類型類是容器的接口(如Maybe或[]等擁有1個以上值構造子的類型)。如果f有兩個以上的參數,那么只能用函數的不完全調用格式,僅保留一個參數進行處理(如Either a)

種類和一些類型相關的東西

本節對類型(type)的知識做了擴展,這里將類型本身分為具體類型和不完全類型。

這種類型的類型被稱為種類(kinds),可以在GHCI中使用:k來對類型進行種類的分析。

GHCI使用 " * "表示具體的類型,如果不是具體類型,就是可以通過一個或多個參數得到具體類型的不完全類型。

如果理解了類型構造子本身也是函數這一點,本節的內容還是比較容易理解的。

第九章 輸入與輸出

第九章開始就沒有中文的翻譯了,只能看英文資料,英文9-14章戳我

到了第九章,我們終于可以寫Hello World了!一本教材講到大半才講輸入輸出的,也算是比較罕見了,呵呵。

在Haskell中,我們并不能像在命令式編程中一樣隨意改變非const變量的值。Haskell保持著這樣一個特性:對于一個函數,只要它的調用參數不變,那么它返回的值總是不變。Haskell不會試圖改變已經確定的變量,而是試圖返回新的變量。那么這里出現一個問題:如果Haskell并不改變任何變量,那么它就無法輸出——因為輸出會改變屏幕。為此,Haskell設計了一種機制將非純函數式編程(即與輸入輸出打交道的部分)與函數式編程(即前面八章介紹的內容)隔離開來,這里將這種機制稱為side-effects,直譯為邊界效應。

Hello World!

Hello World總是每種語言必須提到的東西。對于Haskell,輸出Helloword只需要一行代碼,嗯,不愧是優雅與簡潔的典范。

main = putStrLn "Hello world!"

main表示主函數,所有涉及IO的函數都在main中執行。所以main函數經常被寫作main:: IO (),當然()也可以換成其他的返回類型。

putStrLn函數的解釋是

putStrLn :: String->IO ()

也就是輸入一個字符串,執行一個IO action,返回一個空的tuple。

因為IO行為是非純函數行為,所以Haskell設計了do塊來將所有的非純函數進行封裝,最后通過<-操作符將IO取得的值綁定到一個變量上。do塊中,除了最后一個IO action 其他的均可綁定到一個變量上,不過如putStrLn這種函數的返回值肯定是(),所以綁定沒什么意義。最后一個IO action會將返回值綁定給main本身。do塊這種行為方式類似于verilog中的begin…end語句塊。

如果不涉及IO,仍然使用前面學過的let … in…來直接綁定變量,不過這里in可以省略,缺省成為整個do塊中有效。

return語句:在haskell中return語句只能在IO塊中使用,它表示一個IO行為,輸出一個可以通過變量綁定的值。return語句并不能從該段程序中返回。我們只有在需要執行一次什么都不做的IO或者不希望返回最后一個IO action取得的值時使用return語句。

main:main本身即是一個IO函數,所以可以通過在main函數結尾調用它來遞歸該函數。

其他IO函數:

函數原型

解釋

putStr

類似putStrLn,但尾部不輸出換行符

putChar/getChar

輸出/入字符

print:: Show a => a -> IO ()

輸出一切屬于show類型類的數據

sequence:: Monad m => [m a] -> m [a]

執行參數1中的I/O動作,返回動作的結果

mapM :: Monad m => (a -> m b) -> [a] -> m [b]

map的I/O版,相當于sequence . map

mapM_ :: Monad m => (a -> m b) -> [a] -> m ()

同上,只是不再返回I/O動作的執行結果

注意map一個I/O動作到一個list中,并不會真正執行這個list中的動作。想要真正執行,必須使用sequence函數,當然,方便起見,可以使用mapM或mapM_。

這一塊介紹了Control.Monad內的幾個函數,when函數取一個布爾值和一個I/O動作作為參數,如果bool值為真,執行該動作,否則返回一個什么都不執行的I/O動作;forever永久執行參數中的I/O動作;forM類似于mapM,只是參數的順序顛倒。

文件與流

其實I/O這一塊Haskell與命令式語言并無不同,要注意的只是do塊的位置和I/O函數結果的綁定。對于文件I/O,haskell與C基本一致,常用函數如下:

函數原型

解釋

getContents :: IO String

從標準流讀入數據直到EOF(Ctrl+D)

interact :: (String -> String) -> IO ()

對輸入執行參數1的函數,輸出結果

openFile :: FilePath -> IOMode -> IO Handle

System.IO,打開文件,選擇方式,返回句柄

hGetContents :: Handle -> IO String

根據句柄返回文件內容

hClose :: Handle -> IO ()

根據句柄關閉文件

withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r

整合的一個函數,打開文件并使用參數3處理文件,完成后關閉文件

hGetLine/hPutStr/hputStrLn/hGetChar

大體同標準流,只是參數中多了文件句柄

readFile :: FilePath -> IO String

讀取文件

writeFile :: FilePath -> String -> IO ()

寫入文件,模式為截斷

appendFile :: FilePath -> String -> IO ()

寫入文件,模式為追加

getTemporaryDirectory :: IO FilePath

System.Directory,讀取臨時文件夾路徑

openTempFile :: FilePath -> String -> IO (FilePath, Handle)

System.IO,打開臨時文件,返回一個tuple

removeFile/renameFile

System.Directory,刪除、重命名文件,參數是路徑不是句柄

注意這里的讀取文件相關函數都是惰性的。以上青色字體來自System.IO庫,沒有注明的來自Precluded庫。使用句柄做參數的函數均以h開頭。

緩沖控制:如果需要修改編譯器默認的緩沖機制,可以使用函數hSetBuffering來修改,使用hFlush來強制刷新緩沖區

這里介紹了Unix下管道操作符 | 的使用。簡單來說,可以通過管道操作符將上一個動作的輸出作為下一個動作的輸入。

命令行參數

同C語言程序一樣,Haskell也是可以接受命令行參數的。C語言將命令行參數作為main函數的參數傳遞,而Haskell主要使用兩個函數來取得用戶輸入的參數,import System.Environment

getArgs :: IO [String]

取出所有參數

getProgName :: IO String

取得程序名稱

另外后面給了錯誤退出的函數(類似<stdlib>中的exit函數):errorExit。

隨機數發生器

隨機數發生器在任何語言中都是標準庫自帶的函數/類。雖然Haskell要求純函數的輸入一定時,輸出固定,但是實際上幾乎所有的語言中隨機數發生器生成的都是偽隨機數,所以Haskell這個特性并不意味著其實現比一般的語言困難。相關函數如下(import System.Random):

random :: (RandomGen g, Random a) => g -> (a, g)

參數給出一個隨機數種子,返回一個隨機數和一個新的種子

mkStdGen :: Int -> StdGen

以一個整數為參數,生成一個標準的種子

randoms :: (RandomGen g, Random a) => g -> [a]

根據種子生成一個無限長的隨機序列

randomR :: (RandomGen g, Random a) => (a, a) -> g -> (a, g)

參數1的pair限制了最后取得隨機數的范圍

randomRs :: (RandomGen g, Random a) => (a, a) -> g -> [a]

根據參數1生成規定范圍的無限長list

getStdGen :: IO StdGen

取得一個全局的隨機數發生器種子

newStdGen :: IO StdGen

刷新全局隨機數發生器

??

注意random函數的返回類型可以是任何類型,所以在使用的時候必須在后面加上類型約束::(type1,type2)作為隨機數和種子的類型,如果使用StdGen的種子,則一般返回類型為(Int,StdGen)。

如果不執行newStdGen,那么getStdGen總是返回同樣的種子。

另外這里還介紹了read加入了錯誤處理的版本reads,后者再不能讀取參數時將返回空list。

二進制字符串

不清楚這個翻譯是否合適,總之Bytestrings主要介紹二進制讀寫文件的方法。不同于C語言,這里有兩個版本的二進制讀取,一個是嚴格的非惰性版本,另外一個是惰性版本,分別來自Data.ByteString和Data.ByteString.Lazy. 對于lazy版本,這里和前面介紹的文件IO函數的實現也有所不同——它每次最少讀取64K字節的東西。大體來講ByteString的相關函數與Data.List中的函數接口一致,不過在類型約束中用ByteString代替了[a],用Word8代替了a。函數pack將Word8打包成ByteString,unpack用于解包;fromChunks將嚴格版(非惰性)轉換為惰性版,toChunks則相反;cons和cons'用來取代list的 :操作符,注意后者適用于非惰性版;還有其他一些函數,對應于list中的某些函數,這里就不列舉了。

轉載于:https://www.cnblogs.com/livewithnorest/archive/2012/08/02/2620718.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/377284.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/377284.shtml
英文地址,請注明出處:http://en.pswp.cn/news/377284.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

組合問題 已知組合數_組合和問題

組合問題 已知組合數Description: 描述&#xff1a; This is a standard interview problem to make some combination of the numbers whose sum equals to a given number using backtracking. 這是一個標準的面試問題&#xff0c;它使用回溯功能將總和等于給定數字的數字進…

可變參數模板、右值引用帶來的移動語義完美轉發、lambda表達式的理解

可變參數模板 可變參數模板對參數進行了高度泛化&#xff0c;可以表示任意數目、任意類型的參數&#xff1a; 語法為&#xff1a;在class或者typename后面帶上省略號。 Template<class ... T> void func(T ... args) {// }T:模板參數包&#xff0c;args叫做函數參數包 …

BI-SqlServer

一.概述 SqlServer數據倉庫ETL組件 IntegrationServiceOLAP組件 AnalysisService報表 ReportingServiceMDX(查多維數據集用的)和DMX(查挖掘模型用的)。二.商業智能-Analysis Services 項目 構建挖掘模型1構建挖掘模型2構建挖掘模型3三.商業智能-SqlServerAnalysis-Asp.net WebS…

python 子圖大小_Python | 圖的大小

python 子圖大小In some cases, the automatic figure size generated by the matplotlib.pyplot is not visually good or there could be some non-acceptable ratio in the figure. So, rather than allowing a pyplot to decide the figure size, we can manually define t…

《設計模式整理》

目錄常見設計模式如何保證單例模式只有一個實例單例模式中的懶漢與餓漢模式OOP設計模式的五項原則單例模式中的懶漢加載&#xff0c;如果并發訪問該怎么做常見設計模式 單例模式&#xff1a; 單例模式主要解決了一個全局使用的類頻繁的創建和銷毀的問題。 單例模式下確保某一個…

JSON學習資料整理

1.什么是JSON JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。它基于JavaScript的一個子集。 JSON采用完全獨立于語言的文本格式&#xff0c;但是也使用了類似于C語言家族的習慣&#xff08;包括C, C, C#, Java, JavaScript, Perl, Python等&#xff09;。這些…

OSI七層模型及其數據的封裝和解封過程

OSI(Open System Interconnection)參考模型把網絡分為七層: 1.物理層(Physical Layer) 物理層主要傳輸原始的比特流,集線器(Hub)是本層的典型設備; 2.數據鏈路層(Data Link Layer) 數據鏈路層負責在兩個相鄰節點間無差錯的傳送以幀為單位的數據,本層的典型設備是交換機(Switch)…

rss聚合模式案例_RSS的完整形式是什么?

rss聚合模式案例RSS&#xff1a;真正簡單的聯合 (RSS: Really Simple Syndication) RSS is an abbreviation of Really Simple Syndication. It is also called Rich Site Summary. It is quality attainment for the syndication of collection of web content and used to di…

《MySQL——38道查詢練習(無連接查詢)》

目錄一、準備數據1、創建數據庫2、創建學生表3、創建教師表4、創建課程表5、創建成績表6、添加數據二、查詢練習1、查詢 student 表的所有行2、查詢 student 表中的 name、sex 和 class 字段的所有行3、查詢 teacher 表中不重復的 department 列4、查詢 score 表中成績在60-80之…

工作經常使用的SQL整理,實戰篇(一)

工作經常使用的SQL整理&#xff0c;實戰篇&#xff08;一&#xff09; 原文:工作經常使用的SQL整理&#xff0c;實戰篇&#xff08;一&#xff09;工作經常使用的SQL整理&#xff0c;實戰篇&#xff0c;地址一覽&#xff1a; 工作經常使用的SQL整理&#xff0c;實戰篇&#xff…

XPth和XSLT的一些簡單用法

&#xff08;目的在于讓大家知道有這個東西的存在&#xff09; XPath:即XML Path語言(Xpath)表達式使用路徑表示法(像在URL中使用一樣)來為XML文檔的各部分尋址&#xff01; 關于XPath如何使用了&#xff0c;我們來看看&#xff01;當然這里面的代碼只是入門&#xff0c;更深層…

isc dhcp_ISC的完整形式是什么?

isc dhcpISC&#xff1a;印度學校證書 (ISC: Indian School Certificate) ISC is an abbreviation of the Indian School Certificate. It alludes to the 12th class examination or higher secondary examination conducted by the Council for the Indian School Certificat…

《MySQL——連接查詢》

內連接&#xff1a; inner join 或者 join 外連接 1、左連接 left join 或 left outer join 2、右連接 right join 或 right outer join 3、完全外連接 full join 或 full outer join 圖示理解 全連接 創建person表和card表 CREATE DATABASE testJoin;CREATE TABLE person (…

win7下 apache2.2 +php5.4 環境搭建

這篇文章很好 沒法復制 把鏈接粘貼來http://www.360doc.com/content/13/0506/13/11495619_283349585.shtml# 現在能復制了&#xff1a; 把任何一篇你要復制、卻不讓復制的文章收藏入收藏夾(直接CtrlD,確定) 2在收藏夾中&#xff0c;右擊剛才收藏的那個網址&#xff0c;點屬性 3…

HDU_1533 Going Home(最優匹配) 解題報告

轉載請注明出自cxb:http://blog.csdn.net/cxb569262726 題目鏈接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1533 說實話&#xff0c;這個題目剛開始還真看不出是完備匹配下的最大權匹配&#xff08;當然&#xff0c;這個也可以用網絡流做。&#xff08;應該是添加…

c#中 uint_C#中的uint關鍵字

c#中 uintC&#xff03;uint關鍵字 (C# uint keyword) In C#, uint is a keyword which is used to declare a variable that can store an integral type of value (unsigned integer) the range of 0 to 4,294,967,295. uint keyword is an alias of System.UInt32. 在C&…

《MySQL——事務》

目錄事務的必要性MySQL中如何控制事務手動開啟事務事務的四大特征事務的四大特征事務開啟方式事務手動提交與手動回滾事務的隔離性臟讀現象不可重復讀現象幻讀現象串行化一些補充使用長事務的弊病commit work and chain的語法是做什么用的?怎么查詢各個表中的長事務&#xff1…

運行在TQ2440開發板上以及X86平臺上的linux內核編譯

一、運行在TQ2440開發板上的linux內核編譯 1、獲取源碼并解壓 直接使用天嵌移植好的“linux-2.6.30.4_20100531.tar.bz2”源碼包。 解壓&#xff08;天嵌默認解壓到/opt/EmbedSky/linux-2.6.30.4/中&#xff09; tar xvjf linux-2.6.30.4_20100531.tar.bz2 -C / 2、獲取默認配置…

ArcCatalog ArcMap打不開

原來是因為&#xff1a; 連接了電信的無線網卡 關掉即可 啟動ArcCatalog之后再開啟無線網卡 沒問題&#xff01;轉載于:https://www.cnblogs.com/ccjcjc/archive/2012/08/21/2649867.html

Python熊貓– GroupBy

Python熊貓– GroupBy (Python Pandas – GroupBy) GroupBy method can be used to work on group rows of data together and call aggregate functions. It allows to group together rows based off of a column and perform an aggregate function on them. GroupBy方法可用…