Haskell 中的模塊是含有一組相關的函數,類型和類型類的組合。而 Haskell 進程的本質即是從主模塊中引用其它模塊並調用其中的函數來執行操做。這樣能夠把代碼分紅多塊,只要一個模塊足夠的獨立,它裏面的函數即可以被不一樣的進程反覆重用。這就讓不一樣的代碼各司其職,提升了代碼的健壯性。html
Haskell 的標準庫就是一組模塊,每一個模塊都含有一組功能相近或相關的函數和類型。有處理 List 的模塊,有處理併發的模塊,也有處理複數的模塊,等等。目前爲止咱們談及的全部函數,類型以及類型類都是 Prelude
模塊的一部分,它缺省自動裝載。在本章,咱們看一下幾個經常使用的模塊,在開始瀏覽其中的函數以前,咱們先得知道如何裝載模塊.編程
在 Haskell中,裝載模塊的語法爲 import
,這必須得在函數的定義以前,因此通常都是將它置於代碼的頂部。無疑,一段代碼中能夠裝載不少模塊,只要將 import
語句分行寫開便可。裝載 Data.List
試下,它裏面有不少實用的 List 處理函數.數據結構
執行 import Data.List
,這樣一來 Data.List
中包含的全部函數就都進入了全局命名空間。也就是說,你能夠在代碼的任意位置調用這些函數.Data.List
模塊中有個 nub
函數,它能夠篩掉一個 List 中的全部重複元素。用點號將 length
和 nub
組合: length 。nub
,便可獲得一個與 (\xs -> length (nub xs))
等價的函數。併發
import Data.List
numUniques :: (Eq a) => [a] -> Int
numUniques = length 。nub
你也能夠在 ghci 中裝載模塊,若要調用 Data.List
中的函數,就這樣:編程語言
ghci> :m Data.List
若要在 ghci 中裝載多個模塊,沒必要屢次 :m
命令,一下就能夠所有搞定:ide
ghci> :m Data.List Data.Map Data.Set
而你的進程中若已經有包含的代碼,就沒必要再用 :m
了.函數
若是你只用獲得某模塊的兩個函數,大可僅包含它倆。若僅裝載 Data.List
模塊 nub
和 sort
,就這樣:學習
import Data.List (nub,sort)
也能夠只包含除去某函數以外的其它函數,這在避免多個模塊中函數的命名衝突頗有用。假設咱們的代碼中已經有了一個叫作nub
的函數,而裝入 Data.List
模塊時就要把它裏面的 nub
除掉.搜索引擎
import Data.List hiding (nub)
避免命名衝突還有個方法,即是 qualified import
,Data.Map
模塊提供一了一個按鍵索值的數據結構,它裏面有幾個和Prelude
模塊重名的函數。如 filter
和 null
,裝入 Data.Map
模塊以後再調用 filter
,Haskell 就不知道它到底是哪一個函數。以下即是解決的方法:spa
import qualified Data.Map
這樣一來,再調用 Data.Map
中的 filter
函數,就必須得 Data.Map.filter
,而 filter
依然是爲咱們熟悉喜好的樣子。可是要在每一個函數前面都加 個Data.Map
實在是太煩人了! 那就給它起個別名,讓它短些:
import qualified Data.Map as M
好,再調用 Data.Map
模塊的 filter
函數的話僅需 M.filter
就好了
要瀏覽全部的標準庫模塊,參考這個手冊。翻閱標準庫中的模塊和函數是提高我的 Haskell 水平的重要途徑。你也能夠各個模塊的源代碼,這對 Haskell 的深刻學習及掌握都是大有好處的.
檢索函數或搜尋函數字置就用 Hoogle,至關了不得的 Haskell 搜索引擎! 你能夠用函數名,模塊名甚至類型聲明來做爲檢索的條件.
........................
咱們已經見識過了幾個很酷的模塊,但怎樣才能構造本身的模塊呢? 幾乎全部的編程語言都容許你將代碼分紅多個文件,Haskell 也不例外。在編程時,將功能相近的函數和類型至於同一模塊中會是個很好的習慣。這樣一來,你就能夠輕鬆地一個 import
來重用其中的函數.
接下來咱們將構造一個由計算機幾何圖形體積和麪積組成的模塊,先重新建一個 Geometry.hs
的文件開始.
在模塊的開頭定義模塊的名稱,若是文件名叫作 Geometry.hs
那它的名字就得是 Geometry
。在聲明出它含有的函數名以後就能夠編寫函數的實現啦,就這樣寫:
module Geometry
( sphereVolume
,sphereArea
,cubeVolume
,cubeArea
,cuboidArea
,cuboidVolume
) where
如你所見,咱們提供了對球體,立方體和立方體的面積和體積的解法。繼續進發,定義函數體:
module Geometry
( sphereVolume
,sphereArea
,cubeVolume
,cubeArea
,cuboidArea
,cuboidVolume
) where
sphereVolume :: Float -> Float
sphereVolume radius = (4.0 / 3.0) * pi * (radius ^ 3)
sphereArea :: Float -> Float
sphereArea radius = 4 * pi * (radius ^ 2)
cubeVolume :: Float -> Float
cubeVolume side = cuboidVolume side side side
cubeArea :: Float -> Float
cubeArea side = cuboidArea side side side
cuboidVolume :: Float -> Float -> Float -> Float
cuboidVolume a b c = rectangleArea a b * c
cuboidArea :: Float -> Float -> Float -> Float
cuboidArea a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2
rectangleArea :: Float -> Float -> Float
rectangleArea a b = a * b
標準的幾何公式。有幾個地方須要注意一下,因爲立方體只是長方體的特殊形式,因此在求它面積和體積的時候咱們就將它看成是邊長相等的長方體。在這裏還定義了一個 helper
函數,rectangleArea
它能夠經過長方體的兩條邊計算出長方體的面積。它僅僅是簡單的相乘而已,分量不大。但請注意咱們能夠在這一模塊中調用這個函數,而它不會被導出! 由於咱們這個模塊只與三維圖形打交道.
當構造一個模塊的時候,咱們一般只會導出那些行爲相近的函數,而其內部的實現則是隱蔽的。若是有人用到了 Geometry
模塊,就不須要關心它的內部實現是如何。咱們做爲編寫者,徹底能夠隨意修改這些函數甚至將其刪掉,沒有人會注意到裏面的變更,由於咱們並不把它們導出.
要使用咱們的模塊,只需:
import Geometry
將 Geometry.hs
文件至於用到它的進程文件的同一目錄之下.
模塊也能夠按照分層的結構來組織,每一個模塊均可以含有多個子模塊。而子模塊還能夠有本身的子模塊。咱們能夠把 Geometry
分紅三個子模塊,而一個模塊對應各自的圖形對象.
首先,創建一個 Geometry
文件夾,注意首字母要大寫,在裏面新建三個文件
以下就是各個文件的內容:
sphere.hs
module Geometry.Sphere
( volume
,area
) where
volume :: Float -> Float
volume radius = (4.0 / 3.0) * pi * (radius ^ 3)
area :: Float -> Float
area radius = 4 * pi * (radius ^ 2)
cuboid.hs
module Geometry.Cuboid
( volume
,area
) where
volume :: Float -> Float -> Float -> Float
volume a b c = rectangleArea a b * c
area :: Float -> Float -> Float -> Float
area a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2
rectangleArea :: Float -> Float -> Float
rectangleArea a b = a * b
cube.hs
module Geometry.Cube
( volume
,area
) where
import qualified Geometry.Cuboid as Cuboid
volume :: Float -> Float
volume side = Cuboid.volume side side side
area :: Float -> Float
area side = Cuboid.area side side side
好的! 先是 Geometry.Sphere
。注意,咱們將它置於 Geometry
文件夾之中並將它的名字定爲 Geometry.Sphere
。對 Cuboid 也是一樣,也注意下,在三個模塊中咱們定義了許多名稱相同的函數,由於所在模塊不一樣,因此不會產生命名衝突。若要在 Geometry.Cube
使用 Geometry.Cuboid
中的函數,就不能直接 import Geometry.Cuboid
,而必須得qualified import
。由於它們中間的函數名徹底相同.
import Geometry.Sphere
而後,調用 area
和 volume
,就能夠獲得球體的面積和體積,而若要用到兩個或更多此類模塊,就必須得qualified import
來避免重名。因此就得這樣寫:
import qualified Geometry.Sphere as Sphere
import qualified Geometry.Cuboid as Cuboid
import qualified Geometry.Cube as Cube
而後就能夠調用 Sphere.area
,Sphere.volume
,Cuboid.area
了,而每一個函數都只計算其對應物體的面積和體積.
之後你若發現本身的代碼體積龐大且函數衆多,就應該試着找找目的相近的函數可否裝入各自的模塊,也方便往後的重用.
轉自:http://learnyouahaskell-zh-tw.csie.org/zh-cn/modules.html