前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >模块_Haskell笔记2

模块_Haskell笔记2

作者头像
ayqy贾杰
发布2019-06-12 14:40:30
1.7K0
发布2019-06-12 14:40:30
举报
文章被收录于专栏:黯羽轻扬黯羽轻扬

一.引用

引用模块的语法格式为:

代码语言:javascript
复制
-- 把模块中所有函数加入全局命名空间
import <module>
--  部分引用
import <module> (fn1, fn2)
-- 引入数据类型及其值构造器
import <module> (Datatype(constructor, constructor))
-- 引入所有值构造器
import <module> (Datatype(..))
--  hiding排除
import <module> hiding (fn)
--  保留命名空间
import qualified <module>
--  保留命名空间,并起别名
import qualified <module> as <alias>

例如:

代码语言:javascript
复制
import Data.List
import Data.List (nub, sort)
import Data.Tree (Tree(Node, Branch))
import Data.Tree (Tree(..))
import Data.List hiding (nub)
import qualified Data.Map
import qualified Data.Map as M

hiding语法能够缓解命名冲突问题,但不很方便,对于存在大量命名冲突的模块,可以通过qualified保留命名空间来避免冲突

GHCi环境

通过:m命令引用模块:

代码语言:javascript
复制
> :m Data.List
> :m Data.List Data.Map Data.Set

GHC 7.0之后,支持在GHCi环境直接使用import语法:

代码语言:javascript
复制
> import qualified Data.Map as M
> M.fromList [('a', 1)]
fromList [('a',1)]

所以,不用关注环境区别,具体见import qualified in GHCI

二.声明

模块用来组织代码,比如把功能相近的函数放到同一个模块中

例如二叉树的模块定义:

代码语言:javascript
复制
module BTree
-- 声明要暴露出去的函数及数据类型
( Tree
, singleton
, add
, fromList
, find
) where
-- 引入依赖模块
-- 定义数据类型及函数
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
singleton x = Node x EmptyTree EmptyTree

注意:

  • 强制要求模块名与文件名相同,所以对应的文件名应为BTree.hs
  • 模块声明必须位于首行(之前不能有import之类的东西,import可以放在where之后)

模块中数据结构的导出与import语法类似:

代码语言:javascript
复制
module MyModule (Tree(Branch, Leaf)) wheredata Tree a = Branch {left, right :: Tree a} | Leaf a

只暴露出数据结构Tree及其构造器BranchLeaf,也可以通过..暴露出所有值构造器:

代码语言:javascript
复制
module MyModule (Tree(..))

或者不暴露值构造器,仅允许通过工厂方法等方式获取该类型值(常见的比如Map.fromList):

代码语言:javascript
复制
module MyModule (Tree, factory)

缺点是,这样做就无法使用值构造器进行模式匹配了

子模块

模块具有树形层级结构,模块可以有子模块,子模块还可以有子模块……

对目录结构及命名有要求,例如:

代码语言:javascript
复制
.
├── main.hs
└── Math
   ├── Number.hs
   └── Vector.hs

包名要求首字母大写(Math),子模块文件名要与子模块名保持一致,大小写敏感性与环境有关(比如OSX不敏感)

三.标准库模块

标准库内置了很多强大的函数,可以通过Hoogle查看用法示例、类型声明、甚至源码,非常方便

Data.List

提供了大量的List操作函数,常用的比如map, filter,还有:

谓词:

代码语言:javascript
复制
-- every,全部为True才True
and :: Foldable t => t Bool -> Bool
-- some,有一个为True就True
or :: Foldable t => t Bool -> Bool
-- 常用的some,List中任意元素满足条件就True
any :: Foldable t => (a -> Bool) -> t a -> Bool
-- 常用的every,List中所有元素满足条件才True
all :: Foldable t => (a -> Bool) -> t a -> Bool

构造新List:

代码语言:javascript
复制
-- 在数组中插入分隔元素
intersperse :: a -> [a] -> [a]
-- 与intersperse类似,在二维数组中插入一维数组作为分隔元素,再打平到一维
intercalate :: [a] -> [[a]] -> [a]
-- 二维数组行列转置
transpose :: [[a]] -> [[a]]
-- 降维(把一组List连接成一个List)
concat :: Foldable t => t [a] -> [a]
-- 先做映射再降维,相当于concat . map
concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
-- 无限递归调用,把返回值再传入
iterate :: (a -> a) -> a -> [a]
-- 按位置断开,返回断开的两部分
splitAt :: Int -> [a] -> ([a], [a])
-- 取元素,直到不满足条件为止
takeWhile :: (a -> Bool) -> [a] -> [a]
-- 删元素,直到不满足条件为止
dropWhile :: (a -> Bool) -> [a] -> [a]
-- 按条件断开(首次不满足条件的位置),类似于takeWhile
span :: (a -> Bool) -> [a] -> ([a], [a])
-- 按条件断开(首次满足条件的位置)
break :: (a -> Bool) -> [a] -> ([a], [a])
-- 递归init,直到List为空
inits :: [a] -> [[a]]
-- 递归tail,直到List为空
tails :: [a] -> [[a]]

排序:

代码语言:javascript
复制
-- 归并排序
sort :: Ord a => [a] -> [a]
-- 插入到List中第一个大于等于该元素的元素之前
insert :: Ord a => a -> [a] -> [a]

分组:

代码语言:javascript
复制
-- 分组,依据是相邻且值相等
group :: Eq a => [a] -> [[a]]
-- 按条件分组,满足条件的一组,不满足的一组
partition :: (a -> Bool) -> [a] -> ([a], [a])

匹配:

代码语言:javascript
复制
-- 子串匹配(子List匹配),是否包含指定子串
isInfixOf :: Eq a => [a] -> [a] -> Bool
-- 子串匹配,是否以指定子串开头
isPrefixOf :: Eq a => [a] -> [a] -> Bool
-- 子串匹配,是否以为指定子串结尾
isSuffixOf :: Eq a => [a] -> [a] -> Bool
-- 元素包含性检测,是否包含指定元素
elem :: (Foldable t, Eq a) => a -> t a -> Bool
-- 元素包含性检测,是否不包含指定元素
notElem :: (Foldable t, Eq a) => a -> t a -> Bool

查找:

代码语言:javascript
复制
-- 按条件查找,返回第一个满足条件的元素
find :: Foldable t => (a -> Bool) -> t a -> Maybe a
-- 查找,返回第一个匹配元素索引或Nothing
elemIndex :: Eq a => a -> [a] -> Maybe Int
-- 查找所有
elemIndices :: Eq a => a -> [a] -> [Int]
-- 与find类似,但返回第一个满足条件的元素索引
findIndex :: (a -> Bool) -> [a] -> Maybe Int
-- 与find类似,但返回所有满足条件的项的索引
findIndices :: (a -> Bool) -> [a] -> [Int]

组合:

代码语言:javascript
复制
-- 组合List,还有zip3 ~ zip7
zip :: [a] -> [b] -> [(a, b)]
-- 组合List,并map一遍,还有zipWith3 ~ zipWith7
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

文本处理:

代码语言:javascript
复制
-- 字符串按行拆分(\n)
lines :: String -> [String]
-- join换行(\n)
unlines :: [String] -> String
-- 按空白字符拆分
words :: String -> [String]
-- join空格
unwords :: [String] -> String

删除元素:

代码语言:javascript
复制
-- 去重
nub :: Eq a => [a] -> [a]
-- 删掉第一个匹配元素
delete :: Eq a => a -> [a] -> [a]

集合运算:

代码语言:javascript
复制
-- 求差集,有重复元素的话,只删第一个
(\\) :: Eq a => [a] -> [a] -> [a]
-- 求并集
union :: Eq a => [a] -> [a] -> [a]
-- 求交集
intersect :: Eq a => [a] -> [a] -> [a]

更通用的版本

length, take, drop, splitAt, !!, replicate等函数参数或返回值都有要求Int类型,不够通用,因此提供了类型更通用的对应版本:

代码语言:javascript
复制
genericLength :: Num i => [a] -> i
genericTake :: Integral i => i -> [a] -> [a]
genericDrop :: Integral i => i -> [a] -> [a]
genericSplitAt :: Integral i => i -> [a] -> ([a], [a])
genericIndex :: Integral i => [a] -> i -> a
genericReplicate :: Integral i => i -> a -> [a]

nub, delete, union, intsect, group, sort, insert, maximum, minimum都通过==来判断是否相等,也提供了更通用的允许自己判断相等性的版本:

代码语言:javascript
复制
nubBy :: (a -> a -> Bool) -> [a] -> [a]
deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a]
unionBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
insertBy :: (a -> a -> Ordering) -> a -> [a] -> [a]
maximumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a
minimumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a

By的函数通常与Data.Function.on一起用:

代码语言:javascript
复制
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
f `on` g = \x y -> f (g x) (g y)

比如要表达按正负给相邻元素分组:

代码语言:javascript
复制
groupBy ((==) `Data.Function.on` (> 0)) values

语义很清楚:按照元素是否大于零,给它分类

另外,sortsortBy compare等价(默认的比较方式就是compare),要按List长度排序的话,这样做:

代码语言:javascript
复制
sortBy (compare `on` length) xs

语义同样非常清楚。所以

代码语言:javascript
复制
(==) `on`
compare `on`

都是非常棒的惯用套路

P.S.可以通过:browse <module>命令查看模块中的所有函数及数据类型定义的类型声明

Data.Char

String实际上是[Char]

代码语言:javascript
复制
type String = [Char]    -- Defined in ‘GHC.Base’

所以在处理字符串时,经常会用到Data.Char模块,提供了很多字符相关函数

判定字符范围:

代码语言:javascript
复制
--  控制字符
isControl :: Char -> Bool
--  空白符
isSpace :: Char -> Bool
-- 小写Unicode字符
isLower :: Char -> Bool
-- 大写Unicode字符
isUpper :: Char -> Bool
-- 字母
isAlpha :: Char -> Bool
-- 字母或数字
isAlphaNum :: Char -> Bool
-- 可打印字符
isPrint :: Char -> Bool
-- ASCII数字,0-9
isDigit :: Char -> Bool
-- 八进制数
isOctDigit :: Char -> Bool
-- 十六进制数
isHexDigit :: Char -> Bool
-- 字母,功能等价于isAlpha,实现方式不同
isLetter :: Char -> Bool
-- Unicode注音字符,比如法文
isMark :: Char -> Bool
-- Unicode数字,包括罗马数字等
isNumber :: Char -> Bool
-- 标点符号
isPunctuation :: Char -> Bool
-- 货币符号
isSymbol :: Char -> Bool
-- Unicode空格或分隔符
isSeparator :: Char -> Bool
-- ASCII字符(Unicode字母表前128位)
isAscii :: Char -> Bool
-- Unicode字母表前256位
isLatin1 :: Char -> Bool
-- 大写ASCII字符
isAsciiUpper :: Char -> Bool
-- 小写ASCII字符
isAsciiLower :: Char -> Bool

判断所属类型:

代码语言:javascript
复制
generalCategory :: Char -> GeneralCategory

返回的GeneralCategory是个枚举,共30个类别,例如:

代码语言:javascript
复制
> generalCategory 'a'
LowercaseLetter
> generalCategory ' '
Space

字符转换:

代码语言:javascript
复制
-- 转大写
toUpper :: Char -> Char
-- 转小写
toLower :: Char -> Char
-- 转title形式,与toUpper类似,部分连体字母有区别
toTitle :: Char -> Char
-- 字符转数字,要求[0-9,a-f,A-F]
digitToInt :: Char -> Int
-- 数字转字符
intToDigit :: Int -> Char
-- 字符转Unicode码
ord :: Char -> Int
-- Unicode码转字符
chr :: Int -> Char

所以,要实现简单的加解密可以这样做:

代码语言:javascript
复制
encode shift = map $ chr . (+ shift) . ord
decode shift = map $ chr . (subtract shift) . ord
-- 或者技巧性更足的
decode shift = encode $ negate shift

Data.Map

字典是键值对的无序列表,以平衡二叉树的形式存储,Data.Map提供了一些字典处理函数

P.S.Data.Map中的一些函数与PreludeData.List模块存在命名冲突,所以使用qualified import as保留命名空间并起个别名:

代码语言:javascript
复制
import qualified Data.Map as Map

构造新Map:

代码语言:javascript
复制
-- List转Map,有重复key的话,取最后一个value
Map.fromList :: Ord k => [(k, a)] -> Map.Map k a
-- Map转List
Map.toList :: Map.Map k a -> [(k, a)]
-- 与fromList类似,不直接丢弃重复key,允许手动处理
Map.fromListWith :: Ord k => (a -> a -> a) -> [(k, a)] -> Map.Map k a
-- 空Map
Map.empty :: Map.Map k a
-- 插入(k, a),返回新Map
Map.insert :: Ord k => k -> a -> Map.Map k a -> Map.Map k a
-- 与insert类似,允许处理重复key
Map.insertWith :: Ord k => (a -> a -> a) -> k -> a -> Map.Map k a -> Map.Map k a
-- 单元素Map
Map.singleton :: k -> a -> Map.Map k a
-- 映射到新Map
Map.map :: (a -> b) -> Map.Map k a -> Map.Map k b
-- 滤出新Map
Map.filter :: (a -> Bool) -> Map.Map k a -> Map.Map k a

取Map信息:

代码语言:javascript
复制
-- 判空
Map.null :: Map.Map k a -> Bool
-- 长度
Map.size :: Map.Map k a -> Int
-- 取所有key
Map.keys :: Map.Map k a -> [k]
-- 取所有value
Map.elems :: Map.Map k a -> [a]

查找:

代码语言:javascript
复制
-- 按key查找
Map.lookup :: Ord k => k -> Map.Map k a -> Maybe a
-- 包含性判断
Map.member :: Ord k => k -> Map.Map k a -> Bool

Data.Set

提供了集合相关的工具函数,结构上去Map类似,都以树结构存储

P.S.同样,也存在大量命名冲突,需要qualified import

代码语言:javascript
复制
import qualified Data.Set as Set

构造集合:

代码语言:javascript
复制
-- List转Set
Set.fromList :: Ord a => [a] -> Set.Set a

集合操作:

代码语言:javascript
复制
-- 求交集
Set.intersection :: Ord a => Set.Set a -> Set.Set a -> Set.Set a
-- 求差集
Set.difference :: Ord a => Set.Set a -> Set.Set a -> Set.Set a
-- 求并集
Set.union :: Ord a => Set.Set a -> Set.Set a -> Set.Set a
-- 判断子集
Set.isSubsetOf :: Ord a => Set.Set a -> Set.Set a -> Bool
-- 判断真子集
Set.isProperSubsetOf :: Ord a => Set.Set a -> Set.Set a -> Bool

注意,函数名很调皮啊,数组的List.intersect到集合这变成Set.intersection

Map中的很多函数在Set里也有对应版本,例如null, size, member, empty, singleton, insert, delete, map, filter

同样,集合可以用来实现一行代码去重

代码语言:javascript
复制
unique :: Ord a => [a] -> [a]
unique = Set.toList . Set.fromList

集合去重效率高于List.nub,但缺点是构造集合会对元素进行排序,所以得到的去重结果不保留原顺序List.nub会保留)

参考资料

  • Haskell/Modules
  • Haskell data type pattern matching:模式匹配自定义数据类型
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端向后 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.引用
    • GHCi环境
    • 二.声明
      • 子模块
      • 三.标准库模块
        • Data.List
          • 更通用的版本
            • Data.Char
              • Data.Map
                • Data.Set
                  • 参考资料
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档