ocaml快餐教程(2) - 定义变量与函数

ocaml快餐教程(2) - 定义变量与函数

上一节我们主要介绍了ocaml的基本数据类型。在这些基本数据类型的基础上,我们可以组成更复杂的数据结构,比如我们用逗号分隔一些类型的值,就构成了元组。

比如元组(1, 10.),就一个int * float类型的元组。

# (1,10.);;
- : int * float = (1, 10.)

再例如,由int64, int32, nativeint, int, float构成的元组:

# 1L, 1l, 1n, 1, 1. ;;
- : int64 * int32 * nativeint * int * float = (1L, 1l, 1n, 1, 1.)

我们再来个-1开会:

# Int64.minus_one, Int32.minus_one, Int.minus_one, Nativeint.minus_one ;;       
- : int64 * int32 * int * nativeint = (-1L, -1l, -1, -1n)

变量绑定与模式匹配

我们可以通过let语句,将一个表达式绑定给一个变量,如:

# let a = 1,1l ;; 
val a : int * int32 = (1, 1l)

针对于两个元素构成的元组,我们可以通过fst和snd函数来获取元素的值,例:

# let a = 1,1l ;; 
val a : int * int32 = (1, 1l)
# let a1 = fst a;;
val a1 : int = 1
# let a2 = snd a;;
val a2 : int32 = 1l

超过两个元素的元组,没法使用fst和snd函数。但是我们可以在let左边也用变量元组去绑定元组的元素,这样的操作称为模式匹配。

# let a3,a4,a5 = 3, 4., 5n ;;
val a3 : int = 3
val a4 : float = 4.
val a5 : nativeint = 5n

我们也可以通过and的方式进行联合赋值:

# let a6 = 6n and a7 = 7l and a8 = 8. ;;
val a6 : nativeint = 6n
val a7 : int32 = 7l
val a8 : float = 8.

在模式匹配中,如果有的值不需要绑定变量,我们可以赋给"_",例:

# let a9, _ , a11 = 9e0, 10e-10, 11. ;;
val a9 : float = 9.
val a11 : float = 11.

10e-10的值就直接被pass掉了。

用let定义函数

如果绑定的值带有参数,那么就是定义了一个函数。
我们来看个例子:

# let s3 x = x * x * x ;;
val s3 : int -> int = <fun>
# s3 8 ;;
- : int = 512

这时候发现.区分开的好处了吧,类型容易被推导出来:

# let a x = x *. x ;;
val a : float -> float = <fun>

这时候ocaml面向对象的特性带来加成,有些不用系统,我们都推导的出来:

# let s_2 x = Float.abs x ;;
val s_2 : float -> float = <fun>
# s_2 1e8;;
- : float = 100000000.

用let定义的函数,也可以支持多个参数,比如我们定义一个算加法的函数:

# let add2 x y = x +. y ;;
val add2 : float -> float -> float = <fun>
# add2 100. 200. ;;
- : float = 300.

我们也可以用let来定义柯里化的函数。柯里化要求每次只能有一个参数,如果有多个参数,那么处理一个之后,返回值是另一个函数。
比如上面的add2需要x和y两个参数,我们给定一个值,比如说是2,那么就变成了一个专门加2的函数了:

# let add2_2 = add2 2. ;;
val add2_2 : float -> float = <fun>
# add2_2 100. ;;
- : float = 102.

function和fun

上面的定义方面没什么不好的,如果觉得跟前面讲的let绑定变量的方法不太一致,想要let f = <函数定义> 这种方式的话,可以使用function关键字来定义:

# let s2 = function x -> x *. x ;;
val s2 : float -> float = <fun>
# s2 1e-1 ;;
- : float = 0.0100000000000000019

function的好处是,它是柯里化的,只能支持一个参数。如果想要支持多个参数的话,我们可以嵌套。

我们来看个例子:

# let mul2 = function x -> (function y -> x *. y) ;;
val mul2 : float -> float -> float = <fun>
# mul2 1e8 1e-4 ;;
- : float = 10000.

另外,我们可以把参数变成元组,这样看起来可能比较像其它语言的函数:

# let div2 = function (x,y) -> x /. y ;;
val div2 : float * float -> float = <fun>

调用时需要给元组:

# div2 (1e8,-4.) ;;
- : float = -25000000.

最后,fun可以定义多参数的函数:

# let addx = fun x1 x2 x3 -> x1 + x2 +x3 ;;
val addx : int -> int -> int -> int = <fun>
# addx 1 2 3 ;;
- : int = 6

高阶函数

高阶函数,通俗地讲,就是不但值可以做为参数,函数也可以做为参数。

比如,我们想定义一个两个数的操作的函数,我们这样写:

# let higherf ops x y = ops x y ;;
val higherf : ('a -> 'b -> 'c) -> 'a -> 'b -> 'c = <fun>

这下我们就拥有泛型的能力了,比如针对整数加法,可以用Int.add做为ops,nativeint就用Nativeint.add,浮点数就用Float.add:

# higherf Int. add 1 2 ;;
- : int = 3
# higherf Nativeint.add 1n 2n ;;
- : nativeint = 3n
# higherf Float.add 1. 2. ;;
- : float = 3.

我们再举个例子,比如三角函数计算时都要求使用弧度,我们是不是可以将角度转弧度的过程能够应用于所有的三角函数呢?
我们可以这么写:

# let deg ops x = ops ( x *. Float.pi /. 180.) ;;
val deg : (float -> 'a) -> float -> 'a = <fun>

这样,我们再求角度的三角函数时,就可以通过deg sin, deg cos这样的方式去调用了:

# deg sin 90. ;;
- : float = 1.
# deg sin 45. ;;
- : float = 0.707106781186547462
# deg cos 180. ;;
- : float = -1.
# deg tan 90. ;;
- : float = 16331239353195370.
# deg tan 45. ;;
- : float = 0.999999999999999889

小结

小结下,我们可以用let来绑定变量,也可以来定义函数。可以直接定义函数,也可以通过function和fun来定义。推荐使用function,这样会柯里化。
函数可以给定部分参数,变成新的函数。
函数的参数也可以是函数,这样的函数叫做高阶函数。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jtag特工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值