OCaml中的 class 和 object

Ocaml 也具有面向对象编程的范式,OCaml混合了三种范式: 函数式,面向对象,指令式,使得程序员可以在面对问题是选择最合适的方法来解决。


Class

先来看一个OCaml中class的例子,该例子是声明了一个栈类型,使用链表来实现

# class stack_of_ints =
    object (self)
      val mutable the_list = ( [] : int list ) (* instance variable *)
      method push x =                        (* push method *)
        the_list <- x :: the_list
      method pop =                           (* pop method *)
        let result = List.hd the_list in
        the_list <- List.tl the_list;
        result
      method peek =                          (* peek method *)
        List.hd the_list
      method size =                          (* size method *)
        List.length the_list
    end;;
class stack_of_ints :
  object
    val mutable the_list : int list
    method peek : int
    method pop : int
    method push : int -> unit
    method size : int
  end

基本语法格式 class name = object (self) ... end 定义了一个名为 name. 的类

在stack_of_ints类中,有一个实例变量叫the_list,他是可变的,初始时候给他一个空 int型列表, 如果我们不强加限制[] : int list ,则OCaml的类型推断引擎会默认认为[]表示是一个任意类型的空列表,这不是我们的本意

该类有4个方法,其中  <- 赋值符号被用来更新实例变量 the_list. 这个符号跟record类型的数据中中更新可变字段的符号一样

 -----------------------------------------------------------------------------------------------------------------------------------

Object

类定义好了之后,我们就可以声明他的对象了

# let s = new stack_of_ints;;
val s : stack_of_ints = <obj>

Now we'll push and pop some elements off the stack:

对象#方法  来调用相应的方法, 首先我们把1到10入栈,此时10在栈顶,然后依次弹出

# for i = 1 to 10 do
    s#push i
  done;;
- : unit = ()
# while s#size > 0 do
    Printf.printf "Popped %d off the stack.\n" s#pop
  done;;
Popped 10 off the stack.
Popped 9 off the stack.
Popped 8 off the stack.
Popped 7 off the stack.
Popped 6 off the stack.
Popped 5 off the stack.
Popped 4 off the stack.
Popped 3 off the stack.
Popped 2 off the stack.
Popped 1 off the stack.
- : unit = ()

 -----------------------------------------------------------------------------------------------------------------------------------

多态类

我们知道,在OCaml中 `a list 表示任何类型的列表,比如可以是 string list, int list 等等,  那么我们是否可以定义一个`a stack 呢? 也就是说 `a stack 可以表示 int stack , string stack 等等类型的栈

# class ['a] stack =
    object (self)
      val mutable list = ( [] : 'a list )  (* instance variable *)
      method push x =                      (* push method *)
        list <- x :: list
      method pop =                         (* pop method *)
        let result = List.hd list in
        list <- List.tl list;
        result
      method peek =                        (* peek method *)
        List.hd list
      method size =                        (* size method *)
        List.length list
    end;;
class ['a] stack :
  object
    val mutable list : 'a list
    method peek : 'a
    method pop : 'a
    method push : 'a -> unit
    method size : int
  end

这里只比上面的例子多了一个[`a]

The class ['a] stack doesn't really define just one class, but a whole "class of classes", one for every possible type (ie. an infinitely large number of classes!) Let's try and use our 'a stack class. In this instance we create a stack and push a floating point number onto the stack. Notice the type of the stack:

# let s = new stack;;
val s : '_a stack = <obj>
# s#push 1.0;;
- : unit = ()
# s;;
- : float stack = <obj>

刚开始的时候可以看到 栈的类型还是未定的, 当入栈一个浮点数之后,栈成为了浮点栈

This stack is now a float stack, and only floating point numbers may be pushed and popped from this stack. (For an explanation of the '_a notation, see the OCaml expert FAQ). Let's demonstrate the type-safety of our newfloat stack:

# s#push 3.0;;
- : unit = ()
# s#pop;;
- : float = 3.
# s#pop;;
- : float = 1.
# s#push "a string";;
Error: This expression has type string but an expression was expected of type
         float


我们可以定义一个多态的函数,他可以操作在任意类型的栈上

# let drain_stack s =
    while s#size > 0 do
      ignore (s#pop)
    done;;
val drain_stack : < pop : 'a; size : int; .. > -> unit = <fun>

Notice the type of drain_stack. Cleverly - perhaps too cleverly - OCaml's type inference engine has worked out that drain_stack works on any object which has pop and size methods! So if we defined another, entirely separate class which happened to contain pop and size methods with suitable type signatures, then we might accidentally call drain_stack on objects of that other type.

类型推断系统自动推断出了drain_stack 函数(输入一个对象,输出unit)作用在任意的对象上面,只要该对象具有pop , size 方法,并且这两个方法的类型也是我们这里所看到的即可

We can force OCaml to be more specific and only allow drain_stack to be called on 'a stacks by narrowing the type of the s argument, like this:

# let drain_stack (s : 'a stack) =
    while s#size > 0 do
      ignore (s#pop)
    done;;
val drain_stack : 'a stack -> unit = <fun>

这个例子中,s首先可以断定是一个对象,因为s#size, s#pop说明了s是一个对象, 之前的例子中,s可以是任意类的对象,现在被限定在了 `a stack 范围中 ,也就是说必须是`a stack 这种类的一个对象,具体可以是一个int stack, string stack ...的对象






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值