• Haskell用户定义的类型


    Haskell用户定义的类型(User-Defined Types)

    类型同义词
    您已经有一个用户定义类型的示例:type Cipher = String
    如果需要,可以嵌套类似这种情况的类型同义词:
    type Pos = (Int, Int)
    type Trans = Pos -> Pos
    (Pos 表示 2D 点,Trans 是采用一个 2D 点并返回另一个点的函数的类型)
    虽然您可以引用其他类型的同义词,并将它们嵌套在中,但您不能自行引用它们

    定义新类型
    如果要定义一个全新的类型(而不是现有类型的同义词),则需要使用数据机制
    例如,Bool 在标准 Prelude 中定义如下:
    数据布尔 = 假 | 真的
    “|”读作“或”,替代项称为构造函数。
    构造函数必须以大写字母开头(就像类型一样)
    你不能在不同的类型定义中使用同名的构造函数
    构造函数名称没有实际意义,它们只是标签(但请选择合理的标签,就像您选择合理的函数和参数名称一样)

    使用数据声明
    如果你以这种方式定义新类型,你可以像 Haskell 中的常规类型一样使用它们,例如:
    data Move = North | East | South | West

    move :: Move -> Pos -> Pos
    move North (x, y) = (x, y+1)
    move East (x, y) = (x+1, y)
    move South (x, y) = (x, y-1)
    move West (x, y) = (x-1, y)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    “Move”类型可以采用四个值之一;函数“move”(注意小写“m”)与 Move 类型完全不同)

    构造函数可以有参数!
    Move 和 Bool 都使用不带参数的构造函数,但它们可以更复杂,例如
    data Shape = Circle Float | Rect Float Float
    在这里,我们定义了一个 Shape 类型,它可以是圆形或矩形。
    如果是圆形,则使用单个参数构造;如果是矩形,两个参数。
    area :: Shape -> Float
    area (Circle r) = pi * r^2
    area (Rect l w) = l * w
    Shape 的构造函数实际上是构造函数。 您甚至可以检查它们的类型,例如
    在这里插入图片描述

    参数化数据声明
    数据声明也可以参数化。
    你们中的一些人已经使用了 Maybe 类型,它被定义为:
    data Maybe a = Nothing | Just a
    这表示“Maybe a”类型的值可以是 Nothing 或 Just x 形式,其中 x 是 a 类型的值
    有些人发现它是因为他们发现了 elemIndex 函数,它可以找到一个元素在列表中的第一个位置。 它需要一个 Maybe 类型,因为列表中可能没有该项目的实例。

    新式Newtype
    如果您的数据类型只有一个构造函数和一个参数,则可以改用 newtype 机制。
    以下三种类型声明非常相似:
    newtype Nat = N Int
    type Nat = Int
    data Nat = N Int
    特别是第一个和最后一个非常相似。 newtype 虽然更有效。 编译器使用 newtype 进行检查,然后将其替换为运行时的类型同义词

    递归类型
    请记住,这是无效的
    type Tree = (Int, [Tree])
    但是使用 newtype 或数据机制声明的类型可以是递归的,所以我可以
    data Tree a = Empty | Node a [Tree a]
    或者如果我想要一棵二叉树:
    data Tree a = Empty | Node (Tree a) a (Tree a)

    类Classes
    特别看一下 Eq,它在序言中被声明为:

    (==), (/=) :: a -> a -> Bool
    x /= y = not (x == y)
    
    • 1
    • 2

    这意味着,要成为此类的实例的类型,它必须支持相等和不等运算符。

    假设我希望能够根据它们的面积订购我的 Shape 类型(如前所述)的项目,我可以指定 shape 类派生该父类:

    data Shape = Circle Float | Rect Float Float
    				deriving (Eq, Ord)
    
    
    • 1
    • 2
    • 3

    这仅在参数是这些类的成员时才有效
    这将为我们提供一个表达式的答案,例如

    Rect 1.0 4.0 < Rect 2.0 3.0

    因为 Float 的参数本身就派生出 Eq 和 Ord
    它并不总是给我们正确的答案:

    Rect 1.0 4.0 < Rect 2.0 1.0
    True
    Rect 1.0 4.0 < Rect 1.0 3.0
    False
    Rect 1.0 4.0 < Rect 2.0 1.0
    True

    实例Instances
    或者,我们可以明确说明如何解释这些运算符。
    使用实例机制:

    instance Eq Shape where
       Circle r1 == Circle r2 = r1 == r2
       Rect a b == Rect c d  =  (a*b) == (c*d)
    instance Ord Shape where
       compare (Circle r1) (Circle r2) = compare r1 r2
       compare (Rect a b) (Rect c d) = compare (a*b) (c*d)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    我们不需要定义每个运算符,就像 Eq 和 Ord 类一样,它们是如何根据我们提供的那些计算出来的

  • 相关阅读:
    91.第十九章 MYSQL数据库 -- 数据库原理(一)
    【Java 高并发 多线程 线程安全 JUC】目录
    Pandas-04(缺失数据、分组、合并连接、级联)
    Java-数据库基本概念
    安卓使用okhttpfinal下载文件,附带线程池下载使用
    redis 无法远程连接问题。
    docker容器重启后,jdk环境变量失效,jar包不会自动重启
    Java异常、继承结构、处理异常、自定义异常、SpringBoot中全局捕获处理异常
    LeetCode刷题(python版)——Topic65.有效数字
    JavaWeb JSTL标签库
  • 原文地址:https://blog.csdn.net/kirsten111111/article/details/126352362