theme: juejin
判断表达式
if..then..else
表达式是编程语言中最常用到的基础之一,本片让我们来看看在 Haskell 中表达式是怎样的?
先看个小例子感受一下(依然是借助编译器 GHC):
代码语言:javascript复制Prelude> isTwo n = if n==2 then True else False
Prelude> isTwo 2
True
Prelude> isTwo 3
False
Prelude> :t isTwo
isTwo :: (Eq a, Num a) => a -> Bool
这是一个非常简单的 if..then..else 表达式,isTwo 是一个函数,n 是入参;可以看到,Haskell 的表达式并没有像在 JS 中的括号进行包裹;
当然,你也可以写像 JS 中的等号运算符;
代码语言:javascript复制Prelude> isFive = (==5)
Prelude> isFive 5
True
Prelude> :t isFive
isFive :: (Eq a, Num a) => a -> Bool
和 JS 还有一个大不同是:Hskell 里的 if..then..else 的 else 后的表达式不可省略;
也就是说,必须定义条件成立的时候返回的值,也必须定义条件不成立的时候返回的值,并且两者返回的类型必须相同,这样一定程度上保证了函数定义的完整性。
实际上,if..then..else 是一种结构性的表达式,也可以理解为一种运算符,属于:混合位置运算符;
而普通的加法,处于两个参数中间,称为:中缀运算符;
函数,位于一个参数前面,可理解为:前缀运算符;
函数式编程的“输入 => 计算 => 得值”的思想处处都有体现;
switch
看了 if else ,再看看 switch 怎么写:
代码语言:javascript复制Prelude> :{
Prelude| week n = case n of
Prelude| 1 -> "Mon"
Prelude| 2 -> "Tues"
Prelude| 3 -> "Wed"
Prelude| 4 -> "Thurs"
Prelude| 5 -> "Fri"
Prelude| 6 -> "Sat"
Prelude| 7 -> "Sun"
Prelude| _ -> error "invalid"
Prelude| :}
Prelude> week 4
"Thurs"
Haskell 中无需 break 关键字,当它匹配到一个条件后,就会自动跳出;
_
下划线是定义默认的其它条件;
模式匹配
还有另一种方式可以表达条件运算 —— 模式匹配;
代码语言:javascript复制Prelude> :{
Prelude| abs4 n
Prelude| | n > 4 = n
Prelude| | otherwise = -n
Prelude| :}
Prelude> abs4 2
-2
Prelude> :t abs4
abs4 :: (Ord p, Num p) => p -> p
|
将函数的参数按特定的条件分开;
在模式匹配中,更精确更有指向性的模式总是放在相对通用和宽泛的模式前面(优先匹配);
本瓜觉得跟这里的 模式匹配 跟 责任链模式 有点类似,按照顺序去匹配,把更有可能正确的条件判断放在最前,优先去执行判断,满足条件立即跳出;
不过 JS 实现责任链要进行封装,Haskell 直接原生语法就支持,|
就是对 if..then..else 的封装;
运算符
前文已提到:加号、减号等,这些本质和函数是一样的,函数也是运算符,加减号也是函数!
可以在 GHC 控制台打印类型看看:
代码语言:javascript复制Prelude> :t ( )
( ) :: Num a => a -> a -> a
Prelude> :t (-)
(-) :: Num a => a -> a -> a
只不过它们属于不同位置的运算符(前缀、中缀、后缀、混合位置);
实际上,运算符共有 3 个属性:
- 优先级(在 Haskell 中,有十个优先级(0 ~ 9));
- 结合性(分为左结合、右结合、无结合);
- 位置(前、中、后、混合);
提供一个优先级和结合性的表:
图片来源:异步社区
比如运算符 !!
:表示从一个列表中取出第 n 个元素(从 0 开始)
Prelude> [1,2,3,4,5]!! 1
2
再比如 mod
:表示取余
Prelude> mod 7 2
1
有一个很重要的运算符要特别提醒:$
Prelude> :t ($)
($) :: (a -> b) -> a -> b
用来干嘛的呢?
当你想定义 f (g (h x)) 时,可以简写为 f g h x ,这样写函数的连续调用更轻便、易读;
代码语言:javascript复制Prelude> let f1 = (*2)
Prelude> let f2 = ( 1)
Prelude> f1 $ f2 7
16
自右向左调用,回答了之前 compose 自右向左调的原因:与函数书写的嵌套顺序一致;
、!、
小结
本篇我们又学习了 Haskell 的新的知识点:
- if else 是怎么写的,与 JS 差异在哪;
- switch 是怎么写的,与 JS 差异在哪;
- 模式匹配(与责任链模式类似);
- 函数与运算符等价(非常重要);
- 运算符的三个属性;
- 一些特殊的运算符,比如:
!!
、$
等;
这些都是为后面揭开 Haskell 函数式编程神秘面纱的基础,期间也能一窥这种把函数当计算的奇妙之处,即使不能在开发生产中用到 Haskell,对于平常的编程思考也是大有裨益的,希望你有受用到~~
以上。
我是掘金安东尼,公众号同名,输出暴露输入,技术洞见生活,再会~