网络书屋(Web Reading Room)

A blogging framework for hackers.

Haskell笔记

2001年,出生于荷兰的计算机大师、图灵奖获得者EdsgerW.Dijkstra给德州大学预算委员会写信,力劝不要将计算机入门课程改为Java。不幸的是,学校最终还是用Java课程替换了Haskell。Haskell真的不行了?国内有些人的评论

  • Haskell:函数式编程语言
  • Java : 命令编程语言 尽管Haskell不完美,但仍然比Java好几个数量级,Java就是一个大杂烩(它是通过大范围的广告和销售员夸张的宣传才达到它的商业接受)。

  • 如果别人问你Haskell能够做什么? 一个能够编程的编程语言,高级函数编程语言

  • 如果别人问你Haskell和scheme有什么区别? 比scheme更强调类型理论,但是相对scheme由于对于type theory的过度追求 反而更加难于编程

如果别人问你Haskell 如何实现冒泡法 直接排序 快速排序?

冒泡排序

1
2
3
4
5
6
7
sort1 [x] = [x]
sort1 (x:x1:xs)  {-选取了一个x1temp 值  x当做首数,x1:temp-}
    | x>x1 = x1:sort1 (x:xs)
    | otherwise = x:sort1 (x1:xs)
mpsort [] = []
mpsort x = let tmp = sort1 x in
           mpsort (init tmp) ++ [(last tmp)]

直接排序

1
2
3
4
5
import Data.List
selectsort::[Int] -> [Int]
selectsort [] = []
selectsort [x] = [x]
selectsort xs = minimum xs : selectsort ( delete ( minimum xs )  xs)

快速排序1

1
2
3
4
5
6
quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
  let smallerSorted = quicksort [a | a <- xs, a <= x]
      biggerSorted = quicksort [a | a <- xs, a > x]
  in smallerSorted ++ [x] ++ biggerSorted

快速排序2

1
2
3
4
5
6
quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
    let smallerSorted = quicksort (filter (<=x) xs)
        biggerSorted = quicksort (filter (>x) xs)
    in  smallerSorted ++ [x] ++ biggerSorted

如果别人问你递归是如何实现的?

如果别人问你如何构造Fibonaaci数?

如果别人问你:: => –>是什么意思?

(==) :: Eq a => a –> a –> Bool 可以这样阅读: 相当函数的两个相同的类型值作为参数并返回一个bool值,而这两个参数的类型同在Eq类之中(即类型约束) eq这一类型 提供了判断相等性的接口,凡是可以比较相等性的类型嗯必属于Eq类 (=>左边的部分叫做类型约束) a表示人意的类型 ————-泛形 —-多态函数挂钩。。。。

:: 读作他的类型为 (类似 !! 从list去除 第几个元素 <– 属于 elem是什么的元素 mod 可以和什么除。。

如果有人问你如何用haskell编写函数?

他和scheme有很大的不同,scheme用的是(define) 而haskell用的是模式匹配,比如下面的求最大值的程序

1
2
3
4
5
6
7
maximum' :: (Ord a) => [a] -> a
maximum' [] = error "maximum of empty list"
maximum' [x] = x
maximum' (x:xs)
    | x > maxTail = x
    | otherwise = maxTail
    where maxTail = maximum' xs

如果别人叫你简化上面的maximum程序

1
2
3
4
maximum' :: (Ord a) => [a] -> a
maximum' [] = error "maximum of empty list"
maximum' [x] = x
maximum' (x:xs) = max x (maximum' xs)

如果别人问你Haskell到底长成啥子样子?

你会告诉他他涨起来真的是一节一节的,一段一段的,有棱有角

如果有人问你replicate 3 5是如何实现的?

1
2
3
4
replicate' :: (Num i, Ord i) => i -> a -> [a]
replicate' n x
| n <= 0    = []
| otherwise = x:replicate' (n-1) x

如果有人问你take是如何实现的呢? drop?

1
2
3
4
5
take' :: (Num i, Ord i) => i -> [a] -> [a]
take' n _
| n <= 0   = []
take' _ []     = []
take' n (x:xs) = x : take' (n-1) xs

如果有人问你_ 下划线表示什么意思?

_ 代表任意东西

如果有人问你reverse如何实现?

1
2
3
reverse' :: [a] -> [a]
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]

如果有人问你repeat如何实现?

1
2
repeat' :: a -> [a]
repeat' x = x:repeat' x

如果有人做了一个tuple ,想要用zip产生更多tuples?

1
2
3
4
zip' :: [a] -> [b] -> [(a,b)]
zip' _ [] = []
zip' [] _ = []
zip' (x:xs) (y:ys) = (x,y):zip' xs ys

如果有人问你mod的类似函数 elem的实现?

1
2
3
4
5
elem' :: (Eq a) => a -> [a] -> Bool
elem' a [] = False
elem' a (x:xs)
| a == x    = True
| otherwise = a `elem'` xs

如果有人问你typeclass有哪里类?

利用 type 自己查去

如果有人问你Haskell有没有类似scheme的lambda函数?

告诉他有,而且更加直接,虽然刚开始书写起来不方便

scheme版的lambda:

1
2
(lambda (x y)
   (+ x y))

Haskell版的lambda:

1
2
(\y -> y + 3
其中\后面跟着参数  ->后面跟着函数体

18.如果有人问haskell有没有存在高阶函数?

有 。 map ,filter

1
map (\y-> y+3) [3,5,6]

19.如果有人问你flip有什么含义?

1
2
3
4
5
let f x  y = x / y
Prelude> map (f 2) [1,2,3]
[2.0,1.0,0.6666666666666666]
Prelude> map (flip f 2) [1,2,3]
[0.5,1.0,1.5]

20. 如何有人问 如何运行 2048

1
2
$ runhaskell putchar_test.hs
use runhaskell to run the *.hs programme

21. 如果有人问haskell实现递归的自然表达式?

1
2
3
4
5
6
7
8
9
quicksort (x:xs) =
elem' a (x:xs)

zip' (x:xs) (y:ys) = (x,y):zip' xs ys
reverse' (x:xs) = reverse' xs ++ [x]

take' n (x:xs) = x : take' (n-1) xs
maximum' (x:xs) = max x (maximum' xs)
maximum' (x:xs)

你可以看见每一个形式都有函数名,函数使用方式,函数的参数表
这是一种模式,因为Haskell使用函数的时候,先写出函数,然后空格 写出变量 紧接着其他可选变量 所以你也函数匹配模式 也应该是这样来写 基于这种思想,你重新回过头看函数定义,你会发现还真的是这样额?! 从函数的定义知道他的用法,这就是Haskell与众不同的一点。

22. 如果有人问如何写出阶乘形式?

告诉他用函数,告诉他用函数匹配,告诉他用递归。。。。。

1
2
3
factorial :: (Integral a) => a -> a
factorial 0 = 1
factorial n = n * factorial (n - 1)

23. 如果别人问你Haskell如何新建一中类型?

暂时不太懂 但是我知道 Haskell使用 data来定义类别 julia使用type来定义

24. 如果别人问你monads 是什么玩意?

其实这个是更加深层次的运用Haskell,在scheme也有. 比如IO monad,可以想象为一种抽象数据类型。

25. 如果别人问你如何学习haskell?

告诉他有一篇文章叫做 如此有趣的学习Haskell Haskell趣學指南 http://learnyouahaskell-zh-tw.csie.org/zh-cn/chapters.html 还不错 ,这个教程

学习Haskell ,要是你能够对于空格特别敏感,会对你有所好处,因为很多地方都是用空格分开 而不是() [] 之类的。空格有实际意义,分开不同的字段

26. 如果别人问你 | 有什么意义?

分类说明

1
2
3
4
5
6
bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
    | weight / height ^ 2 <= 18.5 = "You're underweight, you emo, you!"
    | weight / height ^ 2 <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
    | weight / height ^ 2 <= 30.0 = "You're fat! Lose some weight, fatty!"
    | otherwise                   = "You're a whale, congratulations!"

每一个函数定义的时候,在Haskell看来只是其中的一种模式,一个模式下可以有很多的分类,如果他找不到模式 就说找不到,所以定义的时候,尽可能的完善定义。 比如下面的lucky函数 和sayMe 函数 lucky函数:

1
2
3
lucky :: (Integral a) => a -> String
lucky 7 = "LUCKY NUMBER SEVEN!"
lucky x = "Sorry, you're out of luck, pal!"

sayme函数:

1
2
3
4
5
6
7
sayMe :: (Integral a) => a -> String
sayMe 1 = "One!"
sayMe 2 = "Two!"
sayMe 3 = "Three!"
sayMe 4 = "Four!"
sayMe 5 = "Five!"
sayMe x = "Not between 1 and 5"

27. 如果别人叫你简化bmiTell函数

使用where

1
2
3
4
5
6
7
bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
    | bmi <= 18.5 = "You're underweight, you emo, you!"
    | bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
    | bmi <= 30.0 = "You're fat! Lose some weight, fatty!"
    | otherwise   = "You're a whale, congratulations!"
    where bmi = weight / height ^ 2

28. 如果别人问你 where可以跟多少个变量 ?

跟他说 很多个。

1
2
3
4
5
6
7
8
9
10
bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
    | bmi <= skinny = "You're underweight, you emo, you!"
    | bmi <= normal = "You're supposedly normal. Pffft, I bet you're ugly!"
    | bmi <= fat    = "You're fat! Lose some weight, fatty!"
    | otherwise     = "You're a whale, congratulations!"
    where bmi = weight / height ^ 2
          skinny = 18.5
          normal = 25.0
          fat = 30.0

函数在 where 绑定中定义的名字只对本函数可见,因此我们不必担心它会污染其他函数的命名空间。注意,其中的名字 都是一列垂直排开,如果不这样规范,Haskell 就搞不清楚它们在哪个地方了

29. 如果有人问你 where当中可以使用模式匹配吗?

可以。

1
2
where bmi = weight / height ^ 2
(skinny, normal, fat) = (18.5, 25.0, 30.0)

30. 如果别人叫你写一个show类型的函数?

1
2
3
4
5
tell :: (Show a) => [a] -> String
tell [] = "The list is empty"
tell (x:[]) = "The list has one element: " ++ show x
tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y
tell (x:y:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y

这个函数顾及了空 List,单元素 List,双元素 List 以及较长的 List,所以这个函数很安全。(x:[]) 与 (x:y:[]) 也可以写作 [x] 和 [x,y] (有了语法糖,我们不必多加括号)。不过 (x:y:_)这样的模式就不行了,因为它匹配的 List 长度不固定。

31. 如果别人问你Int 和Integer的区别?

Int 有上下线 Integer没有

1
2
3
4
5
6
7
8
Prelude> minBound :: Int
-9223372036854775808
Prelude> min
min       minBound  minimum
Prelude> minBound  :: Integer

<interactive>:247:1:
    No instance for (Bounded Integer) arising from a use of `minBound'

32. 如果别人问你char and [char] 的区别?

1
2
3
4
5
6
7
Prelude> :t 'a'
'a' :: Char
Prelude> :t "a"
"a" :: [Char]
Prelude> :t "dfasdf"
"dfasdf" :: [Char]
Prelude>

33. 如果别人问你变量有类型,那么函数是不是也有类型?

1
2
removeNonUppercase :: [Char] -> [Char]
removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]

removeNonUppercase 的类型为 [Char]–>[Char],从它的参数和回传值的类型上可以看出, 它将一个字串映射为另一个字串。[Char] 与 String 是等价的,但使用 String 会更清晰: removeNonUppercase :: String –> String。编译器会自动检测出它的类型,我们还是标明了 它的类型声明。要是多个参数的函数该怎样?如下便是一个将三个整数相加的简单函数 参数之间由 –> 分隔,而与回传值之间并无特殊差异

34. 如果别人问你有没有比较好用的编程工具?

告诉他Haskell!

该如何学习? 告诉他安装 ghci,并且在软件中输入 :brower 就能观察到很多的类型 并且支持tab键。 :brower 会列出内置函数的 类型,以及他的基本使用方法

35. 如果别人问你高阶函数是如何定义?

高阶函数

1
2
3
4
map f lst --将lst按照函数f映射得到一个新的数列
map :: (a->b) -> [a] ->
map f [] = []
map f (x:xs) = f x : map f xs

36. 如果别人问你如何使用 :brower 提供的函数类型?

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
36.1
Prelude> :t product
product :: Num a => [a] -> a
Prelude> product [3,5,2]
30
36.2
readFile :: FilePath -> IO String

Prelude> readFile "/tmp/a.txt"
"fdsf"
Prelude> let  d=readFile "/tmp/a.txt"
Prelude> d
"fdsf"

37. 如果别人问你如何切断一个数组在某个位置?

1
2
3
splitAt :: Int -> [a] -> ([a], [a])
Prelude> splitAt 3 [3,5,6,2,3,6,3]
([3,5,6],[2,3,6,3])

38. 如果别人问你为什么你这样使用subtract?

告诉他,我是看着定义来使用的。

1
2
3
4
subtract :: Num a => a -> a -> a
Prelude> subtract 20 3
-17
Prelude> subtract [30,3] [3,3]

39. 如果别人问你 zip相关的函数?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Prelude> unzip [(3,4)]
([3],[4])
Prelude> unzip [(3,4),(3,7)]
([3,3],[4,7])
Prelude> unzip [(3,4),(3,7),(7,4)]
([3,3,7],[4,7,4])
Prelude> unzip [(3,4),(3,7),(7,4),(8,9)]
([3,3,7,8],[4,7,4,9])
Prelude> unzip [((3,4),(3,7)),((7,4),(8,9))]
([(3,4),(7,4)],[(3,7),(8,9)])

unzip3 :: [(a, b, c)] -> ([a], [b], [c])

Prelude> unzip3 [(3,6,3)]
([3],[6],[3])
Prelude> unzip3 [(3,6,3),(3,5,9)]
([3,3],[6,5],[3,9])
Prelude> unzip3 [(3,6,3),(3,5,9),(8,9,0)]
([3,3,8],[6,5,9],[3,9,0])
Prelude> unzip3 [(3,6,3,5),(3,5,9,3),(8,9,0,6)]

<interactive>:172:9:
    Couldn't match expected type `(a0, b0, c0)'
                with actual type `(t0, t1, t2, t3)'

40. 如果别人问你un相关的函数?

1
2
3
4
5
6
Prelude> unlines ["ds","dfsf"]
"ds\ndfsf\n"
until :: (a -> Bool) -> (a -> a) -> a -> a
unwords :: [String] -> String
Prelude> unwords ["dsf","fdsf"]
"dsf fdsf"

41. 如果比人问你如何使用 sin等三角函数?

转换为弧度制

1
2
3
4
Prelude> sin (45/180*pi)
0.7071067811865475
Prelude> sin (45*pi/180)
0.7071067811865475

42. 如果别人问你Haskell针对字符的函数?

Haskell中预定义的针对字符的函数有:

1
2
3
4
5
6
   isAscii, isLatin1, isControl, isPrint, isSpace, isUpper, isLower,
   isAlpha, isDigit, isOctDigit, isHexDigit, isAlphaNum,
   digitToInt, intToDigit,
   toUpper, toLower,
   ord, chr,

ord将字母转换为数字, chr反之.

43. 如果别人问你构造数组的几种方式?

数列是通过[]来进行描述的

43.1 数组构造

数列是用[]和(:)构造的, []是一个空的数列, x:xs的含义是元素x附加到数列xs的前面组成一个更长的数列. 比如, 1:[] 等于[1], 2:3:1:[]等于[2,3,1], 运算符(:)是从右向左运算的. 所有的数列都可以看作是从[]开始, 将各元素用(:)附到上面形成的. 在实际编程中有一些简记法可以快速地构造数列.

43.2 列举法

将数列的元素一一列举, 比如: [1,2,3], [‘A’,‘B’,’d’], [[1,2], [4,5,6]]等等, 数列的类型用”[元素类型]“来表示, 这几个例子的类型依次为: [Int], [Char], [[Int]].

43.3 范围法

适用于构造等差数列, 比如: [1..5]等于[1,2,3,4,5], [‘a’..’d’]等于[‘a’,‘b’,‘c’,’d’]等于”abcd”因为type String=[Char]. 默认的等差为1, 也可以给出前两个元素指定等差, 比如: [2,4..8]等于[2,4,6,8], [2,4..7]等于[2,4,6], [2.5,4..9.5]等于 [2.5,4.0,5.5,7.0,8.5,10.0].

43.3 描述法

描述法给出数列中元素取值的范围以及所满足的条件, 与数学中集合的描述法是一样的. 例如: [3*x+2| x<–[3,6,9]] —记号”<–“表示属于,x依次取3,6,9,代入3*x+2,得到数列[11,20,29] [x*x| x<–[1..9], x `rem` 3==1] —给出x的范围,还限定x除3余1 [(x,y)|x<–[1,2,3],y<–[x..3]] —等于 [(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)]

44. 如果别人跟你说Haskell可以write poet?

第一次语文 数学 计算机的结合

1
2
3
4
5
6
7
8
9
Prelude>  let nouns = ["he","she","I"]
Prelude> let adjectives=["lazy","handsome","something"]
Prelude> [adjective ++ " " ++ noun | adjective <-adjectives ,noun<-nouns]
["lazy he","lazy she","lazy I","handsome he","handsome she","handsome I","something he","something she","something I"]

*Main> let nouns = ["he","she","I"]
*Main> let adjectives=["lazy","handsome","something"]
*Main> [adjective ++ " " ++ noun | adjective <-adjectives ,noun<-nouns]
["lazy he","lazy she","lazy I","handsome he","handsome she","handsome I","something he","something she","something I"]

45. 如果别人跟你说Haskell可以跟几何并轨?

第一次和几何的触碰

1
2
3
Prelude>  let triangels=[(a,b,c)|c<-[1..10],b<-[1..c],a<-[1..b],c^2==a^2+b^2,a+b+c==24]
Prelude> triangels
[(6,8,10)]

46. 如果别人问你Haskell的cabal是什么?

类似于ubuntu的apt-get
scilab 的atoms python的 easy_install 或者pip redhat 的yum

47. 如果别人问你有几个基本的TypeClass?

类型类是haskell中站在较高位置的语言认识。几种基本的类型类包括Eq,Ord,Show,Read,Enum等。

47.1 Eq

包含可判断相等性的类型。提供实现的函数是 == 和 /=。所以, 只要一个函数有Eq类的类型限制,那么它就必定在定义中用到了 == 和 /=

47.2 Ord

包含可比较大小的类型。除了函数以外,我们目前所谈到的所有类型都属于 Ord 类。Ord 包中包含了 <, >, <=, >= 之类用于比较大小的函数。compare 函数取两个 Ord 类中的相同类型的值作参数,回传比较的结果。这个结果是如下三种类型之一:GT, LT, EQ。

47.3 Show

的成员为可用字串表示的类型。目前为止,除函数以外的所有类型都是 Show 的成员。 操作 Show Typeclass,最常用的函数表示 show。它可以取任一Show的成员类型并将其转为字串

47.4 Read

是与 Show 相反的 Typeclass。read 函数可以将一个字串转为 Read 的某成员类型。

47.5 Enum

的成员都是连续的类型 — 也就是可枚举。Enum 类存在的主要好处就在于我们可以在 Range 中用到它的成员类型: 每个值都有后继子 (successer) 和前置子 (predecesor),分别可以通过 succ 函数和 pred 函数得到。 该 Typeclass 包含的类型有:(), Bool, Char, Ordering, Int, Integer, Float 和 Double。

47.6 Bounded

的成员都有一个上限和下限。

47.7 Num

Num 是表示数字的 Typeclass,它的成员类型都具有数字的特征。检查一个数字的类型:

47.8 Integral

同样是表示数字的 Typeclass。Num 包含所有的数字:实数和整数。而 Integral 仅包含整数,其中的成员类型有 Int 和 Integer。

47.9 Floating

仅包含浮点类型:Float 和 Double。 可以用一张图来表示: 类图

48. 我想用Haskell产生一个三角形

1
2
3
let triangels=[(a,b,c)|c<-[1..10],b<-[1..10],a<-[1..10]]
let righttriangels=[(a,b,c)|c<-[1..10],b<-[1..c],a<-[1..b],a^2+b^2==c^2]
let righttriangelsc=[(a,b,c)|c<-[1..10],b<-[1..c],a<-[1..b],a^2+b^2==c^2,a+b+c==24]

49. 如果别人问你如何表示数学的集合?

1
2
3
4
5
6
Prelude> [x*2|x <- [1..10]]
[2,4,6,8,10,12,14,16,18,20]
Prelude> [x*2|x<- [1..10],x*2 >=12 ]
[12,14,16,18,20]
Prelude> [x | x <- [50..100], x `mod` 7 ==3]
[52,59,66,73,80,87,94]

50. 如果别人问你!! 代表什么东西?

1
2
3
[3, 5,6] !!2
*Main> [3, 5,6] !!2
6

51. 几个常用的数组提取函数?

并且是从0开始的 常用几个list函数

1
2
3
4
5
6
7
8
*Main> head [5,6,4]
5
*Main> last [5,6,4]
4
*Main> tail [5,6,4]
[6,4]
*Main> init [5,6,6,43,6]
[5,6,6,43]

length null reverse take等函数 具体说一下take

1
2
3
4
5
6
7
*Main> take 4 [5,6,7,4,3,8]
[5,6,7,4]
*Main> take 3 [5,6,7,4,3,8]
[5,6,7]
*Main> take 2 [5,6,7,4,3,8]
[5,6]
*Main>

同理类似的drop