haskell类型

1、源文件

介绍这个主要是由于下文不少代码写在源文件中,而后从ghci加载源文件进行测试。app

建立一个文本文件,在其中输入,并保存为add.hs文件函数

-- file: add.hs
add x y = x - y

打开ghci,加载刚才的文件(假设文件目录为e:\haskell\add.hs),命令以下测试

ghci> :cd e:\haskell
ghci> :load add.hs
ghci> add 2 1

2、语言特性

1. 延迟计算

表达式的值在须要用到的时候才被计算,好比在一个文本文档中输入以下,并保存为lazy.hsui

-- file: lazy.hs
add x y = if left || right then x + y else x -y
left = True
right = length [1..] > 0

而后加载这个文件,并执行 add 2 1,你会发现结果为3,而不是一直等待计算结果,说明right的值并无被计算出来。在不少其余语言中(如C#),逻辑或or被特殊处理,从而在左份量为真的时候对右份量短路(short-circuits),而haskell中,(||)运算符只是一个普通的函数,并因为其延迟计算的特性,致使左份量为真的时候,右份量不被计算出来。固然了,haskell中也能够写出短路的逻辑或函数spa

myOr l r = if l then l else r

haskell中还有一个称做守卫表达式的东东,上面说的myOr函数等价于设计

-- file: myOr.hs
myOr l r | l = l
myOr l r = r

2. 类型

a) 函数类型

咱们看take函数的类型code

ghci> :type take
take :: Int -> [a] -> [a]

符号->是右结合的,即,类型说明等价于orm

take :: Int -> ([a] -> [a])

b)代数数据类型

定义一个新的数据类型对象

-- file: Object.hs
data ObjectProperty = Object Int String [String]
                      deriving (Show)

这个类型ObjectProperty与三元组(Int, String, [String])的元素相同,然而haskell认为它们类型不相同。若是要定义一个ObjectProperty类型变量,blog

ghci> let object = Object 0 "i" ["j", "k"]
ghci> :type it
it :: ObjectProperty
ghci> :type Object
Object :: Int -> String -> [String] -> ObjectProperty

分析: Object是值(数据)构造器,ObjectProperty是对象类型,固然,也能够将对象类型与值构造器写成相同的,如

data Object = Object Int String [String]

然而二者仅仅是名称相同而已。

 对于Object的元素分别为Int,String等类型,还能够自定义这些类型的同义类型,以下

-- file: Object.hs
type ObjectId = Int
type ObjectName = String
type ObjectFields = [String]
data Object = Object ObjectId ObjectName ObjectFields

其实跟C++中的typedef关键字相似。

能够查看值构造器Object的类型(是一个函数)

ghci> :type Object
Object :: ObjectId -> ObjectName -> ObjectFields -> Object

 因为Object类型中元素是匿名的,能够经过匹配来获取元素值,例如

ghci> let object = Object 0 "a" ["b", "c"]
ghci> let Object x y z = object
ghci> x
0
ghci> :type x
x :: ObjectId

c)枚举类型

-- file: Roygbiv.hs
data Roygbiv =     Red
                |  Orange
                |  Yellow
                |  Green
                |  Blue
                   deriving (Eq, Show)

其中Red、Orange等都为值构造器

d) 记录类型

前面简单介绍过模式匹配。好比想内窥一下Object(前文介绍的自定义代数数据类型)的内部元素,则可使用模式匹配,咱们此次定义一个函数来获取某个元素

getId (Object id _ _) = id
getName (Object _ name _) = name
getFields (Object _ _ fields) = fields

然而这种方法仍是显得代码大块,看起来不爽。

能够定义记录类型,以下

-- file: Object.hs
data Object = Object {
    oid :: Int,
    oname :: String,
    ofields :: [String]
    } deriving (Show)

这个类型定义其实跟下面的形式几乎相同

-- file: Object.hs
data Object = Object Int String [String]
                     deriving (Show)

getId :: Object -> Int
getId (Object id _ _) = id

getName :: Object -> String
getName (Object _ name _) = name

getFields :: Object -> [String]
getFields (Object _ _ fields) = fields

加载上面那个文件时,依然能够向之前那么使用Object类型

ghci> let object = Object 1 "az" ["b"]

或者以下使用

ghci> -- order of elements can be varied
ghci> let object = Object{oid = 10, ofields = ["i"], oname="str"}

e)参数化类型

跟C#中的泛型相似。好比定义一个类型MyMaybe以下(注意与Prelude模块中的Maybe区分,虽然功能相似)

-- file: Maybe.hs
data MyMaybe a = MyJust a
               | MyNothing

这里a就是参数化类型,a能够是String或者Int等,还能够是MyMaybe类型,即嵌套

ghci> let a = MyJust 1
ghci> let b = Just a
ghci> :type b
b :: Num a => Maybe (MyMaybe a)

f)递归类型

haskell中的List类型与C#中的不一样,它是一个递归类型,以下

-- file: MyList.hs
data MyList a = Cons a (MyList a)
              |  Empty
                 deriving (Show)

这样设计,使得对其进行递归计算方便。

 好比获取List的首项,能够写成以下

-- append to the former MyList.hs file end

x = Empty
y = Cons 1 x

myHead (Cons i j) = i
myHead Empty = error "MyList.myHead: empty list"

这里对Empty获取首项会抛出一个错误,这样处理仍是能够的,咱们不能返回Empty,不然跟myHead的第一个计算式的返回类型不一样(第一个计算式返回类型为a,而Empty类型为MyList a)。不过,若是结合Maybe类型,或者上面介绍的MyMaybe类型,则能够避免抛出错误,从而使得代码更好看些。处理以下

myHead (Cons i j) = MyJust i
myHead Empty = MyNothing
相关文章
相关标签/搜索