• 3天学习haskellhaskell第三天


    一类

    1 自定义类型

    data关键字用来定义自己的数据类型 (类型必须大写)

    data Rank=Ten | jack |queen |king |ace

    2 多态

    (1)backwards::[a]->[a]

    backwards []=[]

    backwards (h:t)=backwards t ++ [h]

    [a]的类型是一个元素为任意类型的列表。

    (2)data Triplet a =Trio a a a deriving (Show)

    a是一个类型变量。表示具有相同类型的三元组是Triplet类型的。(deriving (Show)   用来显示数据)

    3递归类型

    树的定义:节点,要么是叶子,要么是树的列表。值在叶子上。

    data Tree a=Children [Tree a] |Leaf a deriving (Show)

    Tree是类型构造器,Children ,Leaf 是数据构造器

    使用:

    let tree =Children [Leaf 1,Children[Leaf 2,Leaf3] ]

    tree

    求树的深度:

    depth(Leaf _)=1

    depth(Children c)=1+max(map depth c)

    4类 (类似于操作)

    不同于面向对象编程中的概念,类定义了哪些操作可以在那些输入上进行。用来精细控制多态和重载。

    如果类型支持类的所有函数,那么这个类型是类的一个实例。



    二  monad [1]

    1  定义

    (1)monad 作用是 以一种特定属性的方式组合函数

     (2) Haskell 中把单子定义成一个 type class

    class Monad m where

      (>>=) :: m a -> (a -> m b) -> m b

      return :: a ->m a

    对任一个型别建构元 m,如果我们能定义出 >>=  return 两个函数,并满足后述的一些条件,m 就是一个单子。对 type class 我们不在此解释太多,只需知道这是 Haskell允许我们重载 (overload) >>=  return 等符号、使许多型别的类似函数可以用同一组符号表示的机制就可以了。型别 m a 代表一个「结果为型别 a 的运算」,运算过程中可能产生单子 m希望描述的副作用,如同 Maybe a 用来表示一个结果是 a,但可能会产生例外的运算。函数return 把一个型别为 a 的值「提升」到 m a.  Maybe 的例子中,return 就是 Just.函数>>= 则是提升到 m 之上的函数应用,左手边是一个 m a,右手边是一个从 a  m b 的函数;x >>= f 大致上的意思是执行 x 代表的运算,如果得到一个型别是 a 的值,把他传给f.结果的型别是 m b

     (3)主要包括:

    1 类型构造器-定义一个类型。

    2 return 返回。

    3 >>= 操作, 该操作  接受一个类型的实例  和 一个函数 (实例写在>>=前边,函数写在>>=后边),输出一个结果.

    例如,

    (>>=) : Maybe a -> (a -> Maybe b) -> Maybe b

    (Just x) >>= f = f x

    Nothing >>= f = Nothing


    例如Maybe monad的定义,

    data Maybe a=Nothing | Just a (Just是一种特定的类型)

    instance Mnoad Maybe where

    return  =just

    Nothing >>=f =Nothing

    (Just x)>>= f =f  x


    2 单子定理

    单子定律

    单子的 return  >>= 两个函数当然不能随便写。型别建构元 m 要称为单子,return >>= 须满足下面三个定律:

    1.    (return x) >>= f == f x,

    2.    m >>= return == m,

    3.    (m >>= f) >>= g == m >>= (\x -> f x >>= g).

    头两条定律谈到 return 的合理行为。第一条确保 return x 不含副作用: (return x)>>= f 应该和把 x 直接丢给 f 一样。第二条定律中,把 m 的结果直接 return 回来并不改变其值。第三条是 >>= 的递移律,可和函数组合的定义比较:

    f (g x) = (f . g) x

    这条定律确保 >>= 的行为确实类似函数应用。

    读者可以检查一下 Maybe  return  >>= 确实满足这三条定律。目前 Haskell 本身无法自动检查这三条定律,只能希望程序员遵守。程序员照理说也希望所有单子都满足这些定律,以便于程序的推理分析。不幸的是确实有些很有用的「单子」为了效率因素会在某些情况下违反单子定律。以后有机会再谈。

    3 列表单子

    我们进一步扩充算式,添加一个 Or 操作数:

    data Expr = Num Int | Neg Expr | Add Expr Expr
              | Div Expr Expr | Or Expr Expr

    此处的Or 倒不是逻辑上的「或」,而是非确定(non-deterministic)运算:Or e1 e2 的值可能是 e1,也可能是 e2.非确定操作数有点牵强地用在这里仅是为了举例,但在程序语言的应用中,需要处理非确定性的情形并不少见。例如一个文法 parse一段句子,可能有不只一种 parse的方法。我们会需要写程序找出所有 parse.

    我们希望找出一个算式所有可能的值,因此 eval 的型别改为 Expr-> [Int],将所有可能的值放在一个串行中。串行是另一个单子的常见例子。函数 return 将一个型别为 a 的值 x提升成串行,自然的选择是传回 [x],因为 x 是自己的唯一值:

    return :: a -> [a]
    return x = [x]

    假设 xs 是一个型别为 [a] 的串行,函数 f 拿一个 a,将所有的可能结果放在型别为 [b] 的串行中。我们如何把 xs 喂给 f 呢?答案是先用 f 处理 xs 里的每个元素,再把结果接起来:

    (>>=) :: [a] -> (a -> [b]) -> [b]
    xs >>= f = concat (map f xs)

    写成 list comprehension可能更清楚xs >>= f = [y | x <-xs, y <- f x].注意 return >>= 的型别分别是 a-> [a]  [a]-> (a -> [b]) -> [b],符合 Monad class的要求。(此处为了说明而标上型别。在 Haskell中,由于型别已在 class宣告中给过一次,在此是不加型别的。)

    函数 eval 该怎么处理新加的 Or 呢?Ore1 e2 的所有可能值是 e1 的所有可能值以及e2 的所有可能值,所以:

    eval (Or e1 e2) = eval e1 ++ eval e2

    而对于 NumNegAdd 等情况,很幸运地,eval 可在不修改的情况下直接使用新的 return >>= 定义。例如

    eval (Add (Or (Num 1) (Num 3)) (Or (Num 3) (Num 4)))

    的值会是 [4,5,6,7].

    至于 Div 呢?函数 eval  Div 条款用了一个 mzero,当时的值是 Nothing.在此处只要把mzero 设定为 [] 就可以了 -- 分母为零时 Div 没有值,因此我们传回空串行。

    串行单子在此处用来表达非确定性计算,有时当我们说「非确定性单子」时,指的是串行。在 Haskell这种缓式语言中,串行尾端在需要时才会算出来,因此 eval 的行为像是回溯 (backtracking):先用第一个值去试试看,如果可用就一直使用下去,如果失败则回溯回来换成下一个值。读者可以试试看 eval(Div (Num 4) (Add (Neg (Num 2)) (Or (Num 0) (Num 2)))) (4 / (-2 + (0 OR 2))) 是怎么算出来的。有人也因此把串行称作「回溯单子」。但不论非确定性或回溯还另有其他数据结构可表示(有的也许会特别强调公平性或效率等等因素)。此处我们还是简单地称之为串行单子。

    参考 [1] http://www.iis.sinica.edu.tw/~scm/ncs/2009/11/a-monad-primer/

    参考 [2]http://cookoo.iteye.com/blog/27006

    参考 [3]http://zhiwei.li/text/2011/05/haskell%E7%9A%84monad/


  • 相关阅读:
    P2711 小行星 最小割
    bzoj2141: 排队 cdq分治
    bzoj 4237: 稻草人 cdq分治
    P1527 [国家集训队]矩阵乘法 整体二分
    P2617 Dynamic Rankings 整体二分
    P3834 【模板】可持久化线段树 1(主席树) 整体二分
    SPREAD for Windows Forms 代码片段
    PHP+Oracle Instant Client
    SQL利用CASE按分组显示合计
    SPREAD for Windows Forms 控制输入法
  • 原文地址:https://www.cnblogs.com/catkins/p/5270681.html
Copyright © 2020-2023  润新知