表达式
当然,不管何种奇葩语言,小学数学的中缀表达式总是能用的。在ghci
中可以试试
Prelude> 5 / 2
2.5
Prelude> (50 * 100) - 4999
1
Prelude> not (True && True)
False
Prelude> 1 /= 5
True
Prelude> 1 == 5
False
一切跟你学过的一样。值得一提的是,Haskell中只有一个一元运算符-
,没有+
。然而-
有奇怪的语义。5 * -3
不合语法,但5 * (-3)
就可以。
Haskell有一个重要的特性是静态类型。C/C++也是静态强类型的,所以如此稳重而难写。
函数调用也跟初中数学学的一样用前缀表达式。只是没有括号和逗号分隔参数。比如这样
Prelude> succ 8
9
Prelude> min 3.4 3.2
3.2
Prelude> max 100 101
101
这种形式的函数调用具有最高优先级。比如下面两个式子是一样的
Prelude> succ 9 + max 5 4 + 1
16
Prelude> (succ 9) + (max 5 4) + 1
16
中缀表达式和前缀表达式没有什么区别,操作符就是函数。两种形式可以相互变换
Prelude> 5 + 3
8
Prelude> (+) 5 3
8
Prelude> 5 `div` 3
1
Prelude> div 5 3
1
悲伤的是,Haskell中不存在像
变量(函数)
Haskell中,变量就是对应一个表达式的名字。
Prelude> x = 1
1
Prelude> pi * x ** 2
3.141592653589793
这里r
和pi
都是变量,pi
是内置的。
作为一种函数式编程语言,Haskell中的Variable根本不vary,严格来说是immutable,即不可修改的。这与命令式编程语言有巨大的不同,在C/C++中变量指的是计算机内存中的一个可以放东西的位置,而Haskell中变量就是数学上的一个表示值的符号。在C中你可以这样
int i = 0;
i = i + 1;
但是数学书这样写到底是什么意思呢?
Haskell中的函数也是数学意义上的函数,即纯函数。数学中函数是输入到输出的映射,在这里说明白点,为了跟命令式编程决裂,纯函数满足
- 一样的参数总是得到一样的返回值,与数学中的定义吻合。
- 求值没有任何外面可见的副作用。
比如计算圆面积的函数
Prelude> circleArea r = pi * r ** 2
Prelude> circleArea 2
12.566370614359172
Prelude> add x y = x + y
Prelude> add 1 2
3
Prelude> abs x = if x >= 0 then x else -x
Prelude> abs 0.5
0.5
Prelude> abs (-1)
1
参数直接用空格分隔即可。当然用括号分割的参数也是一种变量,只在函数里面可见。
函数是Haskell里的一等公民。你可以到处看到它,到处使用它。函数可以作为别的函数的参数或者返回值,也可以当变量使。前面看到运算符就是某种函数,再举一个不太恰当的例子,
Prelude> x = 1
1
我说这是个没有参数的函数。对吗?如果变量不可修改,函数都是纯函数,那么这有什么区别么?
如果你以前没学过编程,这一切应该在正常不过了。不幸的是,我学过,所以现在感觉非常别扭。变量不能修改的语言肯定能力不足吧?恰恰相反,到这我刚好想到几点好处。
- Immutable的性质让很多C/C++中恶心的问题不成问题,
const
、指针、引用都是都没啥意义,++
、--
的恶心玩意不存在了。 - 纯函数有引用透明性,就是说你知道
f 2 3
结果是2
,任何地方f 2 3
就可以换成2
而没有别的变化。这导致了你可以放心大胆的替换各处式子而不改变结果。记不记得多少次你程序稍微那么一改就跑飞了? - 纯函数没有副作用,不能偷偷修改什么全局变量、静态变量。很多bug的来源一开始就不存在了。这可能是上一条推论。
然而也可以想到一些不太对的地方
- IO不能是纯函数吧?你不能指望用户输入被你的参数确定,也不能妄称你的输出不是副作用。
- 随机数呢?也不可能纯函数啊!咦,不对,我们用的是伪随机数生成器。定下来种子,剩下的就是数学了
高屋建瓴的瞎扯
我不是学CS的,我是EE狗,我不懂理论计算机科学。但我觉得函数式编程和命令式编程最本质的区别是他们对应的计算模型不同。对于我们门外汉来说,图灵机大概是我们对这个的全部理解,稍微变一下可能还知道RAM模型,但这绝不是全部,半小时前我也就知道这些。
就我们这里关心的来说,命令式编程将被完成计算的东西视为RAM模型,我们从内存中一步一步地读取东西算一算再写回去。之所以如此假设,应该不是因为这是人的思考方式,而是因为机器是这么算的——至少我们造得出来的计算机就是如此工作的。EE用时序数字电路造了计算机,用FSM近似一下图灵机,数十年的演化导致编程就是命令式的了 但这不是“计算”的全部。
函数式编程将完成计算的东西视为λ-演算,他跟RAM模型、FSM、图灵机不是一个套路,没有时序,没有状态。通过变量标识、函数抽象、函数应用用易于读写的方式描述了计算本身。变量就是用一个名字绑定一个值,函数定义就是一个表达式,函数应用就是替换来替换去的变形化简。这应该是计算最“人类”的方式,基于物理实现去描述很反人类。但在编程里却反过来了
本质上,图灵机和λ-演算是一致的,这是Church–Turing thesis的结果。当然证明是证明不了的,没了图灵,我都不知道啥是计算……