美文网首页
ocaml快餐教程(2) - 定义变量与函数

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

作者: Jtag特工 | 来源:发表于2020-11-24 20:59 被阅读0次

    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,这样会柯里化。
    函数可以给定部分参数,变成新的函数。
    函数的参数也可以是函数,这样的函数叫做高阶函数。

    相关文章

      网友评论

          本文标题:ocaml快餐教程(2) - 定义变量与函数

          本文链接:https://www.haomeiwen.com/subject/fbaviktx.html