小白入门Haskell 语言
Haskell 语言
安装
因为我是 Windows 系统,在这里下载一个 GHC for Win 后解压缩,将解压缩后的目录中的 bin 添加到环境变量的 Path 中,在命令行中输入 ghci
就可用交互式的了。
其中 bin 目录下有一个 runhaskell.exe 文件,我们对一个后缀为 .hs 的文件应用这个可执行文件后,就能写下面的代码了,而不是在 ghci
中交互,虽然那样学习更方便。
学习
四则运算
值得一提的是除法,我们可以写出如下 main
函数并打印结果,如
main = do print (5 / 2)
结果为 2.5
而不是 Cpp 中的 2
,如果我们需要欧几里得除法则可以这样写
main = do print (5 `div` 2) -- div 5 2 也是一样的
结果为 2
而取模也不是通常的 %
符号而是
main = do print (5 `mod` 2) -- mod 5 2 也是一样的
布尔值
用 True
和 False
表示,用 not
表示取反, &&
表示逻辑与, ||
表示逻辑或,如
main = do
let c = 10
if not (c > 11)
then putStr "c <= 10"
else putStr "c > 11"
需要注意括号的位置,基本上我们可以认为 not
是一个函数。
比较两个数
main = do
let c = 10
if 5 /= c
then putStr "c not eqaul to 5"
else putStr "c is 5"
注意这里的不等于使用的是 a /= b
而不是 a != b
,而等于则为 a == b
与其他语言是类似的。
一些基本函数
main = do print (succ 8) -- 9
main = do print (min 10 11) -- 10
main = do print (max 10 11) -- 11
函数的一些顺序有下面这个例子
main = do print (succ 9 + max 5 4 + 1)
这等价于
main = do print ((succ 9) + (max 5 4) + 1)
编写自己的函数
doubleMe x = x + x
main = do
print (doubleMe 9) -- 18
squareMe x = x * x
main = do
print (squareMe 9) -- 81
当然也可以用小数调用这些函数如
doubleMe x = x + x
main = do
print (doubleMe 9.1) -- 18.2
也就是说我们写的函数实际上是一个泛型的函数!这很棒,对于限制类型后面会提到。
多参数的函数
addxy x y = x + y
main = do
print (addxy 7 8) -- 15
带有 if
的函数
plusOne x = if x == 0 then x + 1 else -1
main = do
print (plusOne 1)
print (plusOne 0)
当然我们可以调整一些缩进写成
plusOne x =
if x == 0
then x + 1
else -1
main = do
print (plusOne 1)
print (plusOne 0)
这样使得代码更具有可读性。
list
我不清楚是否应该称呼其为数组,我们可以写出这样的代码
main = do
let c = [1,2,3,4]
print c -- [1,2,3,4]
而我们常用的字符串如 "abc"
实际上为 ['a','b','c']
的语法糖,这与在 C 语言中有点类似,也就是说
main = do
print ("abc" == ['a','b','c']) -- True
是毫无疑问的。
对于 list 拼接也显得很自然,有
c = [1,2,3] ++ [2,3,4]
main = do
print c -- [1,2,3,2,3,4]
也有这样的拼接方法如
a = 10
b = [1,2,3]
main = do
print (a : b) -- [10,1,2,3]
下标访问,在 C 语言中我们常常会这样写
#include <stdio.h>
int main(void) {
int a[10];
printf("%d", a[1]); // 未初始化,值可能为随机
return 0;
}
而在 Haskell 中则是
a = "abcdefg"
main = do
putChar (a !! 2) -- c
我们发现其索引开始也是 0
,这与一般的习惯很符合。
值得注意的是, list 之间也可以通过 <
、 >
、 >=
、 <=
比较,结果好像为字典序(但一般很少用到)。
head
函数只取首个元素
c = [1,2,3,4,5]
main = do
print (head c) -- 1
tail
函数为除了首个元素的其余元素的 list
c = [1,2,3,4,5]
main = do
print (tail c) -- [2,3,4,5]
last
函数为最后一个元素
c = [1,2,3,4,5]
main = do
print (last c) -- 5
init
函数为除了最后一个元素的其余元素的 list
c = [1,2,3,4,5]
main = do
print (last c) -- [1,2,3,4]
注意, head
不能应用于空的 list ,其余函数我没有测试。
length
函数返回 list 的长度
main = do
print (length [1,2,3]) -- 3
print (length []) -- 0
null
函数返回一个布尔值表示 list 是否为空
main = do
print (null []) -- True
print (null [1]) -- False
reverse
函数返回一个翻转的 list
c = [1,2,3]
main = do
print (reverse c) -- [3,2,1]
上面所说的函数全都是一个参数的函数,且参数都为 list ,下面的函数为两个参数,具体看示例为
take
函数表示截取前几个元素组成新的 list
c = [1,2,3,4,5]
main = do
print (take 3 c) -- [1,2,3]
print (take 0 c) -- []
print (take 8 c) -- [1,2,3,4,5]
drop
函数表示丢弃前几个元素组成新的 list
c = [1,2,3,4,5]
main = do
print (drop 3 c) -- [4,5]
print (drop 0 c) -- [1,2,3,4,5]
print (drop 100 c) -- []
maximum
函数与 minimum
函数想必不必多说
c = [1,2,3,4,5]
main = do
print (maximum c) -- 5
print (minimum c) -- 1
sum
函数返回和
c = [1,2,3]
main = do
print (sum c) -- 6
product
函数返回乘积
c = [1,2,3]
main = do
print (product c) -- 6
elem
函数告诉我们这个元素是否在 list 中
c = [1,2,3]
main = do
print (1 `elem` c) -- True
print (-1 `elem` c) -- False
通常用一个中缀表达式来写,当然也可以写作 elem 1 c
,因为更自然,与之前的 div
和 mod
是类似的。
范围的 list 可以这样写
main = do
print [1..5] -- [1,2,3,4,5]
print [2,4..10] -- [2,4,6,8,10]
print [3,6..20] -- [3,6,9,12,15,18]
但是不能写 [5..1]
而必须写 [5,4..1]
,我们可以理解为等差数列前两项以及最后一项,但小数的时候不同
main = do
print [0.1,0.3 .. 1] -- [0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]
cycle
函数可以将一个东西无限制的拼接如
main = do
print (take 10 (cycle [1,2,3])) -- [1,2,3,1,2,3,1,2,3,1]
于是我们只能用 take
函数将其取出部分。
repeat
函数则有
main = do
print (take 10 (repeat 5)) -- [5,5,5,5,5,5,5,5,5,5]
也有更方便的函数 replicate
可以做到
main = do
print (replicate 3 10) -- [10,10,10]
接下来是一个很厉害的工具,可以制作我们想要的 list 形态,例如我想要 { x ∣ 0 ≤ x ≤ 20 , x m o d 3 = 1 , x ∈ Z } \{x\mid 0\leq x\leq 20,x\bmod 3=1,x\in\mathbb{Z}\} {x∣0≤x≤20,xmod3=1,x∈Z} 的 list 可以写
main = do
print [x | x <- [0..20], x `mod` 3 == 1]
这甚至与数学中很类似。
我们也可以写函数接收一个 list 返回出想要的 list 如
weWant xs = [x | x <- xs, odd x]
main = do
print (weWant [0,1,7,9]) -- [1,7,9]
上面用到了函数 odd
同样的也有 even
。
不止如此,我们也可以写出
prodEachElem xs ys = [x * y | x <- xs, y <- ys]
main = do
print (prodEachElem [1,2,3] [4,5,6]) -- [4,10,18]
用数学表达则为 x = { 1 , 2 , 3 } , y = { 4 , 5 , 6 } , z = { a b ∣ a ∈ x , b ∈ y } x=\{1,2,3\},y=\{4,5,6\},z=\{ab\mid a\in x,b\in y\} x={1,2,3},y={4,5,6},z={ab∣a∈x,b∈y} 很自然。
回顾之前的函数 length
我们可以用 sum
写一个自己的版本为
length' x = sum [1 | _ <- x]
main = do
print (length' []) -- 0
print (length' [1,2,3]) -- 3
他可以正常的工作,其中下划线 _
代表我们并不关心这个是什么。
tuple
中文一般为元组。元组一般为 (a,b)
这样的形式。
fst
函数返回元组中的首个元素,而 snd
返回第二个。
main = do
print (fst (1,"WWW")) -- 1
print (snd (2,"WWWA")) -- "WWWA"
有一个很酷的函数为 zip
可以制作元组如
main = do
print (zip [1,2,3] ["A","B","C"]) -- [(1,"A"),(2,"B"),(3,"C")]
print (zip [1..3] ['A','B','C']) -- [(1,'A'),(2,'B'),(3,'C')]
print (zip [1..] ['A','B']) -- [(1,'A'),(2,'B')]
可以看到是以 length
较短的标准制作的,这样的话写出 [1..]
这样看似无穷多的 list 也变得合理了(惰性?)。
当然,元组也远远不止两个元素,也可以写出
d = [(a,b,c) | a <- [1,3..10], b <- [2,4..7], c <- [7,11..18]]
main = do
print d
注意这里并不是 zip
函数,所以不要写无穷无尽的,我们可以将这里视为三重的循环体。刚刚出了事故我的电脑死机了,看来这个编译器会一丝不苟的执行下去呢,并没有检测是否会造成死循环。
类型
参考资料
[1] http://learnyouahaskell.com/chapters
文章来源: fantianzuo.blog.csdn.net,作者:兔老大RabbitMQ,版权归原作者所有,如需转载,请联系作者。
原文链接:fantianzuo.blog.csdn.net/article/details/126587376
- 点赞
- 收藏
- 关注作者
评论(0)