发布于 

where

where,let,case

参考书籍: Learn you a haskell

where

用法

在编程时,我们希望避免重复计算同一个值,但我们也都知道,在Haskell中并不存在变量,那我们一般怎么存储呢?where便是我们常用的关键字。比如,我们现在实现一个计算语数英三科分数之和并判断等级的函数:

1
2
3
4
5
6
7
8
judge :: 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 + english
也就是说,我们可以将where关键字放在哨位后面,并在后面提前将算出来的数赋值给另一个常量,这样便可以避免重复计算了。另外,where后可以写多个表达式,只需让它们在同一列就好了,如下:
1
2
3
4
5
6
7
8
9
10
11
12
judge :: 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
4
initials :: String -> String -> String
initials firstname lastname = [f] ++ "." ++ [l] ++ "."
truewhere (f:_) = firstname
(l:_) = lastname

where中的函数

where部分中,我们也可以定义函数,譬如我们定义一个函数,它可以取一个由三科成绩的元组组成的列表,并返回总分:

1
2
3
judge :: [(Int,Int,Int)] -> [Int]
judge xs = [ add a b c | (a,b,c) <- xs]
truewhere add a b c = a + b + c

let

用法

letwhere很相似。where允许我们在函数底部绑定变量,而let则允许在任何地方定义局部变量。其用法为let <bindings> in <expressions>let中绑定的名字仅对in中可见。例如:

1
2
3
4
average :: Int -> Int -> Int -> Int
average x y z =
truelet sum = x + y + z
in sum/3
其看上去与where差不多,但是let本身是一个表达式,意味着其有返回值,因此它可以实现以下操作:
1
2
ghci> 4 * (let a = 9 in a + 1) + 2
42
以下是几个常见用法:
1
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
2
judge :: [(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
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"
truetruetruetruetrue (x:_) -> x
case表达式的结构如下:
1
2
3
4
case expression of pattern -> result
truetruetruetrue pattern -> result
pattern -> result
...
此外,case的使用十分灵活,可以在任何地方使用。比如,可以在表达式中套用它,来执行模式匹配,其与直接使用模式匹配是等价的。
1
2
3
4
5
6
7
8
9
10
11
12
describeList :: [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."


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本站由 [@Songer](https://blog.songer.xyz/) 创建,使用 Stellar 作为主题。