- Haskell 教程
- Haskell - 首页
- Haskell - 概述
- Haskell - 环境设置
- Haskell - 基本数据模型
- Haskell - 基本运算符
- Haskell - 决策
- Haskell - 类型和类型类
- Haskell - 函数
- Haskell - 函数详解
- Haskell - 函数组合
- Haskell - 模块
- Haskell - 输入和输出
- Haskell - Functor
- Haskell - Monads
- Haskell - Zippers
- Haskell 有用资源
- Haskell - 快速指南
- Haskell - 有用资源
- Haskell - 讨论
Haskell - 类型和类型类
Haskell 是一种函数式语言,并且是强类型语言,这意味着整个应用程序中使用的的数据类型在编译时都会被编译器知晓。
内置类型类
在 Haskell 中,每个语句都被视为一个数学表达式,并且此表达式的类别称为类型。你可以将“类型”视为编译时使用的表达式的类型。
要了解更多关于类型的信息,我们将使用“:t”命令。一般来说,类型可以被视为一个值,而类型类可以被视为一组类似类型的集合。在本章中,我们将学习不同的内置类型。
Int
Int 是一个表示整数类型数据的类型类。从 -2147483647 到 2147483647 之间的每个整数都属于Int 类型类。在下面的示例中,函数fType()将根据其定义的类型进行操作。
fType :: Int -> Int -> Int fType x y = x*x + y*y main = print (fType 2 4)
这里,我们将函数fType()的类型设置为int。该函数接受两个int值并返回一个int值。如果你编译并执行这段代码,它将产生以下输出:
sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts sh-4.3$ main 20
Integer
Integer 可以被视为Int的超集。此值不受任何数字的限制,因此 Integer 可以是任何长度,没有任何限制。为了了解Int和Integer类型之间的基本区别,让我们修改上面的代码如下:
fType :: Int -> Int -> Int fType x y = x*x + y*y main = print (fType 212124454 44545454454554545445454544545)
如果你编译上面的代码,将会抛出以下错误消息:
main.hs:3:31: Warning: Literal 44545454454554545445454544545 is out of the Int range - 9223372036854775808..9223372036854775807 Linking main ...
发生此错误是因为我们的函数 fType() 期望一个 Int 类型的值,而我们传递了一些非常大的 Int 类型的值。为了避免此错误,让我们用“Integer”修改类型“Int”,并观察差异。
fType :: Integer -> Integer -> Integer fType x y = x*x + y*y main = print (fType 212124454 4454545445455454545445445454544545)
现在,它将产生以下输出:
sh-4.3$ main 1984297512562793395882644631364297686099210302577374055141
Float
请看下面的代码片段。它展示了 Float 类型在 Haskell 中是如何工作的:
fType :: Float -> Float -> Float fType x y = x*x + y*y main = print (fType 2.5 3.8)
该函数将两个浮点值作为输入,并产生另一个浮点值作为输出。当你编译并执行此代码时,它将产生以下输出:
sh-4.3$ main 20.689999
Double
Double 是一个浮点数,在末尾具有双精度。请看下面的示例:
fType :: Double -> Double -> Double fType x y = x*x + y*y main = print (fType 2.56 3.81)
当你执行上面的代码片段时,它将生成以下输出:
sh-4.3$ main 21.0697
Bool
Bool 是布尔类型。它可以是 True 或 False。执行以下代码以了解 Bool 类型在 Haskell 中是如何工作的:
main = do
let x = True
if x == False
then putStrLn "X matches with Bool Type"
else putStrLn "X is not a Bool Type"
这里,我们定义了一个变量“x”作为 Bool,并将其与另一个布尔值进行比较以检查其原始性。它将产生以下输出:
sh-4.3$ main X is not a Bool Type
Char
Char 表示字符。单引号内的任何内容都被视为字符。在下面的代码中,我们修改了之前的fType()函数以接受 Char 值并返回 Char 值作为输出。
fType :: Char-> Char fType x = 'K' main = do let x = 'v' print (fType x)
上面的代码片段将使用'v'的char值调用fType()函数,但它返回另一个 char 值,即'K'。以下是它的输出:
sh-4.3$ main 'K'
请注意,我们不会显式地使用这些类型,因为 Haskell 足够智能,可以在声明之前捕获类型。在本教程的后续章节中,我们将看到不同的类型和类型类是如何使 Haskell 成为一种强类型语言的。
EQ 类型类
EQ 类型类是一个接口,它提供测试表达式相等性的功能。任何想要检查表达式相等性的类型类都应该是此 EQ 类型类的一部分。
上面提到的所有标准类型类都是此EQ类的一部分。无论何时我们使用上面提到的任何类型检查任何相等性,我们实际上都在调用EQ类型类。
在下面的示例中,我们使用“==”或“/=”运算在内部使用EQ类型。
main = do
if 8 /= 8
then putStrLn "The values are Equal"
else putStrLn "The values are not Equal"
它将产生以下输出:
sh-4.3$ main The values are not Equal
Ord 类型类
Ord 是另一个接口类,它为我们提供了排序的功能。到目前为止我们使用过的所有类型都是此Ord接口的一部分。像 EQ 接口一样,Ord 接口可以使用“>”、“<”、“<=”、“>=”、“compare”进行调用。
请在下面找到我们使用此类型类的“compare”功能的示例。
main = print (4 <= 2)
这里,Haskell 编译器将检查 4 是否小于或等于 2。由于它不是,因此代码将产生以下输出:
sh-4.3$ main False
Show
Show 具有将参数打印为字符串的功能。无论其参数是什么,它始终将结果打印为字符串。在下面的示例中,我们将使用此接口打印整个列表。“show”可用于调用此接口。
main = print (show [1..10])
它将在控制台上产生以下输出。这里,双引号表示它是字符串类型的值。
sh-4.3$ main "[1,2,3,4,5,6,7,8,9,10]"
Read
Read 接口与 Show 做相同的事情,但它不会以字符串格式打印结果。在下面的代码中,我们使用了read接口读取一个字符串值,并将其转换为 Int 值。
main = print (readInt "12") readInt :: String -> Int readInt = read
这里,我们将一个字符串变量(“12”)传递给readInt方法,该方法在转换后返回 12(一个 Int 值)。以下是它的输出:
sh-4.3$ main 12
Enum
Enum 是另一种类型的类型类,它在 Haskell 中启用顺序或有序功能。此类型类可以通过Succ、Pred、Bool、Char等命令访问。
以下代码展示了如何查找 12 的后继值。
main = print (succ 12)
它将产生以下输出:
sh-4.3$ main 13
Bounded
所有具有上限和下限的类型都属于此类型类。例如,Int类型数据具有“9223372036854775807”的最大边界和“-9223372036854775808”的最小边界。
以下代码展示了 Haskell 如何确定 Int 类型的最大和最小边界。
main = do print (maxBound :: Int) print (minBound :: Int)
它将产生以下输出:
sh-4.3$ main 9223372036854775807 -9223372036854775808
现在,尝试查找 Char、Float 和 Bool 类型的最大和最小边界。
Num
此类型类用于数值运算。Int、Integer、Float 和 Double 等类型属于此类型类。请看下面的代码:
main = do print(2 :: Int) print(2 :: Float)
它将产生以下输出:
sh-4.3$ main 2 2.0
Integral
Integral 可以被视为 Num 类型类的子类。Num 类型类包含所有类型的数字,而 Integral 类型类仅用于整数。Int 和 Integer 是此类型类下的类型。
Floating
与 Integral 类似,Floating 也是 Num 类型类的一部分,但它仅包含浮点数。因此,Float 和Double属于此类型类。
自定义类型类
与任何其他编程语言一样,Haskell 允许开发人员定义用户定义的类型。在下面的示例中,我们将创建一个用户定义的类型并使用它。
data Area = Circle Float Float Float surface :: Area -> Float surface (Circle _ _ r) = pi * r ^ 2 main = print (surface $ Circle 10 20 10 )
这里,我们创建了一个名为Area的新类型。接下来,我们使用此类型来计算圆的面积。在上面的示例中,“surface”是一个函数,它将Area作为输入并产生Float作为输出。
请记住,“data”在这里是一个关键字,并且 Haskell 中的所有用户定义类型都以大写字母开头。
它将产生以下输出:
sh-4.3$ main 314.15927