模块#

简介#

import <module> (<function>, ...)
import <module> hiding (<function>, ...)
import qualified <module>
import <module> as <alias>
  • 模块:一组相关函数、类和类型类,一个 Haskell 程序就是主模块加载其他模块的过程;

  • 导入:import关键字导入同一目录下的模块,导入模块后,模块包含的函数、类和类型类可直接使用;

    import Data.List
    
    numUniques :: Eq a => [a] -> Int
    numUniques = length . nub
    
  • 部分导入:小括号()包括要导入的函数、类或类型类;

    import Data.List (nub, sort)
    
  • 反选:hiding关键字可将指定函数、类或类型类排除导入范围;

    import Data.List hiding (nub) -- 导入除 'nub' 外的所有元素
    
  • 命名冲突:qualified关键字指定导入的模块中重名函数、类或类型类前必须添加模块名;

    import qualified Data.Map
    
    test1 :: [Integer] -- 来自 "Prelude"
    test1 = filter odd [1, 2, 3, 4]
    
    test2 :: Data.Map.Map Integer [Char] -- 来自 "Data.Map"
    test2 =
        Data.Map.filter (> "a") (Data.Map.fromList [(1, "a"), (2, "b")])
    
  • 别名:as关键字为模块起别名;

    import qualified Data.Map as M
    res :: M.Map Integer [Char]
    res = M.filter (> "a") (M.fromList [(1, "a"), (2, "b")])
    
  • GHCi::module [+/-] [*]<mod> ...可在 GHCi 中导入或移除相应模块,多个模块用空格分隔,若省略符号则清除除Prelude模块的所有模块,并切换至指定模块;

    Prelude> :m + Data.List
    Prelude Data.List> :t nub
    nub :: Eq a => [a] -> [a]
    Prelude Data.List> :m - Data.List
    Prelude> :t nub
    
    <interactive>:1:1: error: Variable not in scope: nub
    

导出模块#

module <module>.<submodule>
    ( <function>
    , <algebraicDataType>(<valueConstructor, ...)
    , module <module>
    , ...
    ) where
  • 导出:将当前模块中的函数、类型或类型类导出,使得外部只能使用导出的函数、类型或类型类,未导出的不能从外部使用;

  • 语法格式:

    • module关键字定义当前模块名;

      • 模块名应与模块文件名相同;
      • 可指定子模块,所有子模块位于同一目录(模块)下;
    • 小括号()内定义导入当前模块时会被导出的函数、类型或类型类,多个函数、类型或类型类用逗号分隔;

      • 导出代数数据类型时,可在其后指定要导出的值构造器,多个值构造器用逗号分隔;

      • 若要导出所有值构造器,则可使用..

        module Shapes
            ( Point(..) -- 从代数类型 'Point' 导入所有构造器
            , Shape(Circle, Rectangle) -- 仅导入两个构造器
            , surface   -- 导入函数
            ) where
        
      • 若要导出模块,则用module关键字指定模块名;

    • where关键字表示模块的起始;

 1module Geometry.Sphere
 2    ( volume
 3    , area
 4    ) where
 5
 6volume :: Float -> Float
 7volume radius = (4.0 / 3.0) * pi * (radius ^ 3)
 8
 9area :: Float -> Float
10area radius = 4 * pi * (radius ^ 2)
 1module Geometry.Cuboid
 2    ( volume
 3    , area
 4    ) where
 5
 6volume :: Float -> Float -> Float -> Float
 7volume a b c = rectangleArea a b * c
 8
 9area :: Float -> Float -> Float -> Float
10area a b c =
11    rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2
12
13rectangleArea :: Float -> Float -> Float -- 未导出
14rectangleArea a b = a * b
 1module Geometry.Cube
 2    ( volume
 3    , area
 4    ) where
 5
 6import qualified Geometry.Cuboid as Cuboid
 7
 8volume :: Float -> Float
 9volume side = Cuboid.volume side side side
10
11area :: Float -> Float
12area side = Cuboid.area side side side

官方模块#

  • 所有官方模块及其子模块、函数、类和类型类的签名和源代码均可在官网查看;

Prelude#

  • Prelude模块默认向每个 Haskell 程序导入多个标准函数、类和类型类;
  • Prelude是进入 GHCi 时的默认模块,不可移除;

Data.List#

  • Data.List模块提供关于列表操作的函数;
  • Prelude模块导入了部分该模块函数;

插入#

Data.List.intersperse :: a -> [a] -> [a]#

接受一个元素和一个列表,将该元素插入列表中每两个元素之间。

import Data.List
exp1 = intersperse 0 [1, 2, 3, 4] -- [1,0,2,0,3,0,4]
Data.List.intercalate :: [a] -> [a] -> [a]#

接受一个列表和一个包含列表的列表,将该列表插入列表的列表中每两个列表之间。

exp2 = intercalate [0, 0] [[1, 2], [3, 4]] -- [1,2,0,0,3,4]
Data.List.insert :: Ord a => a -> [a] -> [a]#

接受一个元素和一个列表,从列表第一个元素开始,若小于等于下一个元素,则在当前元素后下一元素前插入该元素。

exp3 = insert 4 [3, 5, 1, 2, 8, 2] -- [3,4,5,1,2,8,2]
exp4 = insert 4 [1, 3, 4, 4, 1]    -- [1,3,4,4,4,1]

删除#

Data.List.delete :: Eq a => a -> [a] -> [a]#

接受一个元素和一个列表,移除第一个匹配。

exp5 = delete 'h' "hey there" -- "ey there"

重构#

Data.List.transpose :: [a] -> [a]#

接受一个包含列表的列表,并用每个子列表对应索引的元素组成新的子列表(可将列表的列表看作矩阵,transpose函数转换矩阵的行和列)。

exp6 = transpose [[1, 2, 3], [4, 5, 6]] -- [[1,4],[2,5],[3,6]]
Data.List.sort :: Ord a => [a] -> [a]#

对列表升序排序。

exp7 = sort [1, 3, 2, 6, 5]       -- [1,2,3,5,6]
exp8 = sort "Alice in Wonderland" -- "  AWacddeeiillnnnor"
Data.List.group :: Eq a => [a] -> [a]#

接受一个列表,若相邻元素相同则组成子列表。

exp9 = group [1, 1, 1, 2, 2, 3, 3, 3, 3] -- [[1,1,1],[2,2],[3,3,3,3]]
Data.List.inits :: [a] -> [a]#

init函数类似,但会对结果迭代应用。

exp10 = inits [1, 2, 3] -- [[],[1],[1,2],[1,2,3]]
Data.List.tails :: [a] -> [a]#

tail函数类似,但会对结果迭代应用。

exp11 = tails [1, 2, 3] -- [[1,2,3],[2,3],[3],[]]

searchSublist :: Eq a => [a] -> [a] -> Bool
searchSublist needle haystack =
    let nlen   = length needle
        isPart = \b x -> (take nlen x == needle) || b
    in  foldl isPart False (tails haystack)
Data.List.partition :: (a -> Bool) -> [a] -> ([a], [a])#

接受一个谓词和一个列表,返回一个序对,第一个元素为所有符合谓词的元素的列表,第二个为不符合的列表。

exp12 = partition (`elem` ['A' .. 'Z']) "ALICEmaryAMBERcindy"
        -- ("ALICEAMBER","marycindy")

迭代#

判断#

Data.List.isInfixOf :: Eq a => [a] -> [a] -> Bool#

接受两个列表,若第二个列表包含第一个列表,则返回True

exp13 = "alice" `isInfixOf` "alice in wonderland" -- True
Data.List.isPrefixOf :: Eq a => [a] -> [a] -> Bool#

接受两个列表,若第二个列表开头包含第一个列表,则返回True

exp14 = isPrefixOf "hey" "oh hey there" -- False
Data.List.isSuffixOf :: Eq a => [a] -> [a] -> Bool#

接受两个列表,若第二个列表结尾包含第一个列表,则返回True

exp15 = "there" `isSuffixOf` "oh there hey" -- False

查找#

Data.List.find :: Foldable t => (a -> Bool) -> t a -> Maybe a#

接受一个谓词和一个列表,以Maybe类型返回第一个符合谓词的元素。

exp16 = find (> 3) [1 .. 5] -- Just 4
exp17 = find (> 9) [1 .. 5] -- Nothing
Data.List.findIndex :: (a -> Bool) -> [a] -> Maybe Int#

find函数类似,但以Maybe类型返回第一个符合谓词的元素索引。

exp18 = findIndex (== 4) [5, 3, 2, 1, 6, 4] -- Just 5
Data.List.findIndices :: (a -> Bool) -> [a] -> [Int]#

findIndex函数类似,但返回所有符合谓词的索引。

exp19 = findIndices (`elem` ['A' .. 'Z']) "Where Are The Caps?"
        -- [0,6,10,14]
Data.List.elemIndex :: Eq a => a -> [a] -> Maybe Int#

elem函数类似,以Maybe类型返回该元素第一个匹配的索引。

exp20 = 4 `elemIndex` [1 .. 5] -- Just 3
exp21 = 9 `elemIndex` [1 .. 5] -- Nothing
Data.List.elemIndices :: Eq a => a -> [a] -> [Int]#

elemIndex函数类似,但返回所有匹配索引的列表。

exp22 = 4 `elemIndices` [1, 4, 2, 4] -- [1,3]
exp23 = 9 `elemIndices` [1, 4, 2, 4] -- []

合并#

Data.List.zip4 :: [a] -> [b] -> [c] -> [d] -> [(a, b, c, d)]#

zipzip3函数相似,合并对应数字数量的列表,最大为zip7

Data.List.zipWith4 :: (a -> b -> c -> d -> e) -> [a] -> [b] -> [c] -> [d] -> [e]#

zipWithzipWith3函数相似,合并对应数字数量的列表,最大为zipWith7

拆分#

集合#

Data.List.nub :: Eq a => [a] -> [a]#

接受一个列表,移除所有重复元素。

exp24 = nub [1, 2, 2, 3, 1, 3, 4, 4] -- [1,2,3,4]
Data.List.(\\) :: Eq a => [a] -> [a] -> [a]#

对两个列表进行差集运算,移除第一个列表中与第二个列表元素相同的元素。

exp25 = [1 .. 10] \\ [1, 3, 6, 8, 9] -- [2,4,5,7,10]
Data.List.union :: Eq a => [a] -> [a] -> [a]#

对两个列表进行合集运算,若第二个列表中的元素在第一个中不存在,则追加到第一个列表的末尾。

exp26 = "alice" `union` "alice in wonderland" -- "alice nwodr"
Data.List.intersect :: Eq a => [a] -> [a] -> [a]#

对两个列表进行交集运算。

exp27 = [1 .. 7] `intersect` [5 .. 10] -- [5,6,7]

泛用#

Data.List.genericLength :: Num i => [a] -> i#
Data.List.genericTake :: Integral i => i -> [a] -> [a]#
Data.List.genericDrop :: Integral i => i -> [a] -> [a]#
Data.List.genericSplitAt :: Integral i => i -> [a] -> ([a], [a])#
Data.List.genericIndex :: Integral i => [a] -> i -> a#
Data.List.genericReplicate :: Integral i => i -> a -> [a]#

对应函数的泛用版,原函数只接受或返回Int类型,而泛用版接受或返回更宽泛的类型(如IntegralNum)。

exp28 = let xs = [1 .. 10] in sum xs / genericLength xs -- 5.5
Data.List.nubBy :: (a -> a -> Bool) -> [a] -> [a]#
Data.List.deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a]#
Data.List.unionBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]#
Data.List.intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]#
Data.List.groupBy :: (a -> a -> Bool) -> [a] -> [a]#

对应函数的泛用版,原函数使用(==)进行相等性比较,而泛用版用相等性函数比较,通常和Data.Function.on函数结合使用。

exp29 = let xs = [-3, -2, 5, 4, -6, 5, 7]
        in  groupBy (\x y -> (x > 0) == (y > 0)) xs
        -- [[-3,-2],[5,4],[-6],[5,7]]
Data.List.sortBy :: (a -> a -> Ordering) -> [a] -> [a]#
Data.List.insertBy :: (a -> a -> Ordering) -> a -> [a] -> [a]#
Data.List.maximumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a#
Data.List.minimumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a#

对应函数的泛用版,原函数使用compare函数进行大小比较,而泛用版用大小比较函数比较,通常和Data.Function.on函数结合使用。

import Data.Function (on)
exp30 = let xs = [[5, 4, 5, 4, 4], [1, 2, 3], [3, 5, 4, 3], [], [2], [2, 2]]
        in  sortBy (compare `on` length) xs
        -- [[],[2],[2,2],[1,2,3],[3,5,4,3],[5,4,5,4,4]]

Data.Char#

字符判断#

Data.Char.isControl :: Char -> Bool#

判断字符是否为 Unicode 控制字符。

exp1 = isControl '\n' -- True
Data.Char.isSpace :: Char -> Bool#

判断字符是否为 Unicode 空白字符(空格、换行、回车、水平制表符、垂直制表符)。

exp2 = isSpace '\r' -- True
Data.Char.isLower :: Char -> Bool#

判断字符是否为 Unicode 小写字母。

exp3 = isLower 'ф' -- True
Data.Char.isUpper :: Char -> Bool#

判断字符是否为 Unicode 大写字母。

exp4 = isUpper 'D' -- True
Data.Char.isLetter :: Char -> Bool#
Data.Char.isAlpha :: Char -> Bool#

判断字符是否为 Unicode 字母。

exp5 = isAlpha 'ה' -- True
Data.Char.isAlphaNum :: Char -> Bool#

判断字符是否为 Unicode 字母或 Unicode 数字。

exp6 = isAlphaNum '三' -- True
exp7 = isAlphaNum 'i'  -- True
Data.Char.isPrint :: Char -> Bool#

判断字符是否为 Unicode 可打印字符。

exp8 = isPrint ' ' -- True
Data.Char.isDigit :: Char -> Bool#

判断字符是否为 ASCII 数字。

exp9  = isDigit 'i' -- False
exp10 = isDigit '0' -- True
Data.Char.isOctDigit :: Char -> Bool#

判断字符是否为 ASCII 八进制数字。

exp11 = isOctDigit '8' -- False
Data.Char.isHexDigit :: Char -> Bool#

判断字符是否为 ASCII 十六进制数字。

exp12 = isHexDigit 'E' -- True
Data.Char.isMark :: Char -> Bool#

判断字符是否为 Unicode 标记字母(与前一个字符结合形成带符号字母)。

exp13 = isMark '́'      -- True
exp14 = map isMark "á" -- [False,True]
Data.Char.isNumber :: Char -> Bool#

判断字符是否为 Unicode 数字字符。

exp15 = isNumber 'i' -- False
exp16 = isNumber 'Ⅸ' -- True
Data.Char.isPunctuation :: Char -> Bool#

判断字符是否为 Unicode 标点符号。

exp17 = isPunctuation '。' -- True
Data.Char.isSymbol :: Char -> Bool#

判断字符是否为 Unicode 货币或数学符号。

exp18 = isSymbol '△' -- True
exp19 = isSymbol '-' -- False
Data.Char.isSeparator :: Char -> Bool#

判断字符是否为 Unicode 空格或 Unicode 分隔符。

exp20 = isSeparator '\160' -- True
Data.Char.isAscii :: Char -> Bool#

判断字符是否为 ASCII 字符。

exp21 = isAscii '\a' -- True
Data.Char.isLatin1 :: Char -> Bool#

判断字符是否为 Latin1 (ISO 8859-1) 字符。

exp22 = isLatin1 'á' -- True
Data.Char.isAsciiLower :: Char -> Bool#

判断字符是否为 ASCII 小写字母。

Data.Char.isAsciiUpper :: Char -> Bool#

判断字符是否为 ASCII 大写字母。

类型#

Data.Char.generalCategory :: Char -> GeneralCategory#

返回字符的一般类型GeneralCategory

GeneralCategory类型包含31种数据,本身是Eq类型类的成员。

exp1 = generalCategory ' '  -- Space
exp2 = generalCategory 'A'  -- UppercaseLetter
exp3 = generalCategory 'a'  -- LowercaseLetter
exp4 = generalCategory '\n' -- Control
exp5 = generalCategory '1'  -- DecimalNumber

转换#

Data.Char.toUpper :: Char -> Char#

将字符转换为相应 Unicode 大写字母,非大小写字母不变。

exp1 = toUpper 'ф' -- '\1060'
Data.Char.toLower :: Char -> Char#

将字符转换为相应 Unicode 小写字母,非大小写字母不变。

exp2 = toLower '\1060' -- '\1092'
Data.Char.toTitle :: Char -> Char#

将字符转换为相应 Unicode 标题大写字母或 Unicode 大写字母,对于非连写字母,标题大写字母与大写字母一致。

exp3 = toTitle 'f' -- 'F'
Data.Char.digitToInt :: Char -> Int#

将字符转换为对应`Int`数字,该字符必须满足`isHexDigit`,否则报错。

exp4 = digitToInt 'a' -- 10
Data.Char.intToDigit :: Int -> Char#

将 0 到 15 闭区间的Int数字转换为字符。

exp5 = intToDigit 12 -- 'c'
Data.Char.ord :: Char -> Int#

将 Unicode 字符转换为 Unicode 码。

exp6 = ord 'a' -- 97
exp7 = ord '三' -- 19977
Data.Char.chr :: Int -> Char#

将 Unicode 码转换为 Unicode 字符。

 1exp8 = chr 120 -- 'x'
 2
 3-- | 凯撒密码加密。
 4encode :: Int -> String -> String
 5encode shift msg = map (chr . (+ shift) . ord) msg
 6exp9 = encode 3 "Alice Liddell" -- "Dolfh#Olgghoo"
 7
 8-- | 凯撒密码解密。
 9decode :: Int -> String -> String
10decode shift msg = encode (negate shift) msg
11exp10 = decode 3 "Dolfh#Olgghoo" -- "Alice Liddell"

Data.Map#

  • Data.Map模块提供处理映射的函数;

Data.Set#

  • Data.Set模块提供处理集合的函数;