haskell趣学-入门
更新日期:
文章目录
什么是Heskell
- 纯函数式语言
- 命令式编程, 变量在执行过后, 会发生变化(状态变化)
- 函数式, 函数唯一做的事是引用计数结果, 不产生副作用(如改变全局变量)
- 应用透明, 若以同样的参数调用同一个函数两次,得到的结果一定是相同. 编译器理解程序的行为, 容易验证函数的正确性, 使简单组合成复杂
- 惰性(lazy), 若以同样的参数调用同一个函数两次, 得到的结果一定是相同值.
- 结合引用透明, 可以把程序仅看作是数据的一系列变形
- 静态类型, 编译器检查错误, 自动类型推导
使用Haskell
编译器 GHC, apt-get install Haskell-platform
1 | ghci> :l myfunction.hs
|
入门
ghci> 2 + 15
17
ghci> 5 / 2
2.5
ghci> False || True
True
ghci> not False
True
ghci> 1 == 0
False
ghci> 5 /= 5
False
ghci> 5+"llama" -- 运算符要求两端都是数值
ghci> 5==True -- 报错, `==`对两个可比较的值可用, 橘子和苹果没法比较
中缀函数: *
, +
, 大多数命令式编程语言中的函数调用形式通常就是函数名,括号,
由逗号分隔的参数表.
在Haskell 中,函数调用的形式是函数名,空格,空格分隔的参数表
1 2 3 4 5 6 7 8 9 10 | succ 8 -- successor, 返回一个数的后继 min 8 9 -- 函数调用拥有最高的优先级 succ 9 + max 5 4 + 1 -- (succ 9) + (max 5 4) + 1 succ 9*10 -- (succ 9)*10 -- 使用中缀是函数清晰 div 92 10 92 `div` 10 |
定义函数
1 2 3 4 5 6 7 | -- 声明一个函数, 功能是一个数字乘2 doubleMe x = x + x doubleUs x y = x*2 + y*2 -- doubleUs x y = doubleMe x + doubleMe y -- 首字母大写的函数是不允许 doubleSmallNumber x = (if x > 100 then x else x*2) + 1 |
List
List 是一种单类型的数据结构, 可以用来存储多个类型相同的元素. 们可以在里面装一组数字或者一组字符, 但不能把字符和数字装在一起.
在 ghci 下,我们可以使用 let
关键字来定义一个常量.
在 ghci 下执行 let a =1
与在脚本中编写 a=1
是等价的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | ghci> let lostNumbers = [4,8,15,16,23,48] ghci> lostNumbers [4,8,15,16,23,48] -- 一个 List 由方括号括起,其中的元素用逗号分隔开来 ghci> [1,2,3,4] ++ [9,10,11,12] [1,2,3,4,9,10,11,12] -- 字串实际上就是一组字符的 List,"Hello" 只是 ['h','e','l','l','o'] -- ++ 有效率问题 ghci> "hello" ++ " " ++ "world" "hello world" ghci> ['w','o'] ++ ['o','t'] "woot" -- 用 : 运算符往一个 List 前端插入元素 -- [1,2,3] 实际上是 1:2:3:[] 的语法糖 ghci> 5:[1,2,3,4,5] [5,1,2,3,4,5] -- 索引 ghci> [9.4,33.2,96.2,11.2,23.25] !! 1 33.2 -- 常用函数 ghci> head [5,4,3,2,1] 5 ghci> tail [5,4,3,2,1] [4,3,2,1] ghci> last [5,4,3,2,1] 1 ghci> init [5,4,3,2,1] [5,4,3,2] ghci> length [5,4,3,2,1] 5 ghci> null [1,2,3] False ghci> null [] True ghci> reverse [5,4,3,2,1] [1,2,3,4,5] ghci> take 3 [5,4,3,2,1] [5,4,3] ghci> take 1 [3,9,3] [3] ghci> drop 3 [8,4,2,1,5,6] [1,5,6] ghci> minimum [8,4,2,1,5,6] 1 ghci> maximum [1,9,2,3,4] 9 ghci> sum [5,2,1,6,3,2,5,7] 31 ghci> product [6,2,1,2] -- 返回元素的积 24 -- 判断一个元素是否在包含于一个 List,通常以中缀函数的形式调用它 ghci> 4 `elem` [3,4,5,6] True ghci> 10 `elem` [3,4,5,6] False |
使用 Range
1 2 3 4 5 6 7 8 9 10 11 12 13 | ghci> [1..20] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] ghci> ['a'..'z'] "abcdefghijklmnopqrstuvwxyz" -- 跨步 ghci> [2,4..20] [2,4,6,8,10,12,14,16,18,20] -- 由于是惰性的, 可以得到无限长度 List ghci> take 24 [13,26..] ghci> take 10 (cycle [1,2,3]) ghci> take 10 (repeat 5) |
List Comprehension
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ghci> [x*2 | x <- [1..10]] [2,4,6,8,10,12,14,16,18,20] ghci> [x*2 | x <- [1..10], x*2 >= 12] [12,14,16,18,20] ghci> boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x] -- 过滤若条件为 false, 则不被包含 ghci> [ x | x <- [10..20], x /= 13, x /= 15, x /= 19] [10,11,12,14,16,17,18,20] ghci> [ x*y | x <-[2,5,10], y <- [8,10,11], x*y > 50] [55,80,100,110] -- 自己定义length -- _ 表示我们并不关心从 List 中取什么值,与其弄个永远不用的变量,不如直接一个 _ ghci> length' xs = sum [1 | _ <- xs] -- 去除所有的奇数 ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]] ghci> [ [ x | x <- xs, even x ] | xs <- xxs] [[2,2,4],[2,4,6,8],[2,4,2,6,2,6]] |
元组(Tuple)
Tuple (元组)很像 List, 都是将多个值存入一个个体的容器. Tuple 则要求你对需要组合的数据的数目非常的明确, 它的类型取决于其中项的数目与其各自的类型.(可以装载不同类型的数据)
[(1,2),(8,11),(4,5)]
相较 [[1,2],[8,11,5],[4,5]]
[(1,2),(8,11,5),(4,5)]
,[(1,2),("one",2)]
都会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | ghci> fst (8,11) 8 ghci> fst ("Wow", False) "Wow" ghci> snd (8,11) 11 ghci> snd ("Wow", False) False -- 这两个函数仅对序对有效, 不能应用于三元组,四元组和五元组之上 ghci> zip [1..] ["apple", "orange", "cherry", "mango"] [(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")] -- 告诉它只要周长为 24 的直角三角形 -- 同时也考虑上 b 边要短于斜边,a 边要短于 b 边情况 ghci> let rightTriangles' = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24] ghci> rightTriangles' [(6,8,10)] |