1 / 56

如何编辑 Haskell 程序? 如何运行解释器? 如何在解释器下运行一个 Haskell 程序?

如何编辑 Haskell 程序? 如何运行解释器? 如何在解释器下运行一个 Haskell 程序?. 类型和函数. 一个程序(函数)形如 f :: A -> B 或者 f :: A -> B -> C, A, B, C 表示函数的输入数据集合和输出数据集合 例如 , Int 表示整数集合 double :: Int -> Int mymax :: Int -> Int -> Int 问题: 如何表示输入数据和输出数据? Haskell 提供了哪些表示数据的类型? 如何定义函数,或者定义函数的语法如何?. 第二章 基本类型与定义. 本章介绍

arnaud
Download Presentation

如何编辑 Haskell 程序? 如何运行解释器? 如何在解释器下运行一个 Haskell 程序?

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 如何编辑Haskell程序? • 如何运行解释器? • 如何在解释器下运行一个Haskell程序?

  2. 类型和函数 • 一个程序(函数)形如 f :: A -> B 或者 f :: A -> B -> C, A, B, C 表示函数的输入数据集合和输出数据集合 • 例如, Int 表示整数集合 double :: Int -> Int mymax :: Int -> Int -> Int • 问题: 如何表示输入数据和输出数据?Haskell提供了哪些表示数据的类型? • 如何定义函数,或者定义函数的语法如何?

  3. 第二章 基本类型与定义 • 本章介绍 • 基本类型 • 基本函数定义

  4. 类型 一个类型是一些值的集合,这些值均支持相同的运算。 例如,布尔类型Bool • 包含两个逻辑值: True 和 False; • 支持&& (与),||(或),not(非)等运算。

  5. 类型 在Haskell中, 每个合理的表达式都有它所属的类型,写作 e :: t 类型t可以在编译时由系统的类型推导程序( type inference )计算出。 表达式e具有类型t

  6. 例如, double :: Int -> Int, 所以 double 4 :: Int 类型推导规则 一个基本的类型推导规则是: f :: A -> B e :: A f e :: B

  7. 类型安全(Type Safety) 但是 是错误的, 称之为类型错误( type error). double True 所有的类型错误均可以在编译时发现,这使得函数程序是类型安全的: well-typed programs never go wrong (no errors at run time)!. 类型避免了一类错误的发生。

  8. 基本类型 Haskell提供了一些基本类型,用户也可定义自己的类型。 布尔类型: Bool Bool具有两个值: True和False. 它们表示一个测试或者条件是否成立的两个可能的结果。 布尔运算(operators)包括: && (and), | | (or), not, == (equal) 例如, True | | False = True not True = False 可以查看 Prelude.hs中这些函数的定义。

  9. 类型: Bool 运算&& 和| | 称为中缀运算,其类型为 : (&&) :: Bool -> Bool -> Bool (| |) :: Bool -> Bool -> Bool 运算| | 是“可兼或”。我们可以定义“不可兼或”: exOr :: Bool -> Bool -> Bool exOr x y = (x | | y) && not (x && y) 另一种定义exOr的方式: exOr True x = not x exOr False x = x 注意(&&)中的括号不可少

  10. 类型: Integers 1, 2, 3, … :: Int 此类型包括介于–2^31 和2^31 –1间的整数。 其中的运算包括: 3 + 4 = 7 3*7 = 21 2^3 = 8 关系运算: >, <=, ==, <, <=, /= 试着在hugs键入:t (+)和 :t div查看这些运算的类型。 整数除法 div 7 2 = 3 mod 7 2 = 1

  11. 运算符 运算只是“中缀”函数. (+) :: Int -> Int -> Int 我们可以用两种方式使用+: (+) 3 2 = 5 3 + 3 = 5 一个二元函数也可以使用`(backquote)转变为中缀运算: 7 `div` 2 3 7 `mod` 2 1 Infix version of div and mod

  12. 重载(Overloading) 我们使用同一个符号(==)比较整数的相等,布尔值的相等,这也表明(==)具有下列类型: Int -> Int -> Bool Bool -> Bool -> Bool 这种现象称为重载(overloading): 使用同一个符号或者名表示不同类型上的(相似)运算. 更多的例子: (+), (-), (*) 等.

  13. 使用条件的定义 如何定义求两个整数中较大的数, max? max :: Int -> Int -> Int max x y | x >= y = x | otherwise = y 如果条件为True, 则max x y 等于 x 如果条件为 False, 那么 max x y 等于 y

  14. 使用条件(guards) 一般地,使用条件定义的函数具有下列格式: name x1 x2 … xn | g1 = e1 | g2 = e2 … | otherwise = e 函数名 形式参数 结果 条件

  15. 条件 (2) • 条件必须是布尔类型的表达式,即它们的值为True 或者False. • 给定一组参数,Haskell 自上而下计算条件的值,然后将第一个条件之值为True对应的结果作为函数在相应输入下的结果。所以,条件的次序很重要。 • 定义中的条件往往覆盖所有的情况。 习题:定义函数 maxThree ,它返回三个整数中的 最大值.

  16. An example using guards maxThree :: Int -> Int -> Int -> Int maxThree x y z | x >= y && x >= z = x | y >= z = y | otherwise = z Or using a predefined function: maxThree’ x y z = max (max x y) z Exercise: compute maxThree 6 (4+3) 5.

  17. 条件表达式 Haskell 提供下列条件表达式: if condition then m else n 其语义是, 如果条件condition (of type Bool) 的值为True, 则表达式的值为m, 否则为n, 其中m和n必须具有相同的类型. 例如, max x y = if x >= y then x else y Exercise. Define min :: Int -> Int -> Int

  18. 使用定义做计算 • 函数调用的计算过程 • 用函数定义的右式代替函数; • 使用实在参数代替形式参数 • double :: Int -> Int • double x = 2*x • double 3 2 *3 • 6

  19. 计算顺序 一个表达式的计算顺序可能有多种,但是,其计算结果不变. double 5 10 double (2+3) 2*(2+3) The evaluation order does not matter.

  20. 递归(Recursion) 能否用fac (n-1) 计算fac n? 问题: 定义函数fac :: Int -> Int fac n = 1 * 2 * … * n 假如我们已知fac (n-1),如何计算fac n? fac n = 1 * 2 * … * (n-1) * n = fac (n-1) * n

  21. 阶乘表 已知 fac 0 = 1. n fac n 0 1 1 1 2 2 3 6 4 24 ... 所以 fac 1 = 1 * 1. 所以 fac 2 = 1 * 2. 所以 fac 3 = 2 * 3.

  22. Factorial的递归定义 递归基 fac :: Int -> Int fac 0 = 1 fac n | n > 0 = fac (n-1) * n 递归情况

  23. 计算 Factorials fac :: Int -> Int fac 0 = 1 fac n | n > 0 = fac (n-1) * n fac 4 ?? 4 == 0 False ?? 4 > 0 True fac (4-1) * 4 fac 3 * 4 fac 2 * 3 * 4 fac 1 * 2 * 3 * 4 fac 0 * 1 * 2 * 3 * 4 1 * 1 * 2 * 3 * 4 24

  24. 原始递归 • 原始递归定义规则: • 利用f (n-1)定义f n(n > 0). • 直接定义f 0. • 如何确定一个函数是否存在递归定义: • 可否将 f n 的计算转换为类似的 f (n-1)的计算,但是参数更简单; • 能否直接定义最简单的情况 f 0;

  25. Quiz Define a function power so that power x n == x * x * … * x n times (Of course, power x n == x^n, but you should define power without using ^).

  26. Quiz Define a function power so that power x n == x * x * … * x n times Don’t forget the base case! power x 0 = 1 power x n | n > 0 = power x (n-1) * x Since this equals (x * x * … * x) * x n-1 times

  27. 一般递归(General Recursion) 如何利用f x之值计算f n 之值, 其中 x小于n? Example x^(2*n) == (x*x)^n x^(2*n+1) == (x*x)^n * x

  28. 一般递归的幂函数 注意不要漏掉递归基 power :: Int -> Int -> Int power x 0 = 1 power x n | n `mod` 2 == 0 = power (x*x) (n `div` 2) | n `mod` 2 == 1 = power (x*x) (n `div` 2) * x 两种递归的情况 这个定义有何特点呢?

  29. Comparing the Versions First Version power 3 5 power 3 4 * 3 power 3 3 * 3 * 3 power 3 2 * 3 * 3 * 3 power 3 1 * 3 * 3 * 3 * 3 power 3 0 * 3 * 3 * 3 * 3 * 3 1 * 3 * 3 * 3 * 3 * 3 243 Second Version power 3 5 power 9 2 * 3 power 81 1 * 3 power 81 0 * 81 * 3 1 * 81 * 3 243 4 function calls, 4 multiplications. 6 function calls, 5 multiplications.

  30. 使用递归应注意的问题 • 使用递归可以将一个问题转换为类似的之问题,递归是程序设计语言中一种有力的解决问题方法; • 一般的问题比简单的问题更容易解决,因为递归方法可以解决更复杂的问题; • 使用递归时应该注意保证“程序终止”( termination),即定义一个问题规模,在递归基的情况问题规模为0,其他情况下大于0,而且在定义中,每次递归调用的规模均严格减小。

  31. 字符类型: Char Haskell提高了字符类型Char. 字符类型的值是单引号扩起来的单个字符,如 ‘a’, ‘3’ 等. 一些特殊字符: ‘\t’ (tab), ‘\n’ (newline), ‘\\’ (backslash), ‘\’’(single quote), ‘\”’(double quote)

  32. 串类型: Strings 一个串是用双引号括起来的一些字符,如 “Hello World!” :: String 串上的运算: “Hello “ ++ “world!” “Hello World!” show (max 5 (8+4)) “12” 一段文本的类型

  33. 实数类型 1.4, 14.732, 3.14159, -33.5 :: Float 算术运算: +, -, *, /, ** (exponential) 关系运算: >, >=, <, <=, ==, /=

  34. 列表类型 包括在一对方括号之间的一些同类型值或表达式 [1, 2, 3], [5] :: [Int] 运算: [1, 3] ++ [2, 4, 3] [1, 3, 2, 4, 3] head [1,3,5] 1 last [1, 3, 5] 5 注意: String = [Char] 整数列表类型

  35. Lists 一般地,对于任意类型t, [t] 是类型为t的值构成的列表类型。 例如, [False, True, False], [] :: [Bool] [‘H’, ‘e’, ‘l’, ‘l’,’o’,’!’] :: [Char] (or String) [“Hello”, “world”] :: [[Char]] (or [String]) 空列表 列表的列表

  36. 多元组 • (True, False) :: (Bool, Bool) • (“Bob’, 23, ‘M’) :: (String, Int, Char) • 一般地, 对于任意类型 t1, …, tn, (t1, …, tn) 是一个类型,其元素包括所有形如 (a1, …, an) 的值,其中 ai 具有类型ti.

  37. 运算符 运算符名使用下列字符构成的串: ! # $ % & * + . < = > ? \ ^ | : - ~ 不过,冒号’:’不可作为首字符。 例如: (/\) :: Bool -> Bool -> Bool x /\ y = if x then y else x

  38. Associativity and Binding Powers • To change associativity or binding power: infixr 7 (/\) (right associative) or infix 7 (/\) (non-associative)

  39. 命令类型 writeFile :: FilePath -> String -> IO( ) writeFile “myfile” “Hello” :: IO( ) readFile “myfile” :: IO String 命令类型,没有返回结果 将串 “Hello!” 写入文件 myfile. 由文件 myfile 中读入一个串,其返回结果是一个串 返回一个串的命令类型

  40. 函数类型 f :: A -> B e :: A F e :: B double是类型为Int -> Int的函数。 一般地, t1 -> t2 是将类型t1之值映射到类型t2的值的函数的类型。 double :: Int -> Int double x = x + x

  41. Function types • 运算 “->” 是右结合的,即类型 t1 -> t2 -> t3 等于 t1 -> (t2 -> t3) • 但是, (t1 -> t2) -> t3 不可写成 t1 -> t2 -> t3

  42. Curried functions add’ :: (Int, Int) -> Int add’ (x, y) = x + y add :: Int -> Int -> Int add x y = x + y Note: add 2 :: Int -> Int Uncurried version Curried version add 2 是一个函数

  43. 函数的复合 quadruple :: Int -> Int quadruple = double . double quadruple 3 double (double 3) double 6 12 函数的复合是函数上的运算

  44. Is max correct? 程序设计是一个非常容易犯错误的过程 ;第一次设计的程序也很少是正确的。 软件开发很大一部分费用是找错和改错,即软件测试:给软件不同的输入,然后检查输出是否正确。 软件测试是确保软件符合设计要求的主要方法。

  45. 选择测试数据 测试数据尽可能包括各种可能的情形,特别是那些可能出现错误的情形,特殊情形和边界情形等。 函数 max 的测试数据至少应该包含 x<y, x==y, x>y, 以及正负参数组合的情形。 测试数据应该覆盖每一种情况,使每种情况都能运行至少一次。

  46. ”Testing can never demonstrate the absence of errors in software, only their presence” Edsger W. Dijkstra 测试永远不能证明软件中不存在错误,只能证明软件中存在错误。 测试也是找错的最有效方法

  47. 软件规格说明(Specifications) 如何说明 `max 是正确的´? 一个规格说明( specification)陈述max应该满足的性质。 Property: x <= max x y Property: y <= max x y

  48. 为什么陈述 specifications? • 帮助我们理清max 应该具有的功能。 • 有助于测试的进行。 • 可以证明程序的正确性。

  49. 规格说明与测试 我们可以定义检查一个性质是否成立的函数 prop_Max :: Int -> Int -> Bool prop_Max x y = x <= max x y && y <= max x y 如果性质prop_Max 总是返回 True, 那么规格说明是满足的。 我们可以测试许多输入x, y以检查max是否具有“正确性”性质,而无需检查max的结果是什么。

  50. Testing with QuickCheck QuickCheck一个随机测试工具 Main> quickCheck prop_Max OK, passed 100 tests QuickCheck 随机生成100组输入并检查测试的性质是否成立。如果不成立,则返回一个反例。

More Related