Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1690146
  • 博文数量: 410
  • 博客积分: 9563
  • 博客等级: 中将
  • 技术积分: 4517
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-03 19:59
个人简介

文章分类

全部博文(410)

文章存档

2017年(6)

2016年(1)

2015年(3)

2014年(4)

2013年(32)

2012年(45)

2011年(179)

2010年(140)

分类: LINUX

2012-04-18 16:35:13


  1. haskell notes


  2. 本文为 Learn You Haskell 笔记, 摘录代码, 记录理解:
  3. 协议了解不多, 总之主干和内容都按原作者:

  4. 代码后跟文字采用 Haskell 注释"--", 文字中代码使用双引号包裹,
  5. 代码中可变的参数, 双引号中使用"#{}", 执行输出结果用"-->"引出
  6. 比如"cd #{filename}"表示进入自己指定目录

  7. 交互式命令行可用 GHC 或者 Hugs, 我用的 GHC, 安装 ghc6,
  8. 在同一个目录建立文件夹内建立文件"#{filename}.hs", 把代码写成脚本,
  9. 用"ghci"命令启动, "Ctrl+d"以及":quit"和":q"退出 ghci
  10. 输入":l #{filename}"载入, 修改文件后, 运行":r"重新载入
  11. ghci 命令冒号":"开头, 输入":?"获取 help, ":{"和":}"开始和结束多行输入
  12. ":! #{command}"执行终端命令, ":set prompt #{string_prompt}"来改提示
  13. "$HOME/.ghci"或者"$HOME/.ghc/ghci.conf"存放配置,
  14. 注意可能要设置配置文件权限只给 owner 可写, 如出错, ghci 中会提示
  15. 更多没弄懂, 包括编译, 上面提及内容文档在这里:
  16. 逻辑运算: && , || , not , True , False , == , /= , 不同类型不能进行比较
  17. backticks, infix, prefix: "div 20 4"也可以写作"20 `div` 4". 也可以用 let `div` a b 定义, 不影响
  18. Haskell 中定义变量定义函数不需要严格的先后顺序来保证调用
  19. if 语句必须有 else 结尾, 测试没有 then 也是 prase error 的, 不含缩进写法如下:
  20. doubleSmallNumber x = if x > 100 then x else x*2 -- 或者把 then 和 else 换一行用缩进
  21. 函数名不能以大写字母开头, 单引号可以在函数中间或者结尾, 表示严格版本
  22. 函数可以没有参数, 输出字符串时需要双引号,
  23. 交互式命令行当中使用 let 来定义变量和函数, 脚本当中不需要

  24. [1, 2] ++ [3,4] -- 连接列表, --> [1, 2, 3, 4]
  25. "hello" ++ " " ++ "world" -- 字符串是列表, 必须使用双引号, --> "hello world"
  26. 1: [2, 3, 4] -- 冒号在列表之前加入元素, 同理字符和字符串; 只能用在开头
  27. 1: 2: 3: [] -- 其实等同于 [1, 2, 3], 可以连写的
  28. [1, 2, 3] !! 1 -- 取出'1'位置的数字, 从 0 开始计数, 因此是 --> 2
  29. [[1, 2], [3, 4]] -- 可以用":{ #{list} }:"输入为多行, "!!"连写可以取出
  30. 数组可按字典序比较大小, > , < , >= , <= , == , /=

  31. "#{function} #{list}"操作的数组的函数:
  32. head 取出第一个; tail head其余; last 最后一个; init last其余
  33. length [1, 2, 3] -- 取长度 --> 3
  34. null [] -- 判断是否空数组 --> True
  35. reverse [1, 2, 3] -- 倒转顺序 --> [3, 2, 1]
  36. take 3 [1, 2, 3, 4] -- 取出前3个生成数组 --> [1, 2, 3]
  37. drop 3 [1, 2, 3, 4] -- 去掉前3个剩下组成数组 --> [4]
  38. maximum , minimum , sum , product , 对应运算
  39. elem 4 [3, 4] -- 判断是否存在 --> True

  40. [1.. 3] --数组自动生成, 同理 ['1'.. '9'] , ['a'.. 'z'] , ['A'.. 'z']
  41. [6, 4.. 1] -- 按照规律生成, 不支持幂次的规律, 可以倒置 --> [6, 4, 2]
  42. take 3 [1, 3..] -- 支持无穷数列, 常用 take 取出 --> [1, 3, 5]
  43. take 10 (cycle [1,2,3]) -- cycle 函数,也支持字符串 --> [1,2,3,1,2,3,1,2,3,1]
  44. take 10 (repeat 5) -- repeat 也是无穷, 同理字符串 --> [5,5,5,5,5,5,5,5,5,5]
  45. replicate 3 10 -- -> [10,10,10]
  46. [x * 2 | x <- [1.. 3]] -- 竖线左边是取出的结果, 右边限制条件 --> [2, 4, 6]
  47. [x * 2 | x <- [1.. 10], x*2 >= 12] -- 多个限制条件用逗号 --> [12, 14, 16, 18, 20]
  48. [x | x <- [1..10], odd x] -- 条件 odd x 结果为 True 时输出 --> [1, 3, 5, 7, 9]
  49. [ x*y | x <- [2,5,10], y <- [8,10,11]] -- 注意结合顺序, 列表类似 --> [16,20,22,40,50,55,80,100,110]
  50. length' xs = sum [1 | _ <- xs] -- 注意 "_" 表示任意物件
  51. [ [ x | x <- xs, even x ] | xs <- xxs] -- 过滤二维数组的语法, 其中 xxs 未给出
  52. [(1, 2), (2, 3)] -- 用列表内嵌元组限制列表内的元素格式相同, 否则报错
  53. fst , snd 函数, 仅仅支持 pair, 但元组可以有不同数据类型
  54. zip [1,2,3,4,5] [5,5,5,5,5] -- 按照数量少的个数 --> [(1,5),(2,5),(3,5),(4,5),(5,5)]
  55. [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24] -- 强大 --> [(6, 8, 10)]
  56. [a + b | (a, b) <- xs] -- 对于元组实用 List Comprehension
  57. static type system 编译前差错, type inference 不用每次声明
  58. :t 'a' -> 'a' :: Char -- ":t #{char}"用来察看类型, 这是字符串
  59. :t "a" -> "a" :: [Char] -- 字符串和字符串数组的区别
  60. removeNonUppercase :: [Char] -> [Char] -- 定义类型
  61. removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']] -- 定义函数, 可以自动推断
  62. addThree :: Int -> Int -> Int -> Int -- 经常就,, 最后一个 Int 是返回值
  63. addThree x y z = x + y + z -- 提到如果不懂, 先写函数, 用":t"查看

  64. Int -2147483648 ~ 2147483647
  65. Interger not bounded 无界的
  66. Float is a real floating point with single precision,
  67. Double is a real floating point with double the precision!
  68. Bool is a boolean type. It can have only two values: True and False
  69. Char represents a character. It's denoted by single quotes. A list of characters is a string
  70. ( ) 是类型, 元组和列表的类型只能看了
  71. fst :: (a, b) -> a -- a 和 b 称为: type variable, 函数称为 polymorphic functions
  72. (+) 是 infix function ,可以通过"(+) 1 2"调用"--> 3"
  73. (==) :: (Eq a) => a -> a -> Bool -- "=>" 表示: "class constraint"
  74. 意思是, 参数满足类型约束, a 属于 Eq 这个类, 只有 Eq 这个类里数相互可以比较
  75. All standard Haskell types except for IO and functions are a part of the Eq typeclass
  76. Eq is used for types that support equality testing. == and /=
  77. Ord is for types that have an ordering. >, <, >= and <=
  78. All the types we covered so far except for functions are part of Ord
  79. compare :: (Ord a) => a -> a -> Ordering , Ordering is a type that can be GT, LT or EQ
  80. show :: (Show a) => a -> String , Members of Show can be presented as strings
  81. All types covered so far except for functions are a part of Show
  82. read "[1,2,3,4]" ++ [3] -- Read is sort of the opposite typeclass of Show. --> [1, 2, 3, 4]
  83. read :: (Read a) => String -> a , read 要求是字符串, 单引号 Char 不行
  84. read "[1,2,3,4]" :: [Int] -- read 用法 --> [1, 2, 3, 4]
  85. Enum members are sequentially ordered types — they can be enumerated
  86. ['a'.. 'z'] -- 按照说明给出的都是该类表达式生成的, 可是":t"查看却不像前面几个类型的显示
  87. minBound :: (Bounded a) => a -- 看不懂这句: In a sense they are polymorphic constants
  88. minBound :: Int -- 看去是最大最小边界两个函数, 元组中类似, 列表不接受 --> -2147483648
  89. 20 :: (Num t) => t , 这个地方还是看不懂的
  90. Num is a numeric typeclass. Its members have the property of being able to act like numbers
  91. 20 :: Float 20.0 -- It appears that whole numbers are also polymorphic constants
  92. (*) :: (Num a) => a -> a -> a , (*) accepts all numbers, 与数值相似, (疑问)
  93. Integral is also a numeric typeclass. In this typeclass are Int and Integer
  94. Floating includes only floating point numbers, so Float and Double
  95. fromIntegral (length [1,2,3,4]) + 3.2 -- 还有这个(怀疑): (Num b) => length :: [a] -> b
  96. Haskell 定义函数可以用 let , 指定类型只能在文件中进行, 至少 let 用上去报错
  97. 注意定义 7 和定义 x 不可以调换顺序, 会发生覆盖, 似乎下往上读的, 匹配又是上往下的,
  98. lucky :: (Integral a) => a -> String
  99. lucky 7 = "LUCKY NUMBER SEVEN!"
  100. lucky x = "Sorry, you're out of luck, pal!"
  101. 函数调用时会去匹配写好的模式, 注意尽可能将需要的模式涵盖以免出错
  102. factorial :: (Integral a) => a -> a factorial 0 = 1
  103. factorial n = n * factorial (n - 1)
  104. 注意用法, 操作元组可以直接用变量, 也可以像下面这样:
  105. addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)
  106. addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
  107. 可以自己定义超过二维的数组取头部, 注意下划线的用法, 忽略类型的区别:
  108. first :: (a, b, c) -> a
  109. first (x, _, _) = x
  110. 因为 [1, 2, 3] 实际上是 1:2:3:[] 的语法糖, 有下面写法, 另外注意 error 用法
  111. head' :: [a] -> a
  112. head' [] = error "Can't call head on an empty list, dummy!"
  113. head' (x:_) = x
  114. 下面的用法更有趣, 不过类型 Show 很让我费解, 函数以外都属于 Show
  115. tell :: (Show a) => [a] -> String
  116. tell [] = "The list is empty"
  117. tell (x:[]) = "The list has one element: " ++ show x
  118. tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y
  119. tell (x:y:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y
  120. 于是还有了用递归方式求数组长度的, 个人没有感想
  121. length' :: (Num b) => [a] -> b
  122. length' [] = 0
  123. length' (_:xs) = 1 + length' xs
  124. 形如 xs@(x:y:ys) 来限定 xs 的类型, 用法
  125. capital :: String -> String
  126. capital "" = "Empty string, whoops!"
  127. capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]
  128. 符号 ++ 用在连接列表, 不能用在 Patten Match 当中, 不合理
  129. 重载运算符时先确定结合性(infixl|infixr|infix), 然后优先级:
  130. infixr 3 &&
  131. (&&) :: Bool -> Bool -> Bool
  132. False && x = False
  133. True && x = x

  134. 称为 Guard 的选择语句, 相当 switch/case, 可以缩进或不缩进, 可以嵌套
  135. max' :: (Ord a) => a -> a -> a max' a b
  136. | a > b = a
  137. | otherwise = b
  138. case 意思大概那样, 实际上和 where 和其他的用法可对转, 具体看教程
  139. head' :: [a] -> a
  140. head' xs = case xs of [] -> error "No head for empty lists!"
  141. (x:_) -> x
  142. where 关键字用来简化函数当中某个频繁语句的书写, 可以缩进或者不缩进,
  143. f x = x * y where y = y * 4 -- 还可以用 where (a, b) = (1, 3) 的方式简化表达式
  144. where 绑定的内容是私有的, 仅函数内部可见, 不能在函数间共用
  145. 教程提到了 global 的用法, 实际上等同于定义函数作为关键字
  146. 下面例子有迷惑性, 实际是直接完成了 pattern matching, 疯狂
  147. initials :: String -> String -> String
  148. initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
  149. where (f:_) = firstname
  150. (l:_) = lastname
  151. 细看 where 还被用来定义函数
  152. calcBmis :: (RealFloat a) => [(a, a)] -> [a]
  153. calcBmis xs = [bmi w h | (w, h) <- xs]
  154. where bmi weight height = weight / height ^ 2
  155. let 对比 where, 不局限于函数; 但不能跨越 guard 使用, 非常 local,
  156. cylinder :: (RealFloat a) => a -> a -> a
  157. cylinder r h =
  158. let sideArea = 2 * pi * r * h
  159. topArea = pi * r ^2
  160. in sideArea + 2 * topArea
  161. where 属于语法构造, 而 let 是独立的表达式, 可以用在各种位置,
  162. 4 * (let a = 9 in a + 1) + 2 --> 42
  163. [let square x = x * x in (square 5, square 3, square 2)] --> [(25, 9, 4)]
  164. (let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar)
  165. --> (6000000,"Hey there!") 这一句当中分号不可以省略, 最后一个绑定可以省略
  166. (let (a,b,c) = (1,2,3) in a+b+c) * 100 --> 600
  167. let 还可以用在 list comprehension 里边, 这里没有看到 in 关键词了
  168. calcBmis :: (RealFloat a) => [(a, a)] -> [a]
  169. calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi >= 25.0]
  170. let 可以用在定义函数当中, 这也就是 ghci 里的常用
  171. let boot x y z = x * y + z in boot 3 4 2
  172. 就因为 let 过于 local, 所以不能代替 where 用在定义函数

  173. Haskell 没有 for 和 while, 思维上用递归 recursion 理解, 天旋地转
  174. maximum' :: (Ord a) => [a] -> a
  175. maximum' [] = error "maximum of empty list"
  176. maximum' [x] = x
  177. maximum' (x:xs) = max x (maximum' xs)
  178. 例子比如, 递归生成 n 个 x 的列表:
  179. replicate' :: (Num i, Ord i) => i -> a -> [a]
  180. replicate' n x
  181. | n <= 0 = []
  182. | otherwise = x:replicate' (n-1) x
  183. 用了 Num 和 Ord 两者的原因作者写到 Num 不是 Ord 的子集
  184. take 作为例子:
  185. take' :: (Num i, Ord i) => i -> [a] -> [a]
  186. take' n _
  187. | n <= 0 = []
  188. take' _ [] = []
  189. take' n (x:xs) = x : take' (n-1) xs
  190. reverse 作为例子:
  191. reverse' :: [a] -> [a]
  192. reverse' [] = []
  193. reverse' (x:xs) = reverse' xs ++ [x]
  194. repeat 作为例子:
  195. repeat' :: a -> [a]
  196. repeat' x = x:repeat' x
  197. zip 作为例子:
  198. zip' :: [a] -> [b] -> [(a,b)]
  199. zip' _ [] = []
  200. zip' [] _ = []
  201. zip' (x:xs) (y:ys) = (x,y):zip' xs ys
  202. elem 作为例子:
  203. elem' :: (Eq a) => a -> [a] -> Bool
  204. elem' a [] = False
  205. elem' a (x:xs)
  206. | a == x = True
  207. | otherwise = a `elem'` xs
  208. quicksort 被很多人用来展示 Haskell 的优雅,
  209. 运行这段代码发现 ghc 有必要将 smallerSorted 进行对应缩进, 而不是连写在 let 后面:
  210. quicksort :: (Ord a) => [a] -> [a]
  211. quicksort [] = []
  212. quicksort (x:xs) =
  213. let
  214. smallerSorted = quicksort [a | a <- xs, a <= x]
  215. biggerSorted = quicksort [a | a <- xs, a > x]
  216. in smallerSorted ++ [x] ++ biggerSorted
  217. 递归的方式往往是设定规则和边缘, 对于列表往往上头尾部和空列表
  218. 再来看无穷数列, 注意定义函数时将函数本身用于递归,
  219. iterate 作为例子:
  220. iterate' :: (a -> a) -> a -> a
  221. iterate' f x = x: iterate' f (f x)
  222. 几个重要的例子, 体会下怎样处理递归的:
  223. isSquare n = elem n (takeWhile (<=n) squares) where squares = [x^2| x <- [0..]]
  224. fibs = fibgen 1 1 where fibgen n1 n2 = n1 : fibgen n2 (n1+n2)
  225. prime = sieve [2..] where sieve (x:xs) = x: sieve (filter (\y -> rem y x /=0) xs)

  226. 高阶函数 higher order function 是 Haskell 体验中不可少的一部分,
  227. 多参数函数相当于函数接受参数返回另一函数来接收下一个参数, 称为 curried functions
  228. Haskell B. Curry 的名字被使用了, curried 函数简单说是参数分步代入
  229. max 4 5 --> 5 -- 和 (max 4) 5 是一致的, 因为 (max 4) 结束返回了一个函数 (->)
  230. max :: (Ord a) => a -> a -> a -- 可理解为每接受一个参数 a 返回一个函数 (->), 再接受参数,
  231. max :: (Ord a) => a -> (a -> a) -- 这个写法意思一样, 可我不明白那里区别了.
  232. 还提到可以将只部分执行的函数作为参数传递的用法, 没有详解
  233. multThree :: (Num a) => a -> (a -> (a -> a)) -- 像把后面括号部分作为前面已执行部分的参数(?)
  234. let m x y z = x*y*z -- 来看具体的效果
  235. :t m --> m :: Num a => a -> a -> a -> a
  236. :t (m 9) --> (m 9) :: Num a => a -> a -> a -- 表明返回了函数, 注意参数个数
  237. let m'9 = m 9 -- 将一个部分执行的函数结果传递给 m'9
  238. :t m'9 --> m'9 :: Integer -> Integer -> Integer -- 类型改变倒是在意料之外, 对浮点数报错了.
  239. (/10) 200 --> 20.0 -- 因此还有 let divide'10 = (/10) 的写法, 将其写作函数
  240. 具体写法如下, 这个类型, 函数接受一个参数返回浮点数, 根据实际来了.
  241. divideByTen :: (Floating a) => a -> a
  242. divideByTen = (/10)
  243. 看来, 几乎所有, 中置表达式都可以括号加简写,,
  244. isUpperAlphanum :: Char -> Bool
  245. isUpperAlphanum = (`elem` ['A'..'Z'])
  246. 作者说减号'-'例外, 因为负数, 因而只能用 subtrsct
  247. (subtract 4) 3 --> -1 -- 注意参数顺序, (4) (3) (-1)
  248. 部分执行的函数如 (subtract 4) 直接在 ghci 执行会报错, --> No instance for (Show (a0 -> a0)) ..
  249. 因为返回的 (->) 不是类型 Show 的某个实例, 看来 ghci 的输出都这么搞的

  250. 可以用函数作为参数, 注意类型定义的括号是必须的:
  251. applyTwice :: (a -> a) -> a -> a
  252. applyTwice f x = f (f x)
  253. applyTwice (+3) 10 --> 16
  254. applyTwice ("HAHA " ++) "HEY" --> "HAHA HAHA HEY"
  255. applyTwice (++ " HAHA") "HEY" --> "HEY HAHA HAHA"
  256. applyTwice (3:) [1] --> [3,3,1]
  257. zipWith 是高阶函数编程中一个重要函数, 接受一个函数/ 两个变量作为参数,
  258. a, b, c 未必要相同的类型, 不确定时先写出内容再 :t 看类型,
  259. 边缘的情况, 数列长度不同时用 _ 表示:
  260. zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
  261. zipWith' _ [] _ = []
  262. zipWith' _ _ [] = []
  263. zipWith' f [x:xs] [y:ys] = f x y : zipWith' f xs ys
  264. 准确基本的告诫函数可以广泛使用, zipWith' 函数:
  265. zipWith' (+) [4, 2, 5, 6] [2, 6, 2, 3] --> [6,8,7,9]
  266. zipWith' max [6, 3, 2, 1] [7, 3, 1, 5] --> [7,3,2,5]
  267. zipWith' (++) ["foo ", "bar ", "baz "] ["fighters", "hoppers", "aldrin"]
  268. --> ["foo fighters","bar hoppers","baz aldrin"]
  269. zipWith' (*) (replicate 5 2) [1..] --> [2,4,6,8,10]
  270. zipWith' (zipWith' (*)) [[1,2,3],[3,5,6],[2,3,4]] [[3,2,2],[3,4,5],[5,4,3]]
  271. --> [[3,4,6],[9,20,30],[10,12,12]]
  272. 对比命令式编程那种循环加检测来判断是否符合条件的方式,
  273. 函数式编程用抽象的方式探测和过滤是否满足条件并完成计算
  274. 另一个 flip 函数, 接受一个缺两参数的函数将两参数顺序调换,
  275. 这里 x, y 是 f, g 隐含的参数, 看去有些突兀:
  276. flip' :: (a -> b -> c) -> (b -> a -> c)
  277. flip' f = g
  278. where g x y = f y x
  279. 而类型定义中后一个括号可有可无, 不影响接收参数,
  280. 另外直接明写也是可以的:
  281. flip' :: (a -> b -> c) -> b -> a ->c
  282. flip' f x y = f y x
  283. 看下用例, (flip' div) 作为参数传给 zipWith:
  284. flip' zip [1..5] "hello" -> [('h',1),('e',2),('l',3),('l',4),('o',5)]
  285. zipWith (flip' div) [2, 2..] [10, 8.. 2] --> [5, 4, 3, 2, 1]

  286. map 函数接收一个函数和一个列表作为参数,
  287. 返回列表中元素逐个用函数处理的值的列表:
  288. map' :: (a -> b) -> [a] -> [b]
  289. map' _ [] = []
  290. map' f (x:xs) => f x : map' f xs
  291. map 函数属于高阶函数少有的广泛使用, 看例子:
  292. map (+3) [1, 5, 3, 1, 6] --> [4,8,6,4,9]
  293. map (++ "!") ["BIFF", "BANG", "POW"] --> ["BIFF!","BANG!","POW!"]
  294. map (replicate 3) [3..6] --> [[3,3,3],[4,4,4],[5,5,5],[6,6,6]]
  295. map (map (^2)) [[1,2],[3,4,5,6],[7,8]] --> [[1,4],[9,16,25,36],[49,64]]
  296. map fst [(1,2),(3,5),(6,3),(2,6),(2,5)] --> [1,3,6,2,2]
  297. map 函数的功能用列表解析模拟, 比如:
  298. [x+3 | x <- [1,5,3,1,6]] --> [4,8,6,4,9]
  299. map 的写法相较更清晰, 特别嵌套使用不会因为括号而糊涂
  300. filter函数接收一个判断函数 p 加一个列表,
  301. 返回经函数 p 判断为真的值所组成的列表:
  302. filter' :: (a -> Bool) -> [a] -> [a]
  303. filter' _ [] = []
  304. filter' p (x:xs)
  305. | p x = x : filter' p xs
  306. | otherwise = filter' p xs
  307. 当 p x 返回 True 时, 和非时分开两个结果:
  308. filter (>3) [1,5,3,2,1,6,4,3,2,1] --> [5,6,4]
  309. filter (==3) [1,2,3,4,5] --> [3]
  310. filter even [1..10] --> [2,4,6,8,10]
  311. let notNull x = not (null x) in filter notNull [[1,2,3],[],[3,4,5],[2,2],[],[],[]]
  312. --> [[1,2,3],[3,4,5],[2,2]]
  313. filter (`elem` ['a'..'z']) "u LaUgH aT mE BeCaUsE I aM diFfeRent"
  314. --> "uagameasadifeent"
  315. filter (`elem` ['A'..'Z']) "i lauGh At You BecAuse u r aLL the Same"
  316. --> "GAYBALLS"
  317. filter 和列表解析选取还是考虑可读性, 因为功能相近,
  318. 列表解析中可以用 && 来模拟多层的筛选, 或者多层列表解析
  319. quicksort 算法因此稍微简化一些来写:
  320. quicksort :: (Ord a) => [a] -> [a]
  321. quicksort [] = []
  322. quicksort (x:xs) =
  323. let
  324. smallerSorted = quicksort (filter (<=x) xs)
  325. biggerSorted = quicksort (filter (>x) xs)
  326. in smallerSorted ++ [x] ++ biggerSorted
  327. 列表解析和高阶函数有时轻松处理命令式编程中大量循环的判断,
  328. 而且由于惰性计算, 多余的 filter 和 map 也能避免重复执行(?)
  329. largestDivisible :: (Integral a) => a
  330. largestDivisible = head (filter p [100000, 99999..])
  331. where p x = x `mod` 3829 == 0
  332. 例子中当取出第一个满足的数时不再计算, 得益于惰性计算

  333. takeWhile 函数接收一个判断和一个列表作为参数,
  334. 顺序判断每个元素, 将返回错误前的元素组成列表返回:
  335. takeWhile' :: (a -> Bool) -> [a] -> [a]
  336. takeWhile' p (x:xs)
  337. | p x = x : takeWhile' p xs
  338. | otherwise = []
  339. takeWhile 与 filter 相仿, 但前者在第一次产生 false 时即终止的
  340. sum (takeWhile (<10000) (filter odd (map (^2) [1..]))) --> 166650
  341. sum (takeWhile (<10000) [n^2 | n <- [1..], odd (n^2)]) --> 166650
  342. 列表解析的方式也可以写出该函数, 然而完全用列表解析会成为无限
  343. 然后来考虑角谷猜想, 即: 取一自然数(不含零)判断(mod x 2),
  344. 真则取 x/2, 否则取(3*x+1), 继续对所取得数进行此步骤, 直到取出 1
  345. 记录此过程步骤, 问[1..100]有几个数步长(>15)?
  346. chain :: (Integral a) => a -> [a]
  347. chain n
  348. | even n = n : chain (n `div` 2)
  349. | odd n = n : chain (n*3 + 1)
  350. 注意了不能用(/2)代替上面的除以 2, 似乎是浮点数问题,
  351. 另外 1 在程序中特别处理, 看例子:
  352. chain 10 --> [10,5,16,8,4,2,1]
  353. chain 1 --> [1]
  354. 然后用 numLongChains 来返回结果, 用 isLong 来判断长短:
  355. numLongChains :: Int
  356. numLongChains = length (filter isLong (map chain [1..100]))
  357. where isLong xs = length xs > 15
  358. 使用 Int 的原因是 length 返回值是 Int 类型的, 具体看原文
  359. 另外还能使用(map (*) [1..]), 返回元素函数的列表,
  360. [(0*),(1*),(2*),(3*),(4*),(5*)..
  361. 然而这不属于 Show 于是不能打印. 用以下方式探测:
  362. let listOfFuns = map (*) [0..]
  363. (listOfFuns !! 4) 5 --> 20
  364. Lambdas 基本用来写一次性匿名函数代入高阶函数当中,
  365. 书写时先用'\'再写参数再写"->"再写函数体最后包围以括号,
  366. 在上面的例子中直接用 Lambdas 代替 filter 的判断:
  367. numLongChains :: Int
  368. numLongChains = length (filter (\xs -> length xs > 15) (map chain [1..100]))
  369. 像(+3)与(\x -> x+3)等价, 前者的简洁, 后者没有必要
  370. Lambdas 就像一般函数, 可以带有任意多个参数:
  371. zipWith (\a b -> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5]
  372. --> [153.0,61.5,31.0,15.75,6.6]
  373. Lambdas 中的模式匹配不能匹配两种模式, 比如[]和[x:xs], 慎用
  374. map (\(a,b) -> a + b) [(1,2),(3,5),(6,3),(2,6),(2,5)]
  375. --> [3,8,9,8,7]
  376. 不用括号的情况, 因函数本身被 curried, 有如下后三行等价:
  377. addThree :: a -> a -> a -> a
  378. addThree x y z = x+y+z
  379. addThree = \x -> \y -> \z -> x+y+z
  380. addThree = \x y z -> x+y+z
  381. 借助 Lambdas 函数 flip 还可以这样写:
  382. flip' :: (a -> b -> c) -> b -> a -> c
  383. flip' f = \x y -> f y x

  384. 前文中(x:xs)广泛使用, 因而制造 fold 一类函数来做此类事情,
  385. 一个 fold 函数接收一个二元函数一个初值和一个列表为参数,
  386. 逐个抽取列表元素与初值代入函数中运算, 返回值代入初值直到结束,
  387. 比如 foldl 从左边开始取列表的元素,
  388. foldl' (a -> b -> a) -> a -> [b] -> a
  389. foldl' _ x [] = x
  390. foldl' f y (x:xs) = f y (foldl' x xs)
  391. 再来写 sum 函数累加列表各元素:
  392. sum' :: (Num a) => [a] -> a
  393. sum' xs = foldl' (\acc x -> acc + x) 0 xs
  394. 考虑到函数是 curried, 并且(+)可以简化, 简写:
  395. sum' :: (Num a) => [a] -> a
  396. sum' = foldl (+) 0
  397. 一般出于 curried 特性, (foo a = bar b a)简写(foo = bar b)
  398. 用 foldl 再现 elem 函数, 遍历一次列表:
  399. elem' :: (Eq a) => a -> [a] -> Bool
  400. elem' y ys = foldl (\acc x -> if x==y then True else acc) False ys
  401. foldr 顾名思义是从列表右侧开始遍历, 同时,
  402. Lambdas 中参数顺序需要改为(\x acc), 与 foldl 相反
  403. 因为没有现成的(x:xs)模式匹配, 我用了 init/last 来:
  404. foldr' :: (a -> b -> b) -> b -> [a] -> b
  405. foldr' _ x [] = x
  406. foldr' f x xs = f x (foldr' f (last xs) (init xs))
  407. 然后用来实现 map 函数:
  408. map' :: (a -> b) -> [a] -> [b]
  409. map' f xs = foldr (\x acc -> f x : acc) [] xs
  410. 上面的 map 也能用 foldl 实现, 因为(++)非常灵活:
  411. map' f xs = foldl (\acc x -> acc ++ [f x]) [] xs
  412. foldl 和 foldr 区别在于无穷列表处理上, 未给例子
  413. foldl1 与 foldr1 不用给初值, 因而对 [] 会出错
  414. sum' = foldl1 (+)
  415. maximum' :: (Ord a) => [a] -> a
  416. maximum' = foldl1 (\x y -> if x>y then x else y)
  417. reverse' :: [a] -> [a]
  418. reverse' = foldl (\acc x -> x: acc) []
  419. product' :: (Num a) => [a] -> a
  420. product' = foldl1 (*)
  421. filter' :: (a -> Bool) -> [a] -> [a]
  422. filter' p = foldr (\x acc -> if p x then x:acc else acc) []
  423. head' :: [a] -> a
  424. head' = foldl (\x _ -> x)
  425. last' :: [a] -> a
  426. last' = foldl (\_ y -> y)
  427. 按最后意思, fold 将结果作为参数放置后, 这需要注意
  428. 其中 reverse 还可以写成 reverse' = foldl (flip (:)) []
  429. 然后引入 scanl/ scanr 相近 fold 但打印每一步结果成数列:
  430. scanl' :: (a -> b -> a) -> a -> [b] -> [a]
  431. let scanl' f x xs = reverse $ foldl (\acc y -> f x y : acc) [x] xs
  432. scanl (+) 0 [3,5,2,1] --> [0,3,8,10,11]
  433. scanr (+) 0 [3,5,2,1] --> [11,8,3,1,0]
  434. scanl1 (\acc x -> if x > acc then x else acc) [3,4,5,3,7,9,2,1]
  435. --> [3,4,5,5,7,9,9,9]
  436. scanl (flip (:)) [] [3,2,1] --> [[],[3],[2,3],[1,2,3]]
  437. scan 常用在监测那写 fold 方式开展的运算过程,
  438. 题目: 多少个自然数平方根和刚好(<1000)?
  439. sqrtSums :: Int
  440. sqrtSums = length (takeWhile (<1000) (scanl1 (+) (map sqrt [1..]))) + 1 --> 131
  441. 这里不能用 filter 因其不能处理无穷列表, 而用 takeWhile
  442. 运算过程转化为列表在 Haskell 更容易处理

  443. '$'称为"function application", 也直接是个函数:
  444. ($) :: (a -> b) -> a -> b
  445. f $ x = f x
  446. 其优先级最低, 其他操作符左联, ($)是右联的, 下面两两等效:
  447. sum (map sqrt [1..100])
  448. sum $ map sqrt [1..100]
  449. sqrt (3+4+9)
  450. sqrt $ 3+4+9
  451. sum (filter (> 10) (map (*2) [2..10]))
  452. sum $ filter (> 10) $ map (*2) [2..10]
  453. 注意下面用法, 比如"($ 3) (4 +) --> 7":
  454. map ($ 3) [(4+), (10*), (^2), sqrt]
  455. --> [7.0,30.0,9.0,1.7320508075688772]
  456. 复合函数: f (g x) , 其定义为;
  457. (.) :: (b -> c) -> (a -> b) -> a -> c
  458. f . g =\x -> f (g x)
  459. 注意前一个函数接受的与后一个返回的类型应当一致,
  460. Lambdas 功能强大, 但很多时候用符合函数更为明了:
  461. map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]
  462. map (negate . abs) [5,-3,-6,7,-3,2,-19,24]
  463. --> [-5,-3,-6,-7,-3,-2,-19,-24]
  464. map (\xs -> negate (sum (tail xs))) [[1..5],[3..6],[1..7]]
  465. map (negate . sum . tail) [[1..5],[3..6],[1..7]]
  466. --> [-14,-15,-27]
  467. 连续嵌套的带多个参数的表达式将末尾一个用($)隔开:
  468. sum (replicate 5 (max 6.7 8.9))
  469. (sum . replicate 5 . max 6.7) 8.9
  470. sum . replicate 5 . max 6.7 $ 8.9
  471. replicate 100 (product (map (*3) (zipWith max [1,2,3,4,5] [4,5,6,7,8])))
  472. replicate 100 . product . map (*3) . zipWith max [1,2,3,4,5] $ [4,5,6,7,8]
  473. replicate 100 $ product $ map (*3) $ zipWith max [1,2,3,4,5] [4,5,6,7,8]
  474. 但其实($)隔开也是挺相似的, 上三句等价
  475. sum' :: (Num a) => [a] -> a
  476. sum' xs = foldl (+) 0 xs
  477. 不明白为甚这叫无点样式, 直接看以下成对对比的简写:
  478. fn x = ceiling (negate (tan (cos (max 50 x))))
  479. fn = ceiling . negate . tan . cos . max 50
  480. oddSquareSum :: Integer
  481. oddSquareSum = sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
  482. oddSquareSum :: Integer
  483. oddSquareSum = sum . takeWhile (<10000) . filter odd . map (^2) $ [1..]
  484. 作者为了可读性更好建议最后一条用 let/ in 写, 比较特别:
  485. oddSqureSum :: Int
  486. oddSqureSum =
  487. let
  488. oddSquares = filter odd $ map (^2) [1..]
  489. belowLimit = takeWhile (<1000) oddSquares
  490. in sum belowLimit

  491. module 模块是相关函数/ 类型/ 类型类的组合,
  492. 程序一般就是主程序加载模块, 从中获取函数来处理事务,
  493. 模块可以重用, 自足的模块可以被别的意图的程序使用,
  494. Haskell 若干个不同功能的模块组成,
  495. 前面涉及属于默认自动装载的 Prelude (前奏?)模块
  496. 载入模块的语法为: import #{模块名} , 在使用函数之前,
  497. 下面载入 Data.List 模块处理列表, 找出不重复的元素个数,
  498. nub 是 Data.List 模块中除去重复元素返回列表的函数:
  499. import Data.List
  500. numUniqeus :: (Eq a) => [a] -> Int
  501. numUniqeus = length . nub
  502. 执行 import 之后, 所有 Data.List 模块的函数可以在全局命名空间使用,
  503. (length . nub)符合函数等价于(\xs -> length $ nub xs)
  504. GHCI 当中可用 :m + Data.List 来载入模块, 同时载入多个比如:
  505. :m + Data.List Data.Map Data.Set
  506. 对应大致有个 :m - 来释放模块, 在脚本中载入模块亦可
  507. 只想载入某些函数, 比如 nub 和 sort, 如下(Haskell 语法怎么会有逗号?):
  508. import Data.List (nub, sort)
  509. 若自定义了 nub, 不想从模块加载, 用:
  510. import Data.List hiding (nub)
  511. 像 Data.Map 里 filter, null 函数与 Prelude 冲突的话,
  512. 冲突时使用, 当尝试指定载入 import Data.list (filter)
  513. 会报错询问选 Predule.filter 还 Data.List.filter,
  514. 下面语句照常用 filter, 而载入另一个到 Data.List.filter:
  515. import qualified Data.Map
  516. 因为 Data.List.filter 太长, 想简化 M.filter:
  517. import qualified Data.List as M
  518. 下面链接查阅标准库中有哪些模块, 看去真复杂:
  519. 也可以去 Hoogle 搜索函数名, 模块名, 类型声明:

  520. 来看 Data.List , 因 Prelude 是从这里取的, filter 等一般不冲突,
  521. intersperse 接收一个字符一个列表, 用字符散开列表:
  522. intersperse '.' "MONKEY" --> "M.O.N.K.E.Y"
  523. intersperse 0 [1,2,3,4,5,6] --> [1,0,2,0,3,0,4,0,5,0,6]
  524. intercalate 接受一字符串和一字符串列表, 以前者间隔后者返回字符串:
  525. intercalate " " ["hey","there","guys"] --> "hey there guys"
  526. intercalate [0,0,0] [[1,2,3],[4,5,6],[7,8,9]]
  527. --> [1,2,3,0,0,0,4,5,6,0,0,0,7,8,9]
  528. transpose 将二维列表看作矩阵, 行列互换后返回:
  529. transpose [[1,2,3],[4,5,6],[7,8,9]] --> [[1,4,7],[2,5,8],[3,6,9]]
  530. transpose ["hey","there","guys"] --> ["htg","ehu","yey","rs","e"]
  531. 当下列多项式相加, 将各多项式系数相加在一起:
  532. (3x^2 + 5^x + 9)+(10x^3 + 9)+(8x^3 + 5x^2 + x - 1)
  533. map sum $ transpose [[0,3,5,9],[10,0,0,9],[8,5,1,-1]]
  534. 模块带了 foldl' 和 foldl1' 两个更严格的函数,
  535. 两者用于处理大型列表时容易犯错, 详见原文
  536. concat 将列表的列表扁平化为列表:
  537. concat ["foo","bar","car"] --> "foobarcar"
  538. concat [[3,4,5],[2,3,4],[2,1,1]] --> [3,4,5,2,3,4,2,1,1]
  539. 只能处理一层列表, 多层需要使用多次
  540. concatMap 相当于(concat . map), 先执行 map:
  541. concatMap (replicate 4) [1..3] --> [1,1,1,1,2,2,2,2,3,3,3,3]
  542. and 接收布尔值的列表作为参数, 全为 True 时返回 True:
  543. and $ map (>4) [5,6,7,8] --> True
  544. and $ map (==4) [4,4,4,3,4] --> False
  545. or 接收布尔值列表作为参数, 存在 True 时返回 True:
  546. or $ map (==4) [2,3,4,5,6,1] --> True
  547. or $ map (>4) [1,2,3] --> False
  548. any 接收一判断加一列表, 当列表存在判断真则返回 True:
  549. any (==4) [2,3,5,6,1,4] --> True
  550. all (>4) [6,9,10] --> True
  551. all (`elem` ['A'..'Z']) "HEYGUYSwhatsup" --> False
  552. any (`elem` ['A'..'Z']) "HEYGUYSwhatsup" --> True
  553. iterate 接收一函数加一初值, 重复将初值代入计算, 返回结果列表:
  554. take 10 $ iterate (*2) 1 --> [1,2,4,8,16,32,64,128,256,512]
  555. take 3 $ iterate (++ "haha") "haha" --> ["haha","hahahaha","hahahahahaha"]
  556. splitAt 接收一数值加一列表, 按数值进行一次截断, 返回元组:
  557. splitAt 3 "heyman" --> ("hey","man")
  558. splitAt 100 "heyman" --> ("heyman","")
  559. splitAt (-3) "heyman" --> ("","heyman")
  560. let (a,b) = splitAt 3 "foobar" in b ++ a --> "barfoo"
  561. takeWhile 接收一判断加一列表, 返回判断出错前部分的列表:
  562. takeWhile (>3) [6,5,4,3,2,1,2,3,4,5,4,3,2,1] --> [6,5,4]
  563. takeWhile (/=' ') "This is a sentence" --> "This"
  564. 比如计算 10000 以内三次方之和:
  565. sum $ takeWhile (<10000) $ map (^3) [1..] --> 53361
  566. dropWhile 接收一判断加一列表, 从判断错误开始返回列表, 与上互补:
  567. dropWhile (/=' ') "This is a sentence" --> " is a sentence"
  568. dropWhile (<3) [1,2,2,2,3,4,5,4,3,2,1] --> [3,4,5,4,3,2,1]
  569. 下面例子按列表中元组首个元素大于 1000 筛选元组, 给出首个结果:
  570. let stock = [(994.4,2008,9,1),(995.2,2008,9,2),(999.2,2008,9,3),(1001.4,2008,9,4),(998.3,2008,9,5)]
  571. head (dropWhile (\(val,y,m,d) -> val < 1000) stock) --> (1001.4,2008,9,4)
  572. span 接收一判断加一列表, 开始连续否和其余部分, 用元组中列表返回,
  573. break 接收一判断一列表, 开始连续真和其余部分, 用元组中列表返回:
  574. (break p)与(span $ not . p)等价:
  575. break (==4) [1,2,3,4,5,6,7] --> ([1,2,3],[4,5,6,7])
  576. span (/=4) [1,2,3,4,5,6,7] --> ([1,2,3],[4,5,6,7])
  577. sort 接受一列表排序后返回一列表, 元素需是 Ord 类型的:
  578. sort [8,5,3,2,1,6,4,2] --> [1,2,2,3,4,5,6,8]
  579. sort "This will be sorted soon" --> " Tbdeehiillnooorssstw"
  580. group 接收一列表, 将相邻相同元素合并为列表, 返回列表嵌列表:
  581. group [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]
  582. --> [[1,1,1,1],[2,2,2,2],[3,3],[2,2,2],[5],[6],[7]]
  583. 先 sort, 然后 groups, 用来统计列表相同元素数量:
  584. map (\l@(x:xs) -> (x,length l)) . group . sort $ [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]
  585. --> [(1,4),(2,7),(3,2),(5,1),(6,1),(7,1)]
  586. inits "w00t" --> ["","w","w0","w00","w00t"]
  587. tails "w00t" --> ["w00t","00t","0t","t",""]
  588. let w = "w00t" in zip (inits w) (tails w)
  589. --> [("","w00t"),("w","00t"),("w0","0t"),("w00","t"),("w00t","")]
  590. tails 可以用在搜索片断中, 编写下函数:
  591. search :: (Eq a) => [a] -> [a] -> Bool
  592. search needle haystack =
  593. let nlen = length needle
  594. in foldl (\acc x -> if take nlen x == needle then True else acc) False (tails haystack)
  595. isInfixOf 接收两字符串判断后者是否包含前者, 返回布尔值:
  596. "cat" `isInfixOf` "im a cat burglar" --> True
  597. "Cat" `isInfixOf` "im a cat burglar" --> False
  598. "cats" `isInfixOf` "im a cat burglar" --> False
  599. isPrefixOf 和 isSuffixOf 判断是否在头部或尾部:
  600. "hey" `isPrefixOf` "hey there!" --> True
  601. "hey" `isPrefixOf` "oh hey there!" --> False
  602. "there!" `isSuffixOf` "oh hey there!" --> True
  603. "there!" `isSuffixOf` "oh hey there" --> False
  604. elem 和 notElem 检测元素是否在列表当中:
  605. notElem '3' "3.1415" --> False
  606. partition 接收一判断一列表, 按条件分成两列表, 以元组返回:
  607. partition (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
  608. --> ("BOBMORGAN","sidneyeddy")
  609. partition (>3) [1,3,5,6,3,2,1,0,3,7]
  610. --> ([5,6,7],[1,3,3,2,1,0,3])
  611. find 接收一判断一数组, 返回第一个判断为真的元素, 只是:
  612. find (>4) [1,2,3,4,5,6] --> Just 5
  613. find (>9) [1,2,3,4,5,6] --> Nothing
  614. :t find --> find :: (a -> Bool) -> [a] -> Maybe a
  615. Maybe 类型将在后面章节解释, 可对有值无值做返回, 较安全
  616. elemIndex 似 elem, 但返回索引值, 也用 Maybe 类型:
  617. :t elemIndex --> elemIndex :: (Eq a) => a -> [a] -> Maybe Int
  618. 4 `elemIndex` [1,2,3,4,5,6] --> Just 3
  619. 10 `elemIndex` [1,2,3,4,5,6] --> Nothing
  620. elemIndices 类似上条, 但返回多个索引的列表:
  621. ' ' `elemIndices` "Where are the spaces?" --> [5,9,13]
  622. findIndex 似 find, 但返回多个索引的列表或 Nothing:
  623. findIndex (==4) [5,3,2,1,6,4] --> Just 5
  624. findIndex (==7) [5,3,2,1,6,4] --> Nothing
  625. findIndices (`elem` ['A'..'Z']) "Where Are The Caps?" --> [0,6,10,14]

  626. 多个列表的有[3..8]的 zip 和 zipWith, 比如 zip3:
  627. zipWith3 (\x y z -> x + y + z) [1,2,3] [4,5,2,2] [2,2,3] --> [7,8,9]
  628. zip4 [2,3,3] [2,2,2] [5,5,3] [2,2,2] --> [(2,2,5,2),(3,2,5,2),(3,2,3,2)]
  629. lines 在处理文件或文本时将带'\n'的字符串分开返回列表:
  630. lines "first line\nsecond line\nthird line"
  631. --> ["first line","second line","third line"]
  632. unlines 与 lines 相反, 但注意结尾多出'\n'
  633. unlines ["first line", "second line", "third line"]
  634. "first line\nsecond line\nthird line\n"
  635. words 和 unwords 转换句子到单词, 以及相反, 通过空格和'\n'识别:
  636. words "hey these are the words in this sentence"
  637. --> ["hey","these","are","the","words","in","this","sentence"]
  638. words "hey these are the words in this\nsentence"
  639. --> ["hey","these","are","the","words","in","this","sentence"]
  640. unwords ["hey","there","mate"] --> "hey there mate"
  641. delete 接收一字符一字符串, 删去第一个匹配字符后返回:
  642. delete 'h' "hey there ghang!" --> "ey there ghang!"
  643. 'h' . delete 'h' $ "hey there ghang!" --> "ey tere ghang!"
  644. delete 'h' . delete 'h' . delete 'h' $ "hey there ghang!"
  645. --> "ey tere gang!"
  646. (\\)接受两列表, 从及一个列表减去第二个, 似 delete 只删除一次:
  647. [1..10] \\ [2,5,9] --> [1,3,4,6,7,8,10]
  648. "Im a big baby" \\ "big" --> "Im a baby"
  649. union 接收两列表检查逐个后者在前者不含时追加:
  650. "hey man" `union` "man what's up" --> "hey manwt'sup"
  651. [1..7] `union` [5..10] --> [1,2,3,4,5,6,7,8,9,10]
  652. intersect 接收两列表返回交集:
  653. [1..7] `intersect` [5..10] --> [5,6,7]
  654. insert 接收一字符一列表, 插入到首个不小于自身的元素前:
  655. insert 4 [3,5,1,2,8,2] --> [3,4,5,1,2,8,2]
  656. insert 4 [1,3,4,4,1] --> [1,3,4,4,4,1]
  657. insert 4 [1,2,3,5,6,7] --> [1,2,3,4,5,6,7]
  658. insert 'g' $ ['a'..'f'] ++ ['h'..'z'] --> "abcdefghijklmnopqrstuvwxyz"
  659. insert 3 [1,2,4,3,2,1] --> [1,2,3,4,3,2,1]
  660. 历史原因上面有些函数返回 Int, 不能用于出除法, 于是另有 Num 类型以下函数:
  661. length, take, drop, splitAt, !!, replicate
  662. genericLength, genericTake, genericDrop, genericSplitAt, genericIndex, genericReplicate
  663. nub, delete, union, intersect, group 存在对应更通用版本,
  664. nubBy, deleteBy, unionBy, intersectBy, groupBy
  665. 前者限定条件(==), 后者接收一个函数作为分组的条件:
  666. 比如 group 等价(groupBy (==)):
  667. let values = [-4.3, -2.4, -1.2, 0.4, 2.3, 5.9, 10.5, 29.1, 5.3, -2.4, -14.5, 2.9, 2.3]
  668. groupBy (\x y -> (x > 0) == (y > 0)) values
  669. --> [[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
  670. 使用 Data.Function 模块的 on 函数更简洁:
  671. on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
  672. f `on` g = \x y -> f (g x) (g y)
  673. groupBy ((==) `on` (> 0)) values
  674. --> [[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
  675. 另几函数类似, 比如 sort 和 sortBy:
  676. sortBy :: (a -> a -> Ordering) -> [a] -> [a]
  677. Ordering 可以是 LT, EQ, GT. sort 等价 (sortBy compare)
  678. let xs = [[5,4,5,4,4],[1,2,3],[3,5,4,3],[],[2],[2,2]]
  679. sortBy (compare `on` length) xs
  680. --> [[],[2],[2,2],[1,2,3],[3,5,4,3],[5,4,5,4,4]]
  681. (compare `on` length)等价(\x y -> length x `compare` length y)

  682. Data.Char 处理函数字符, 甚至过滤以及映射
  683. isControl 判断是否是控制字符, 具体看链接:
  684. isSpace 判断是否 Unicode 空白或换行之类 \t, \n, \r, \f, \v
  685. isLower 是否 Unicode 小写,
  686. isUpper 是否 Unicode 大写,
  687. isAlpha 是否 Unicode 字母, 英文解释一大串不懂,
  688. isAlphaNum 是否 Unicode 字母或数字,
  689. isPrint 是否 Unicode 可打印, 控制字符不可打印,
  690. isDigit 是否 ASCII 数字, ['0'..'9']中的数字,
  691. isOctDigit 是否 ASCII 八进制数字['0'..'7']中,
  692. isHexDigit 是否 ASCII 十六进制数字['0'..'9'], ['a'..'f'], ['A'..'F'],
  693. isLetter 是否 ASCII 字母, 和 isAlpha 等价,
  694. isMark 是否 Unicode 注音字符, 关于法语, 跳过,
  695. isNumber 是否 Unicode 数字, 关系到罗马数字, 等,
  696. isPunctuation 是否 Unicode 标点, 如连接号括号引号,
  697. isSymbol 是否 Unicode 符号, 如数学或货币符号,
  698. isSeparator 是否 Unicode 空格或分隔符,
  699. isAscii 是否 Unicode 前 128 字符, 也对应 ASCII,
  700. isLatin1 是否 Unicode 前 256 字符, 对应 ISO 8859-1 (Latin-1),
  701. isAsciiUpper 是否 ASCII 大写,
  702. isAsciiLower 是否 ASCII 小写
  703. 类型都是 Char -> Bool , 对于字符串结合 Data.List.all 处理:
  704. all isAlphaNum "bobby283" --> True
  705. all isAlphaNum "eddy the fish!" --> False
  706. 用 isSpace 模拟 Data.List.words, 注意空格:
  707. words "hey guys its me" --> ["hey","guys","its","me"]
  708. groupBy ((==) `on` isSpace) "hey guys its me"
  709. --> ["hey"," ","guys"," ","its"," ","me"]
  710. filter (not . any isSpace) . groupBy ((==) `on` isSpace) $ "hey guys its me"
  711. --> ["hey","guys","its","me"]
  712. Data.Char 输出数据类型属于 Ordering, 可以给 LT, EQ, GT,
  713. GeneralCategory 用来查看类别, 总共 31 种类别:
  714. generalCategory :: Char -> GeneralCategory
  715. generalCategory ' ' --> Space
  716. generalCategory 'A' --> UppercaseLetter
  717. generalCategory 'a' --> LowercaseLetter
  718. generalCategory '.' --> OtherPunctuation
  719. generalCategory '9' --> DecimalNumber
  720. map generalCategory " \t\nA9?|"
  721. --> [Space,Control,Control,UppercaseLetter,DecimalNumber,OtherPunctuation,MathSymbol]
  722. GeneralCategory 类型属于 Eq 类型, 因此可判断:
  723. generalCategory c == Space
  724. toUpper 将小写字母转换为大写, 其他符号不发生改变,
  725. converts 转换为小写, 其他不变,
  726. toTitle 基本上等价 toUpper,
  727. digitToInt 将['0'..9], ['a'..'f'], ['A'..'F']转换数字, 其余报错,
  728. map digitToInt "34538" --> [3,4,5,3,8]
  729. map digitToInt "FF85AB" --> [15,15,8,5,10,11]
  730. intToDigit 与上相反, 接收数字转化字符, [0..15]:
  731. intToDigit 15 --> 'f'
  732. intToDigit 5 --> '5'
  733. ord 将字符转化为 ASCII 编码值, 取决于 Unicode, chr 相反:
  734. ord 'a' --> 97
  735. chr 97 --> 'a'
  736. map ord "abcdefgh" --> [97,98,99,100,101,102,103,104]
  737. 通过改变编码转换字符来模式凯撒编码:
  738. encode :: Int -> String -> String
  739. encode shift msg =
  740. let
  741. ords = map ord msg
  742. shifted = map (+ shift) msg
  743. in map chr shifted
  744. 如果喜欢符合函数, 可以写(map (chr . (+ shift) . ord) msg)
  745. encode 3 "Heeeeey" --> "Khhhhh|"
  746. encode 4 "Heeeeey" --> "Liiiii}"
  747. encode 1 "abcd" --> "bcde"
  748. encode 5 "Marry Christmas! Ho ho ho!" --> "Rfww~%Hmwnxyrfx&%Mt%mt%mt&"
  749. 解码时取相反的 shift 参数即可:
  750. decode :: Int -> String -> String
  751. decode shift msg = encode (negate shift) msg
  752. encode 3 "Im a little teapot" --> "Lp#d#olwwoh#whdsrw"
  753. decode 3 "Lp#d#olwwoh#whdsrw" --> "Im a little teapot"
  754. decode 5 . encode 5 $ "This is a sentence" --> "This is a sentence"

  755. 关联列表也叫字典, 近似散列哈希表, 存放顺序无关的键值对,
  756. phoneBook =
  757. [("betty","555-2938")
  758. ,("bonnie","452-2928")
  759. ,("patsy","493-2928")
  760. ,("lucille","205-2928")
  761. ,("wendy","939-8282")
  762. ,("penny","853-2492")]
  763. 上面是字典的例子, 常用任务是获取给定键对应的值:
  764. findKey :: (Eq k) => k -> [(k,v)] -> v
  765. findKey key xs = snd . head . filter (\(k,v) -> key == k) $ xs
  766. 上面函数当不含对应键, 给出空列表时, 会出现运行时错误, 换 Maybe 类型:
  767. findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
  768. findKey key [] = Nothing
  769. findKey key ((k,v):xs) = if key == k then Just v else findKey key xs
  770. 该函数明显递归, 有边界条件, 有递归的调用, 换 fold 实现:
  771. findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
  772. findKey key = foldr (\(k,v) acc -> if key == k then Just v else acc) Nothing
  773. findKey "penny" phoneBook --> Just "853-2492"
  774. findKey "betty" phoneBook --> Just "555-2938"
  775. findKey "wilma" phoneBook --> Nothing
  776. lookup 函数即对应上述 findKey, 上述函数将遍历一遍列表,
  777. Data.Map 内部用树部署数据, 处理更快, 另有操作工具
  778. 因此从此不再用字典称呼, 而称呼其为 map
  779. lookup 等某些函数从 Data.Map 导入到 Prelude, 这里载入模块:
  780. import qualified Data.Map as Map
  781. fromList 接收一个字典覆盖相同键的重复值返回一个 map,
  782. 猜测前面用 fromList 标明是内部储存用的 map (?), 不能(!!)取出:
  783. Map.fromList [("betty","555-2938"),("bonnie","452-2928"),("lucille","205-2928")]
  784. --> fromList [("betty","555-2938"),("bonnie","452-2928"),("lucille","205-2928")]
  785. Map.fromList [(1,2),(3,4),(3,2),(5,5)] --> fromList [(1,2),(3,2),(5,5)]
  786. 其类型声明, k 在存储时需要是 Ord 来排序, Map.Map 存疑(?):
  787. Map.fromList :: (Ord k) => [(k, v)] -> Map.Map k v
  788. empty 返回一个空的 map:
  789. Map.empty --> fromList []
  790. insert 接收一键一值一 map, 返回加入键值后的 map:
  791. Map.insert 3 100 Map.empty --> fromList [(3,100)]
  792. Map.insert 5 600 (Map.insert 4 200 ( Map.insert 3 100 Map.empty))
  793. --> fromList [(3,100),(4,200),(5,600)]
  794. Map.insert 5 600 . Map.insert 4 200 . Map.insert 3 100 $ Map.empty
  795. --> fromList [(3,100),(4,200),(5,600)]
  796. 用 insert 可以实现 fromList 的功能, 去掉重复返回 map:
  797. fromList' :: (Ord k) => [(k,v)] -> Map.Map k v
  798. fromList' = foldr (\(k,v) acc -> Map.insert k v acc) Map.empty
  799. null 用来判断 map 是否为空:
  800. Map.null Map.empty --> True
  801. Map.null $ Map.fromList [(2,3),(5,5)] --> False
  802. size 用来探测长度, 和 length 类似:
  803. Map.size Map.empty --> 0
  804. Map.size $ Map.fromList [(2,4),(3,3),(4,2),(5,4),(6,4)] --> 5
  805. singleton 接受一键一值创建一个 map:
  806. Map.singleton 3 9 --> fromList [(3,9)]
  807. Map.insert 5 9 $ Map.singleton 3 9 --> fromList [(3,9),(5,9)]
  808. lookup 类似 Data.List.lookup , 但可操作 map:
  809. Map.lookup 2 $ Map.fromList [(2,'4')] --> Just '4'
  810. member 接收一键一 map, 检查键是否在 map 中:
  811. Map.member 3 $ Map.fromList [(3,6),(4,3),(6,9)] --> True
  812. Map.member 3 $ Map.fromList [(2,5),(4,5)] --> False
  813. map 和 filter 与 Data.List 中类似, 独作用于 map:
  814. Map.map (*100) $ Map.fromList [(1,1),(2,4),(3,9)]
  815. --> fromList [(1,100),(2,400),(3,900)]
  816. Map.filter isUpper $ Map.fromList [(1,'a'),(2,'A'),(3,'b'),(4,'B')]
  817. --> fromList [(2,'A'),(4,'B')]
  818. toList 是 fromList 的反演, 特别看类型声明:
  819. Map.toList :: Map.Map k a -> [(k, a)]
  820. Map.toList . Map.insert 9 2 $ Map.singleton 4 3 --> [(4,3),(9,2)]
  821. keys 和 elems 分别打印出键值为列表,
  822. keys 等价(map fst . Map.toList), elems 等价(map snd . Map.toList)
  823. fromListWith 接收一函数一列表返回一 map, 相当与戴上函数的 fromList:
  824. phoneBook =
  825. [("betty","555-2938")
  826. ,("betty","342-2492")
  827. ,("bonnie","452-2928")
  828. ,("patsy","493-2928")
  829. ,("patsy","943-2929")
  830. ,("patsy","827-9162")
  831. ,("lucille","205-2928")
  832. ,("wendy","939-8282")
  833. ,("penny","853-2492")
  834. ,("penny","555-2111")]
  835. 借助 fromListWith 写函数将重复的值用','连接或组成列表, 或最大值/ 总和:
  836. phoneBookToMap :: (Ord k) => [(k, String)] -> Map.Map k String
  837. phoneBookToMap xs = Map.fromListWith (\number1 number2 -> number1 ++ ", " ++ number2) xs
  838. Map.lookup "patsy" $ phoneBookToMap phoneBook
  839. --> "827-9162, 943-2929, 493-2928"
  840. Map.lookup "wendy" $ phoneBookToMap phoneBook
  841. --> "939-8282"
  842. Map.lookup "betty" $ phoneBookToMap phoneBook
  843. --> "342-2492, 555-2938"
  844. phoneBookToMap :: (Ord k) => [(k, a)] -> Map.Map k [a]
  845. phoneBookToMap xs = Map.fromListWith (++) $ map (\(k,v) -> (k,[v])) xs
  846. Map.lookup "patsy" $ phoneBookToMap phoneBook
  847. --> ["827-9162","943-2929","493-2928"]
  848. Map.fromListWith max [(2,3),(2,5),(2,100),(3,29),(3,22),(3,11),(4,22),(4,15)]
  849. --> fromList [(2,100),(3,29),(4,22)]
  850. Map.fromListWith (+) [(2,3),(2,5),(2,100),(3,29),(3,22),(3,11),(4,22),(4,15)]
  851. --> fromList [(2,108),(3,62),(4,37)]
  852. insertWith 类似上函数, 出现重复键时调用函数处理:
  853. Map.insertWith (+) 3 100 $ Map.fromList [(3,4),(5,103),(6,339)]
  854. --> fromList [(3,104),(5,103),(6,339)]
  855. 全部函数见下, 可用 import Data.Map (Map) 代替加载:
  856. containers/Data-Map.html

  857. Data.Set 模块提供集合的处理, 其中元素唯一,
  858. 内部为了高效排序, 因此要 Ord 类型, 速度很快,
  859. 常用操作是插入元素/ 检查成员关系/ 转换为列表
  860. Data.Set 与 Prelude 和 Data.List 命名冲突多, 用:
  861. import qualified Data.Set as Set
  862. text1 = "I just had an anime dream. Anime... Reality... Are they so different?"
  863. text2 = "The old man left his garbage can out and now his trash is all over my lawn!"
  864. let set1 = Set.fromList text1
  865. let set2 = Set.fromList text2
  866. set1 --> fromList " .?AIRadefhijlmnorstuy"
  867. set2 --> fromList " !Tabcdefghilmnorstuvwy"
  868. intersection 函数可检查两集合的交集:
  869. Set.intersection set1 set2 --> fromList " adefhilmnorstuy"
  870. difference 检测两集合前者有后者没有的部分:
  871. Set.difference set1 set2 --> fromList ".?AIRj"
  872. Set.difference set2 set1 --> fromList "!Tbcgvw"
  873. union 返回两集合并集:
  874. Set.union set1 set2 --> fromList " !.?AIRTabcdefghijlmnorstuvwy"
  875. null, size, member, empty, singleton, insert, delete 可从字面理解:
  876. Set.null Set.empty --> True
  877. Set.null $ Set.fromList [3,4,5,5,4,3] --> False
  878. Set.size $ Set.fromList [3,4,5,3,4,5] --> 3
  879. Set.singleton 9 --> fromList [9]
  880. Set.insert 4 $ Set.fromList [9,3,8,1] --> fromList [1,3,4,8,9]
  881. Set.insert 8 $ Set.fromList [5..10] --> fromList [5,6,7,8,9,10]
  882. Set.delete 4 $ Set.fromList [3,4,5,4,3,4,5] --> fromList [3,5]
  883. isSubsetOf 和 isProperSubsetOf 判断两集合前者是否后者子集和真子集,
  884. Set.fromList [2,3,4] `Set.isSubsetOf` Set.fromList [1,2,3,4,5] --> True
  885. Set.fromList [1,2,3,4,5] `Set.isSubsetOf` Set.fromList [1,2,3,4,5] --> True
  886. Set.fromList [1,2,3,4,5] `Set.isProperSubsetOf` Set.fromList [1,2,3,4,5] --> False
  887. Set.fromList [2,3,4,8] `Set.isSubsetOf` Set.fromList [1,2,3,4,5] --> False
  888. 同样 filter 和 map 的功能:
  889. Set.filter odd $ Set.fromList [3,4,5,6,7,2,3,4] --> fromList [3,5,7]
  890. Set.map (+1) $ Set.fromList [3,4,5,6,7,2,3,4] --> fromList [3,4,5,6,7,8]
  891. 集合常用 fromList 去重再借 toList 返回到列表,
  892. Data.List.nub 也可对列表去重, 但相比速度用集合更快, 代价是,
  893. 集合需要 Ord 类型限定, 而 nub 仅需要 Eq 类型限定
  894. let setNub xs = Set.toList $ Set.fromList xs
  895. setNub "HEY WHATS CRACKALACKIN" --> " ACEHIKLNRSTWY"
  896. nub "HEY WHATS CRACKALACKIN" --> "HEY WATSCRKLIN"
  897. 相较而言 nub 保持了列表原先规则, 而 setNub 不保持(?)

  898. 和很多语言一样, Haskell 可以自己写模块重用,
  899. 想这个计算体积面积的模块, 先命名为 Geomerty.hs,
  900. 文件末尾'r'开头的函数使用但不输出, 不影响:
  901. module Geometry
  902. ( sphereVolume
  903. , sphereArea
  904. , cubeVolume
  905. , cubeArea
  906. , cuboidArea
  907. , cuboidVolume
  908. ) where
  909. sphereVolume :: Float -> Float
  910. sphereVolume radius = (4.0 / 3.0) * pi * (radius ^ 3)
  911. sphereArea :: Float -> Float
  912. sphereArea radius = 4 * pi * (radius ^ 2)
  913. cubeVolume :: Float -> Float
  914. cubeVolume side = cuboidVolume side side side
  915. cubeArea :: Float -> Float
  916. cubeArea side = cuboidArea side side side
  917. cuboidVolume :: Float -> Float -> Float -> Float
  918. cuboidVolume a b c = rectangleArea a b * c
  919. cuboidArea :: Float -> Float -> Float -> Float
  920. cuboidArea a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2
  921. rectangleArea :: Float -> Float -> Float
  922. rectangleArea a b = a * b
  923. 注意需要在同一个目录, 大写开头, 加载模块似乎 ghci 不行, 脚本里正常:
  924. import Geometry
  925. 或, 建立 Geometry 目录, 分别创建文件
  926. Sphere.hs :
  927. module Geometry.Sphere
  928. ( volume
  929. , area
  930. ) where
  931. volume :: Float -> Float
  932. volume radius = (4.0 / 3.0) * pi * (radius ^ 3)
  933. area :: Float -> Float
  934. area radius = 4 * pi * (radius ^ 2)
  935. Cuboid.hs :
  936. module Geometry.Cuboid
  937. ( volume
  938. , area
  939. ) where
  940. volume :: Float -> Float -> Float -> Float
  941. volume a b c = rectangleArea a b * c
  942. area :: Float -> Float -> Float -> Float
  943. area a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2
  944. rectangleArea :: Float -> Float -> Float
  945. rectangleArea a b = a * b
  946. Cube.hs :
  947. module Geometry.Cube
  948. ( volume
  949. , area
  950. ) where
  951. import qualified Geometry.Cuboid as Cuboid
  952. volume :: Float -> Float
  953. volume side = Cuboid.volume side side side
  954. area :: Float -> Float
  955. area side = Cuboid.area side side side
  956. import Geometry.Sphere -- 用来导入, 另两类似. 或者:
  957. import qualified Geometry.Sphere as Sphere
  958. import qualified Geometry.Cuboid as Cuboid
  959. import qualified Geometry.Cube as Cube
  960. 然后以 Sphere.area, Sphere.volume, Cuboid.area 调用

  961. Excerpted from haskell notes

阅读(5313) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~