「Learn Haskell」#1 基础语法与函数

基础运算

  • + - * / ():加减乘除
  • div:整除
  • mod:取模
  • True False:布尔值
  • || && not:或且非
  • ==:条件判断,相等
  • /=:条件判断,不等

函数调用

Haskell中调用函数不加括号,先写出函数名,然后逐个列出参数,用空格隔开:

1
2
ghci> max 1 2
2

前缀(prefix)函数与中缀(infix)函数转换:

  • 对前缀函数加``使其变成中缀函数
  • 对中缀函数加()使其变成前缀函数
1
2
3
4
5
6
7
8
ghci> 4 `div` 2
2
ghci> 1 `max` 2
2
ghci> (+) 1 2
3
ghci> (||) True False
True

List

列表是Haskell中很常见的数据类型,和Python中不同,Haskell中的列表中的所有元素必须是同一个类型。

以下是列表常用的函数:

  • (++) :: [a] -> [a] -> [a]:合并两个列表
  • (:) :: a -> [a] -> [a]:将单个元素并入列表。[1, 2, 3]是1:2:3:[]的语法糖
  • (!!) :: [a] -> Int -> a:通过索引取出某个位置上的元素。a !! 1相当于Python中的a[1]
  • head :: [a] -> a:返回列表的第一个元素
  • tail :: [a] -> [a]:返回列表中除去第一个元素后的列表(若只有一个元素则返回空列表[])
  • last :: [a] -> a:返回列表中的最后一个元素
  • init :: [a] -> [a]:返回列表中除去最后一个元素后的列表
  • length :: Foldable t => t a -> Int:返回列表的长度
  • null :: Foldable t => t a -> Bool:返回列表是否为空
  • reverse :: [a] -> [a]:返回翻转后的列表
  • take :: Int -> [a] -> [a]:返回列表a的前n个元素的列表(take n a)
  • drop :: Int -> [a] -> [a]:返回列表a中除去前n个元素后的列表(drop n a)
  • maximum :: (Foldable t, Ord a) => t a -> a:返回列表中的最大值
  • minimum :: (Foldable t, Ord a) => t a -> a:返回列表中的最小值
  • sum :: (Foldable t, Num a) => t a -> a:返回列表中所有元素的和
  • product :: (Foldable t, Num a) => t a -> a:返回列表中所有元素的积
  • elem :: (Foldable t, Eq a) => t a -> Bool:判断值n是否在列表a中(
    1
    2
    3
    elem n a
    -- 或
    n `elem` a --用``包上可以变成中缀函数使用

Texas ranges

使用..可以表示出范围并自动推导:

1
2
3
4
5
6
7
8
9
10
11
12
ghci> [1 .. 10]  
[1,2,3,4,5,6,7,8,9,10]
ghci> ['a' .. 'z']
"abcdefghijklmnopqrstuvwxyz"
ghci> ['K' .. 'Z']
"KLMNOPQRSTUVWXYZ"
ghci> [2, 4 .. 20]
[2,4,6,8,10,12,14,16,18,20]
ghci> [3, 6 .. 20]
[3,6,9,12,15,18]
ghci> [5, 4 .. 1]
[5,4,3,2,1]

也可以用来生成无穷列表,如[1..]、[1, 3..]。同时也有函数可以生成无穷列表:

  • cycle :: [a] -> [a]:将原列表不断循环生成无穷列表
  • repeat :: a -> [a]:将传入的值不断重复生成无穷列表
    • replicate :: Int -> a -> [a]:将值a重复n次,返回生成的列表(replicate n a)

List comprehension

Haskell中也有列表推导,形式是一个中括号,左侧为表达式,右侧为变量的范围和约束条件

1
2
3
4
5
6
7
8
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> [ x | x <- [50 .. 100], x `mod` 7 == 3]
[52,59,66,73,80,87,94]
ghci> [x * y | x <- [2, 5, 10], y <- [8, 10, 11]]
[16,20,22,40,50,55,80,100,110]

Tuple

Haskell中的元组可以有不同长度,元素可以有不同类型。并且一个元组的类型由其中所有元素的类型共同决定。它的常用函数:

  • fst :: (a, b) -> a:返回含有两个元素元组中的第一个元素
  • snd :: (a, b) -> b:返回含有两个元素元组中的第二个元素
  • zip :: [a] -> [b] -> [(a, b)]:接收两个列表,返回一个列表,每个元素是依次将两个列表中元素配对成的二元组

Syntax in Functions

函数可以直接定义:

1
plus x y = x + y

这时Haskell会自动推断函数的类型为(Num a) => a -> a -> a。但是最好在定义函数前声明函数的类型:

1
2
plus :: (Num a) => a -> a -> a
plus x y = x + y

Pattern matching

定义函数时可以使用模式匹配语法。运行时依次将输入与给出的模式相匹配,如果匹配,就执行对应操作;不匹配,就继续与下一个模式相匹配,直到匹配成功,也因此,最后必须要给出一种通用的匹配来接收与给出模式全不匹配的输入。如:

1
2
3
factorial :: (Integral a) => a -> a  
factorial 0 = 1
factorial n = n * factorial (n - 1)
1
2
3
4
5
6
7
8
first :: (a, b, c) -> a  
first (x, _, _) = x

second :: (a, b, c) -> b
second (_, y, _) = y

third :: (a, b, c) -> c
third (_, _, z) = z

其中_表示任何值,且不关心它的内容,只是用来占位

列表的(:)操作也可以用来进行模式匹配:

1
2
3
4
5
6
7
head' :: [a] -> a  
head' [] = error "Can't call head on an empty list, dummy!"
head' (x:_) = x

sum' :: (Num a) => [a] -> a
sum' [] = 0
sum' (x:xs) = x + sum' xs

但(++)操作不可以用来模式匹配

在针对列表进行模式匹配时,如果同时需要整个列表、列表的第一个值、列表除第一个值外的内容,可以使用xs@(q:qs)。比如[1, 2, 3]通过xs@(q:qs)匹配后,xs为[1, 2, 3],q为1,qs为[2, 3]

Guard syntax

在函数的定义中,也可以使用守卫(guard)语法:

1
2
3
4
max' :: (Ord a) => a -> a -> a  
max' a b
| a > b = a
| otherwise = b

先给出传入的参数变量,然后下一行缩进后加|,|后面等号前表示进行的判断,如果为True则返回这个等号后面的值;如果为False则继续判断下一行,直到otherwise

Case expressions

在函数的定义中,也可以使用case表达式来配合模式匹配使用:

1
2
3
case expression of pattern -> result  
pattern -> result
...

例如:

1
2
3
4
5
6
7
head' :: [a] -> a  
head' [] = error "No head for empty lists!"
head' (x:_) = x
-- 等价于:
head' :: [a] -> a
head' xs = case xs of [] -> error "No head for empty lists!"
(x:_) -> x
1
2
3
4
5
6
7
8
9
10
describeList :: [a] -> String  
describeList xs = "The list is " ++ case xs of [] -> "empty."
[x] -> "a singleton list."
xs -> "a longer list."
-- 等价于:
describeList :: [a] -> String
describeList xs = "The list is " ++ what xs
where what [] = "empty."
what [x] = "a singleton list."
what xs = "a longer list."

where

声明在函数定义中要使用的局部变量,可以使用where关键字:

1
2
3
4
initials :: String -> String -> String  
initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
where (f:_) = firstname
(l:_) = lastname

在where中,也可以使用上面的模式匹配

let

let <bindings> in <expression>语法可以在函数的定义中使用,也可以在普通算式或列表中使用:

1
2
3
4
5
cylinder :: (RealFloat a) => a -> a -> a  
cylinder r h =
let sideArea = 2 * pi * r * h
topArea = pi * r ^2
in sideArea + 2 * topArea
1
2
3
4
ghci> 4 * (let a = 9 in a + 1) + 2  
42
ghci> [let square x = x * x in (square 5, square 3, square 2)]
[(25,9,4)]

if statement

Haskell中的if语句为:

1
2
3
4
5
6
7
if ... then ...
else ...
-- or if ... then ... else ...
-- or
if ... then ...
else if ... then ...
else ...

其中最后一个else无论如何也不可以省去

Reference


目录

#0 | 总章        
#1 | 基础语法与函数   
#2 | 高阶函数与模块   
#3 | 类型与类型类    
#4 | 输入输出与文件   
#5 | 函子、应用函子与单子
#6 | 半群与幺半群    
#7 | 一些其它类型类   
#A | Haskell与范畴论   

「Learn Haskell」#1 基础语法与函数

https://blog.tonycrane.cc/p/d63b5b5f.html

作者

TonyCrane

发布于

2021-07-07

更新于

2021-10-21

许可协议