ocaml

安装

Linux环境下

  1. # apt-get install ocaml  
  2. # apt-get install ledit  
  3. # apt-get install tuareg-mode  


Windows环境下

自行下载OCamlWinPlus


学习资源
  1. 《Practical OCaml》, Joshua B. Smith
  2. 官方文档
  3. 教程网站



笔记


一、注释

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # (*comment*)  


二、let给表达式的结果赋一个名字

1. 使用let定义一个整型:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let inchesPerMile = 12*3*1760;;  
  2. val inchesPerMile : int = 63360  

2. 使用let定义一个函数:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let cube (x:int) = x*x*x;;  
  2. val cube : int -> int = <fun>  
[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let square x = x *. x;;  
  2. val square : float -> float = <fun>  
3. 多个参数:
[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let sumsq (x:int) (y:int) = x*x + y*y;;   
  2. val sumsq : int -> int -> int = <fun>  

ocaml里的类型检查和判断是自动完成的


三、布尔型true和false

1. not方法:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # not (5=10);;  
  2. - : bool = true  

2. 条件表达式 if xx then xx else xx:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # if true then (3*5) else (5-2);;  
  2. - : int = 15  


三、浮点数float

浮点方法:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # 1. +. 2.1;;  
  2. - : float = 3.1  
[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # 2.3 *. -3.5;;  
  2. - : float = -8.04999999999999893  
  3. (*注意比较“+.”和“+”*)  

需要注意的是,形如 1 + 1.0 或是 1 +. 1.0之类的运算都是会报错的,这时候需要用到类型转换函数type_of_type:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # 1 + 1.0;;  
  2. Error: This expression has type float but an expression was expected of type  
  3.          int  
  4. # 1 +. 1.0;;  
  5. Error: This expression has type int but an expression was expected of type  
  6.          float  
  7. # float_of_int 1 +. 1.0;;  
  8. - : float = 2.  



总结下(3月24日):

ocaml最基础的数据类型有如下几种(来源)

  1. int  在32位处理器上是31位的,64位处理器上是63位的,剩下的一位做了回收机制的flag。不过要是非想要c的效果,可以使用ocaml在Nativeint模块中提供的nativeint类型。同样的,uint也可以使用nativeint模仿
  2. bool true,false
  3. char 8位的字符,不支持utf-8和unicode
  4. string "Hello world"
  5. float ieee标准的双精度浮点数,不支持单精度浮点数
  6. unit 这个是很有意思的一个数据类型,写作(),用于制作序列运算,具体可以参照tpl书上的做法

ocaml基础的操作符:

  1. + 整型加法
  2. 整型减法
  3. ~-或-   整型负
  4. * 整型乘法
  5. /  整型除法,若除零则会raise一个Division_by_zero
  6. mod  整型同余,若第二个参数为0则会raise一个Division_by_zero
  7. land  整型的逐位逻辑与
  8. lor  整型的逐位或
  9. lxor  整型的逐位异或
  10. lsl  整型的逻辑左移
  11. lsr  整型的逻辑右移
  12. asr  整型的算数右移
  13. +.  浮点加法
  14. -.  浮点减法
  15. ~-.或-.  浮点负
  16. *.  浮点乘法
  17. /.  浮点除法
  18. **  浮点乘方
  19. @  列表连结
  20. ^  字符串连结
  21. !  取引用
  22. :=  引用赋址
  23. =  结构意义上的相等
  24. <>  结构意义上的不相等
  25. ==  物理意义上的相等
  26. !=  物理意义上的不相等
  27. <  小于
  28. <=  不大于
  29. >  大于
  30. >=  不小于
  31. &&  布尔与
  32. ||  布尔或

其中比较有意思的一个是结构意义上的相等和物理意义上的相等的区别。官方文档里给出了这样两段话:

e1 = e2 tests for structural equality of e1 and e2. Mutable structures (e.g. references and
arrays) are equal if and only if their current contents are structurally equal, even if the two
mutable objects are not the same physical object.
Equality between functional values raises
Invalid_argument. Equality between cyclic data structures may not terminate.

e1 == e2 tests for physical equality of e1 and e2. On mutable types such as references,
arrays, strings, records with mutable elds and objects with mutable instance variables, e1
== e2 is true if and only if physical modi cation of e1 also a ects e2. Onnon-mutable
types,
the behavior of ( == ) is implementation-dependent; however, it is guaranteed that
e1 == e2 implies compare e1 e2 = 0.


另外也要注意到,这些操作符在ocaml中也是被当作val对待的。联同其他的一些最常用的val一起被定义在Pervasives模块中,是ocaml默认包含的模块。

这个模块里常见的除了上面列举出来的,还有pred,succ,min,max,compare,raise,sin,abs,log,exp等等,篇幅所限就不写了。注意到一如


四、递归函数

rec标识:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let rec fact(n:int) = if n = 0 then 1 else n * fact(n-1);;  
  2. val fact : int -> int = <fun>  


五、列表

1. 元素类别必须相同:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # [1;2;3;];;  
  2. - : int list = [1; 2; 3]  
[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # [];;  
  2. - : 'a list = []  

2. 从前方追加元素,::读作"cons",是右结合的:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # 1::[2;3];;  
  2. - : int list = [1; 2; 3]  
[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # 1::2::3::[];;  
  2. - : int list = [1; 2; 3]  

3. 使用List模块中的List.hd和List.tl操纵列表元素:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # List.hd [1;2;3];;  
  2. - : int = 1  
[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # List.tl [1;2;3];;  
  2. - : int list = [2; 3]  

4. 一些例子:

1) 尾部追加元素

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let rec snoc(l:int list)(x:int)=  
  2.   if l=[] then x::[]  
  3.   else List.hd l :: snoc(List.tl l) x;;  
  4. val snoc : int list -> int -> int list = <fun>  
  5. # snoc [5;4;3;2] 1;;  
  6. - : int list = [5; 4; 3; 2; 1]  

2) 倒置元素

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let rec revaux(l:int list)(res:int list)=  
  2.   if l=[] then res  
  3.   else revaux (List.tl l) (List.hd l :: res);;  
  4. val revaux : int list -> int list -> int list = <fun>  
  5. # revaux [1;2;3][4;5;6];;  
  6. - : int list = [3; 2; 1; 4; 5; 6]  
  7. # let rev(l:int list) = revaux l [];;  
  8. val rev : int list -> int list = <fun>  
  9. # rev [1;2;3;4;5];;  
  10. - : int list = [5; 4; 3; 2; 1]  

3) 重写递归为tail-recursive style

原递归:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let rec fact(n:int)=  
  2.   if n=0 then 1    
  3.   else n*fact(n-1);;  
  4. val fact : int -> int = <fun>  

重写:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let rec factaux(acc:int)(n:int)=  
  2.   if n=0 then acc  
  3.   else factaux(acc*n)(n-1);;  
  4. val factaux : int -> int -> int = <fun>  
  5. # let fact(n:int)=factaux 1 n;;  
  6. val fact : int -> int = <fun>  

recursion style在返回后不需要进行计算


六、元组

1. 元素之间类型无限制:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # ("children",[3.1;2.5]);;  
  2. - : string * float list = ("children", [3.1; 2.5])  
[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let cube (x:int) = x*x*x;;  
  2. val cube : int -> int = <fun>  
  3. # (cube,"cube");;  
  4. - : (int -> int) * string = (<fun>, "cube”)  

2. 注意区分:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let g(x,y) = x*y;;  
  2. val g : int * int -> int = <fun>  
[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let g(x:int)(y:int) = x*y;;  
  2. val g : int -> int -> int = <fun>  


七、模式匹配

1. 基本形式match xx with xx a->b,若不能完整覆盖该类型则会报warning:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. val fact : int -> int = <fun>  
  2. # let rec listSum(l:int list)=  
  3.   match l with   
  4.   []->0  
  5.   | x::y-> x + listSum y;;  
  6. val listSum : int list -> int = <fun>  

2. 通配符 _:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let rec fact(n:int)=  
  2.   match n with  
  3.   0->1    
  4.   | _ -> n*fact(n-1);;  
  5. val fact : int -> int = <fun>  
[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let lastName name=    
  2.   match name with (n,_,_)->n;;  
  3. val lastName : 'a * 'b * 'c -> 'a = <fun>  


八、多态

‘a类型:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let rec length l =   
  2.   match l with  
  3.   [] -> 0  
  4.   | _::y -> 1 + length y;;  
  5. val length : 'a list -> int = <fun>  


九、异常

1. 定义一个exception,注意首字母大写:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # exception NegNum;;  
  2. exception NegNum  

2. raise使用异常:

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let rec fact n =  
  2.   if n<0 then raise NegNum    
  3.   else if n=0 then 1  
  4.   else n*fact(n-1);;  
  5. val fact : int -> int = <fun>  
  6. # fact (-3);;  
  7. Exception: NegNum.  


十、in

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. let … in e;;  
等价为
[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let … ;;  
  2. # e;;  

1. 本地函数

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let rec loop (n:int)=  
  2.   if n=1 then 1    
  3.   else n*loop(n-1) in   
  4.   loop 10;;  
  5. - : int = 3628800  
  6. # loop 2;;  
  7. Error: Unbound value loop  

2.简单的split实现

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let split l =   
  2.   let rec loop w l =  
  3.     match l with   
  4.       []->[w]  
  5.       | (' '::ls) -> w::(loop [] ls)  
  6.       | (c::ls) -> loop (w @ [c]) ls in  
  7.     loop [] l;;  
  8. val split : char list -> char list list = <fun>  
  9. (*其中@操作是List.append的简写*)  

3. split另一种实现

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let split l =  
  2.   let rec loop w l=  
  3.   match w,l with  
  4.   _,[] -> [w]   
  5.   | _,(' '::ls)->w::(loop [] ls)  
  6.   | _,(c::ls)->loop (w@[c]) ls in  
  7.   loop [] l;;  
  8. val split : char list -> char list list = <fun>  
  9. (*和例子2的效果完全相同,会在多个空格时产生空列表*)  

4. 排除空列表的split实现

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. # let better_split l =  
  2.   let rec loop w l =  
  3.   match w,l with  
  4.   [],[]->[]  
  5.   | _,[]->[w]  
  6.   | [],(' '::ls) -> loop [] ls     
  7.   | _,(' '::ls) -> w::(loop [] ls)  
  8.   | _,(c::ls) -> loop (w@[c]) ls in  
  9.   loop [] l;;  
  10. val better_split : char list -> char list list = <fun>  


先写到这吧orz...
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值