热爱函数式的你,句句纯正的 Haskell【表达式篇】

2022-09-19 11:11:12 浏览数 (1)


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 个属性

    1. 优先级(在 Haskell 中,有十个优先级(0 ~ 9));
    1. 结合性(分为左结合、右结合、无结合);
    1. 位置(前、中、后、混合);

提供一个优先级和结合性的表:

图片来源:异步社区

比如运算符 !! :表示从一个列表中取出第 n 个元素(从 0 开始)

代码语言:javascript复制
Prelude> [1,2,3,4,5]!! 1
2

再比如 mod :表示取余

代码语言:javascript复制
Prelude> mod 7 2
1

有一个很重要的运算符要特别提醒:$

代码语言:javascript复制
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 的新的知识点:

  1. if else 是怎么写的,与 JS 差异在哪;
  2. switch 是怎么写的,与 JS 差异在哪;
  3. 模式匹配(与责任链模式类似);
  4. 函数与运算符等价(非常重要)
  5. 运算符的三个属性;
  6. 一些特殊的运算符,比如:!!$ 等;

这些都是为后面揭开 Haskell 函数式编程神秘面纱的基础,期间也能一窥这种把函数当计算的奇妙之处,即使不能在开发生产中用到 Haskell,对于平常的编程思考也是大有裨益的,希望你有受用到~~

以上。

我是掘金安东尼,公众号同名,输出暴露输入,技术洞见生活,再会~

0 人点赞