美文网首页
03 类型和类型类

03 类型和类型类

作者: 勤劳的悄悄 | 来源:发表于2016-09-10 16:21 被阅读231次

    类型

    Haskell 是强类型和静态类型的,所有的数据都有明确的类型

    静态类型的好处

    • 编译期间就可以发现很多类型导致的错误
    • 支持类型推导

    表达式的类型

    • 使用 :t 命令可以查看表达式的类型
    • 类型必须用大写字母开头
    • :: 读作 “它的类型为”
    ghci> :t 'a'
    'a' :: Char  
    
    ghci> :t True  
    True :: Bool  
    
    ghci> :t "HELLO!"  
    "HELLO!" :: [Char]  
    
    ghci> :t (True, 'a')  
    (True, 'a') :: (Bool, Char) 
    
    ghci> :t 4 == 5  
    4 == 5 :: Bool
    
    

    函数的类型

    函数也有明确的类型,包括参数的类型和返回值的类型。

    比如前面那个过滤大写字母的函数,输入一个字符串,输出一个字符串,类型如下

    removeNonUppercase :: [Char] -> [Char]  
    removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
    

    注意: [Char]String 是等价的

    多个参数的函数,类型之间用 -> 分隔,最后一项是输出类型

    addThree :: Int -> Int -> Int -> Int  
    addThree x y z = x + y + z
    

    几种常见的基本类型

    类型 说明
    Int 32 位整数,有边界
    Integer 大整数,没有边界
    Float 单精度浮点数,精度稍差
    Double 双精度浮点数,精度较高
    Bool 布尔值
    Char 字符类型

    类型变量

    有的时候,函数在定义的时候参数的类型不能确定,等到使用时参数传入后才可以确定类型,这时可以用一个变量代替参数类型。

    比如处理列表的 head 函数。列表可以是任意类型的列表,返回值只要和列表中元素的类型一致即可。

    ghci> :t head  
    head :: [a] -> a
    

    类型变量都使用单个小写字母

    类型类入门

    TypeClasses 叫类型类,他是对类型约束和规范,他要求类型必须满足某些规格,有点类似接口。

    类型被某种 TypeClasses 约束后,和这种 TypeClasses 相关的函数就可以处理被其约束的类型,

    下面是 == 函数的类型声明,该函数的参数被 Eq 约束,说明这个参数可以被 Eq 相关的函数所处理

    ghci> :t (==)
    (==) :: (Eq a) => a -> a -> Bool
    

    读作: 相等函数取两个相同类型的值作为参数,传回一个布尔值。两个参数的类型必须符合 Eq 类型类的约束

    Eq 规定了判断相等性的规范,凡是能够比较相等性的类型都必须被 Eq 类约束,比如数字、字符、字符串等等

    ghci> 5 == 5
    True
    
    ghci> 5 /= 5
    False
    
    ghci> 'a' == 'a'
    True
    
    ghci> "Ho Ho" == "Ho Ho"
    True
    
    ghci> 3.432 == 3.432
    True
    

    elem 函数中,因为其参数要使用 == ,所以也被 Eq 约束

    ghci> :t elem
    (Eq a)=>a->[a]->Bool
    

    基本的类型类

    Eq

    任何需要使用相等性判断的类型,即希望该类型能够被 ==/= 等函数处理,都要受 Eq 约束

    Ord

    需要比较大小,能够被 <, >, <=, >=, compare 等相关函数处理的类型,都受它约束

    注意:要成为 Ord 的成员,首先需要加入 Eq

    ghci> "Abrakadabra" < "Zebra"  
    True  
    
    ghci> "Abrakadabra" `compare` "Zebra"  
    LT  
    
    ghci> 5 >= 2  
    True  
    
    ghci> 5 `compare` 3  
    GT
    
    Show

    show 函数可以将一个数据对象转换成字符串

    任何需要能转换成字符串,并能够被 show 函数处理的的类型,都受它约束

    ghci> show 3  
    "3"  
    
    ghci> show 5.334  
    "5.334"  
    
    ghci> show True  
    "True"
    
    Read

    read 函数可以将字符串转换成一个数据对象,他是 show 函数的反函数

    ReadShow 正好相反,任何类型想要获取从字符串转换成对象的能力,即能够被 read 函数处理,都受它约束

    ghci> read "True" || False  
    True  
    
    ghci> read "8.2" + 3.8  
    12.0 
    
    ghci> read "5" - 2  
    3  
    
    ghci> read "[1,2,3,4]" ++ [3]  
    [1,2,3,4,3]
    

    转换时须明确类型

    需要注意的是,如果字符串转换的类型不能推断,则需要明确指定,否则会报错

    下面代码中,4 既可以转换成 Int 类型,也可以转换成 Integer Float 类型,因为没有明确指定,编译器会报错

    ghci> read "4"  
    < interactive >:1:0:  
        Ambiguous type variable `a' in the constraint:  
          `Read a' arising from a use of `read' at <interactive>:1:0-7  
        Probable fix: add a type signature that fixes these type variable(s)
    

    下面明确指定类型

    ghci> read "5" :: Int  
    5  
    
    ghci> read "5" :: Float  
    5.0  
    
    ghci> (read "5" :: Float) * 4  
    20.0  
    
    ghci> read "[1,2,3,4]" :: [Int]  
    [1,2,3,4]  
    
    ghci> read "(3, 'a')" :: (Int, Char)  
    (3, 'a')
    
    

    Enum

    连续类型,有前置和后继,能够被 succpred 等函数处理。

    相关类型有 (), Bool, Char, Ordering, Int, Integer, Float 和 Double

    ghci> ['a'..'e']  
    "abcde"  
    
    ghci> [LT .. GT]  
    [LT,EQ,GT]  
    
    ghci> [3 .. 5]  
    [3,4,5]  
    
    ghci> succ 'B'  
    'C'
    
    Bounded

    有上下边界的类型,能够被 minBoundmaxBound 等函数处理

    ghci> minBound :: Int  
    -2147483648  
    
    ghci> maxBound :: Char  
    '\1114111' 
    
    ghci> maxBound :: Bool  
    True  
    
    ghci> minBound :: Bool  
    False
    

    注意,如果元组中的分量都是有边界的,那么元组也是有边界的

    ghci> maxBound :: (Bool, Int, Char)  
    (True,2147483647,'\1114111')
    

    Num

    如果你的类型需要进行四则运算,即能够使用 + - * / 等相关函数,则应该被 Num 约束

    下面的代码说明, * 要求的参数类型虽然还不确定,但是只要符合 Num 规范就可以,因此传给他 Int Float 都可以

    ghci> :t (*)  
    (*) :: (Num a) => a -> a -> a
    

    注意,乘号两边的数字类型必须一致,否则会报错,比如

    (5 :: Int) * (6 :: Integer)
    
    Integral

    对所有整数的规范

    Floating

    对所有浮点数的规范

    重要函数 fromIntegral

    这个函数会将一个对象的约束条件从 Integral 转换成 Num,受 Num 约束的数是更通用的数,可以使用更多的函数

    fromIntegral :: (Num b, Integral a) => a -> b
    

    历史问题

    由于历史原因,某些函数会返回整数类型,而不是通用数字类型。这时候如果用返回值和其他数字类型运算会出错。

    举例来说,length 函数的型别声明为:

    length :: [a] -> Int
    

    如果用 length 的返回值,给它加一个浮点数 3.2 就会报错,因为这是将浮点数和整数相加。

    这时候就需要

    fromIntegral (length [1,2,3,4]) + 3.2
    

    </br>

    相关文章

      网友评论

          本文标题:03 类型和类型类

          本文链接:https://www.haomeiwen.com/subject/vcbyettx.html