where
where,let,case
参考书籍: Learn you a haskell
where
用法
在编程时,我们希望避免重复计算同一个值,但我们也都知道,在Haskell中并不存在变量,那我们一般怎么存储呢?where便是我们常用的关键字。比如,我们现在实现一个计算语数英三科分数之和并判断等级的函数: 1
2
3
4
5
6
7
8judge :: Int -> String
judge chinese math english
| score<180 = "You got a D, foolish!"
| score<225 = "You got a C!"
| score<255 = "You got a B,not bad!"
| score<=300 = "Congratulation, you got an A!"
| otherwise = "Error score!"
truewhere score = chinese + math + englishwhere关键字放在哨位后面,并在后面提前将算出来的数赋值给另一个常量,这样便可以避免重复计算了。另外,where后可以写多个表达式,只需让它们在同一列就好了,如下: 1
2
3
4
5
6
7
8
9
10
11
12judge :: Int -> String
judge chinese math english
| score<bad = "You got a D, foolish!"
| score<fine = "You got a C!"
| score<good = "You got a B,not bad!"
| score<=great = "Congratulation, you got an A!"
| otherwise = "Error score!"
truewhere score = chinese + math + english
great = 300
good = 255
fine = 225
bad = 180
作用域
函数的where部分中定义的名字只对本函数可见,因此不需担心其污染其他函数的命名空间。但是,在模式匹配时where部分中定义的名字只在当前模式中可用,在函数的其他模式中不可用。
where中的模式匹配
在where中,我们也可以使用模式匹配来绑定,如之前的内容: 1
2
3
4......
true...
truewhere score = chinese + math + english
(great, good, fine, bad) = (300, 255, 225, 180)1
2
3
4initials :: String -> String -> String
initials firstname lastname = [f] ++ "." ++ [l] ++ "."
truewhere (f:_) = firstname
(l:_) = lastname
where中的函数
在where部分中,我们也可以定义函数,譬如我们定义一个函数,它可以取一个由三科成绩的元组组成的列表,并返回总分: 1
2
3judge :: [(Int,Int,Int)] -> [Int]
judge xs = [ add a b c | (a,b,c) <- xs]
truewhere add a b c = a + b + c
let
用法
let和where很相似。where允许我们在函数底部绑定变量,而let则允许在任何地方定义局部变量。其用法为let <bindings> in <expressions>,let中绑定的名字仅对in中可见。例如: 1
2
3
4average :: Int -> Int -> Int -> Int
average x y z =
truelet sum = x + y + z
in sum/3where差不多,但是let本身是一个表达式,意味着其有返回值,因此它可以实现以下操作: 1
2ghci> 4 * (let a = 9 in a + 1) + 2
421
2
3
4
5
6
7
8
9---在局部作用域中定义函数
ghci> [let square x = x * x in (square 5, square 3, square 2)]
[(25, 9, 4)]
---当一行需要绑定多个名字是,用;进行分开
ghci> [let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar)
(6000000, "Hey there!")
---结合模式匹配从元组中取出数值
ghci> (let (a, b, c) = (1, 2, 3) in a + b + c) * 100
600
列表推导式中的let
还是之前计算总成绩的函数,使用let写法如下: 1
2judge :: [(Int, Int, Int)] -> [Int]
judge xs = [ sum | (a, b, c) <- xs, let sum = a + b + c]
ghci中的let
直接在ghci中定义函数和常量是,let的in部分可以省略。如果省略,名字的定义将会在整个绘画过程中可见。
case
用法
Haskell中的case与其他语言中的类似,能够取一个变量,根据不同的值选择代码块执行。在Haskell中,这与模式匹配类似,实际上,模式匹配本质上便是case的语法糖。例如,以下两段代码是完全等价的: 1
2
3
4
5
6
7head' :: [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"
truetruetruetruetrue (x:_) -> xcase表达式的结构如下: 1
2
3
4case expression of pattern -> result
truetruetruetrue pattern -> result
pattern -> result
...case的使用十分灵活,可以在任何地方使用。比如,可以在表达式中套用它,来执行模式匹配,其与直接使用模式匹配是等价的。 1
2
3
4
5
6
7
8
9
10
11
12describeList :: [a] -> String
describeList ls = "The list is " ++ case ls of [] -> "empty."
truetruetruetruetruetruetruetruetruetruetrue [x] -> "a singleton list."
xs -> "a longer list."
---
describeList :: [a] -> String
describeList ls = "The list is " ++ what ls
truewhere what [] = "empty."
what [x] = "a singleton list."
what xs = "a longer list."