title: OCaml从入门到放弃
date: 2020-01-04 13:28:47


文章目录

  • 0. 前言
  • 1. 注释与输入输出
  • 2. 编译与运行
  • 3. 变量
    • 3.1. 基本类型
    • 3.2. let 定义
    • 3.3. 多态类型
  • 4. 表达式
    • 4.1. 算术运算
    • 4.2. 逻辑运算
    • 4.3. 比较运算
    • 4.4. 位运算
    • 4.5. if 表达式
    • 4.6. while 表达式
    • 4.7. 模式匹配表达式
  • 5. 函数
    • 5.1. 简单函数
    • 5.2. function 和 fun
    • 5.3. 高阶函数
    • 5.4. 递归函数
  • 6. 数据结构
    • 6.1. 类型的显式定义(type)
    • 6.2. 元组类型(tuple)
    • 6.3. 记录类型(结构体 record)
    • 6.4. 联合类型/变体(加强版 union)
      • 6.4.1. 无参构造子(枚举类型)
      • 6.4.2. 带参构造子
      • 6.4.3. 递归类型
      • 6.4.4. 带多态变量的联合类型
      • 6.4.5. 多态变体
    • 6.5. 表(list 不可以修改)
      • 6.5.1. 创建
      • 6.5.2. 添加
      • 6.5.3. 取表头表尾
    • 6.6. 汇总
  • 7. 模块
    • 7.1. 文件即模块
    • 7.2. 接口和模块定义
  • 8. 命令式程序设计
    • 8.1. 可更改变量
    • 8.2. 可更改记录
    • 8.3. 可修改的数组(Array)
    • 8.4. for循环

0. 前言

学习函数式语言

1. 注释与输入输出

OCaml注释如下:

(* hello world *)

输入输出如下:

  • 输入

    • read_int(): 读入一个整数
    • read_float(): 读入一个浮点数
    • read_line(): 读入一个字符串
  • 输出

    • print_char 'a': 打印字符
    • print_int 2: 打印数字
    • print_float 3.4: 打印浮点数
    • print_newline(): 打印换行
    • print_string "hello world": 打印字符串
    • print_endline "hello world": 打印字符串,并换行
    • Printf.printf "int %i, float %f, char %c, string %s\n" 3 3.2 'a' "ok";;: 打印格式化字符串

(注:OCaml中的函数调用:不需要括号,除非无参数时才需要;各参数之间用空格隔开。)

2. 编译与运行

OCaml代码文件以.ml结尾。

  • 解释运行:ocaml xxx.ml
  • 编译运行
    • 编译生成可重定位文件:

      • 字节码:ocamlc -c xxx.ml -o xxx.cmo
      • 目标码:ocamlc -c xxx.ml -o xxx.o
    • 链接生成目标文件:ocamlc -o xxx xxx.cmo
    • 直接生成目标文件:
      • 字节码:ocamlc -o xxx xxx.ml
      • 目标码:ocamlopt -o xxx xxx.ml

3. 变量

变量名开头字母不能大写,若大写则会被当作联合类型中的构造子。

3.1. 基本类型

  • int: 整数类型
  • float: 浮点类型,浮点常数必须带小数点.,否则会被视为int类型
  • char: 字符类型
  • string: 字符串类型
  • unit: 啥都不是类型

在OCaml中,没有强制类型转换,整型只能与整型变量做运算。

若要类型转换,需使用相应函数,如下:

  • float_of_int 1: int => float
  • int_of_float 2.6: float => int
  • int_of_string "-23": string => int
  • float_of_string "1.2e3": string => float
  • string_of_int 12: int => string
  • string_of_float (-2.3): float => string

字符与ASCII码:

  • int_of_char 'x': 获取字符x的ASCII码值
  • char_of_int 120: 把ASCII码转换到字符

3.2. let 定义

  • let <变量1> = <表达式1> and <变量2> = <表达式2>: 全局定义

    • 必须赋初值
    • 可重复定义一个变量
    • 不能在表达式内部使用
  • let <变量1> = <表达式1> and <变量2> = <表达式2> in <表达式3>: 局部定义

    • 必须赋初值
    • 可重复定义一个变量
    • 可在表达式内部使用
    • 变量1,2作用域局限于表达式3

注意:let定义的变量是无法修改!!!修改需要使用命令式的方式,后文细说。

3.3. 多态类型

4. 表达式

4.1. 算术运算

  • (a/b) * b + (a mod b): 整型运算+, -, *, /, mod
  • 4e2 *. 2. /. 3. +. 1.: 浮点型运算,运算符必须带小数点.

4.2. 逻辑运算

  • not true: 非
  • true && false: 与
  • true || false: 或

4.3. 比较运算

>, <, >=, <=如常

相等与不等:

  • = and <>: 结构化比较,对比结构内部的子元素
  • == and !=: 物理比较,比较变量在内存中的存储地址(即比较地址)

对于非结构化数据,两种相等于不等相同。基本类型中,整型和字符非结构化数据浮点型和字符串都是结构化数据

4.4. 位运算

  • op1 land op2: 按位与
  • op1 lor op2: 按位或
  • op1 lxor op2: 按位异或
  • op1 lsl op2: op1左移op2位
  • op1 lsr op2: op1右移op2位
  • op1 asr op2: op1算术右移op2位

4.5. if 表达式

格式:

if <条件表达式> then <表达式1> [ else <表达式2> ]

示例:

# if 1<2 thentrue && falseelsetrue || false;;
- : bool = false

4.6. while 表达式

纯函数式语言没有循环。惊不惊喜,意不意外。

需要循环完成的工作,可以通过递归函数来完成。

4.7. 模式匹配表达式

强大至极

格式:

match <表达式> with| <模式1> [when <条件1>] -> <表达式1>  (* 第一个 | 可以省略 *)| <模式2> [when <条件2>] -> <表达式2>...| <模式n> [when <条件n>] -> <表达式n>

基础使用示例:

(* 取反 *)
let neg x =match x with| true -> false| false -> true
;;
(* 类型:val neg : bool -> bool = <fun> *)(* 是否为零 *)
let is_zero x =match x with| 0 -> true| _ -> false  (* 可以使用通配符 *)
;;
(* 类型:val is_zero : int -> bool = <fun> *)(* 异或 *)
let xor z =match z with(false, false) -> false (* 第一个 | 可以省略 *)| (false, true) -> true| (true, false) -> true| (true, true) -> false
;;
(* 类型:val xor : bool * bool -> bool = <fun> *)let xor x y =match x,y with(false, u) -> u     (* 在模式中可以使用变量 *)| (true, u) -> not u
;;
(* 类型:val xor : bool -> bool -> bool = <fun> *)let f (c:char) : string =match c with'0'..'9' -> "digit"       (* 可以使用 <字符>..<字符> 字符区间模式 *)| 'a'..'z' -> "lower char"| 'A'..'Z' -> "upper char"| _        -> "other char"
;;
(* val f : char -> string = <fun> *)let int_of_bool = function    (* function 可以直接做模式匹配,函数不带参数 *)true -> 1| false -> 0
;;
(* val int_of_bool : bool -> int = <fun> *)

5. 函数

First of all, 函数是变量。

5.1. 简单函数

  • let <fun_name> <参数1> <参数2>...<参数n> = <表达式>

  • let <fun_name> (<参数1>, <参数2>, ...<参数n>) = <表达式>: 将多个参数合并到一个元组中

示例:

let add x y =x + y ;;
(* 类型:val add : int -> int -> int = <fun> *)print_int (add 1 2) ;;  (* 3- : unit = () *)(* 部分作用/部分求值 *)
let inc x =add x 1 ;;
(* val inc : int -> int = <fun> *)(* 将多个参数合并到一个元组中,但这种方式不能部分求值 *)
let plus3 (a, b, c) =a + b + c ;;
(* val plus3 : int * int * int -> int = <fun> *)print_int (plus3 (1,2,3)) ;;  (* 6- : unit = () *)

5.2. function 和 fun

  • let <fun_name> = function <参数> -> <表达式>: 只能有一个参数,可用于模式匹配

  • let <fun_name> = fun <参数1> <参数2>...<参数n> -> <表达式>: 多参数

5.3. 高阶函数

函数作参和作返回值

5.4. 递归函数

在函数名前添加rec关键字,指定是递归函数。

let rec <fun_name> <参数1> <参数2>...<参数n> = <表达式>

示例:

(* 普通递归 *)
let rec factorial n =if n = 0 then1elsen * factorial (n-1) ;;
(* val factorial : int -> int = <fun> *)

尾递归实现循环,在参数中加入辅助函数。

(* 尾递归实现循环 *)
let fib n =if n < 3 then0elselet rec fib_tail n n_1 n_2 =if n = 3 thenn_1+n_2elsefib_tail (n-1) (n_1+n_2) n_1in fib_tail n 1 1
;;
(* val fib : int -> int = <fun> *)

6. 数据结构

6.1. 类型的显式定义(type)

格式:

type [<类型参数>] <类型标识符> = <类型定义表达式>

6.2. 元组类型(tuple)

格式:

<元素1>, <元素2>...<元素n>

每个元素的类型可以不相同。

元组的类型描述为:

<元素1类型> * <元素2类型> *...<元素n类型>

示例:

# let a = "Number", 1;;
val a : string * int = ("Number", 1)
# let b = "pi", 3.14, 5 ;;
val b : string * float * int = ("pi", 3.14, 5)
# let c = 1, (2,3), ((4,5), 6) ;;
val c : int * (int * int) * ((int * int) * int) = (1, (2, 3), ((4, 5), 6))

只有二个元素的元组也称作对偶

函数fstsnd分别取对偶的第一个和第二个分量。

6.3. 记录类型(结构体 record)

定义一个记录类型:

type <类型标识符> = { <字段名1>:<类型1>; ... ; <字段名n>:<类型n> }

创建一个记录类型变量:

{ <字段名1> = <表达式1>; ... ; <字段名n> = <表达式n> }

访问记录类型中的字段:

<记录类型变量>.<字段名>

注意:上述记录类型不可修改!!!

6.4. 联合类型/变体(加强版 union)

格式:

type [<多态类型变量>] <类型标识符> =<构造子名1> [of <参数类型1>]...<构造子名n> [of <参数类型n>]

注:构造子名首字母必须大写

6.4.1. 无参构造子(枚举类型)

无参构造子。类似于枚举类型,构造子能和常数或者布尔值一样使用

示例:

type seasons = Spring | Summer | Autumn | Winter ;;let int_of_seasons = functionSpring -> 1| Summer -> 2| Autumn -> 3| Winter -> 4
;;
(* val int_of_seasons : seasons -> int = <fun> *)int_of_seasons Autumn ;;  (* - : int = 3 *)

6.4.2. 带参构造子

带参构造子。构造子相当于类型,可以用于定义变量

示例:

type num = Int of int | Float of float ;;Int 3 ;;  (* - : num = Int 3 *)let add_num = function(Int m, Int n) -> Int (m+n)| (Int m, Float n) -> Float ((float_of_int m) +. n)| (Float m, Int n) -> Float (m +. (float_of_int n))| (Float m, Float n) -> Float (m +. n)
;;
(* val add_num : num * num -> num = <fun> *)

6.4.3. 递归类型

示例,定义一个二叉树:

type inttree =Leaf of int| Node of inttree * inttree (* *号表明是元组类型,不是乘号 *)
;;

6.4.4. 带多态变量的联合类型

6.4.5. 多态变体

6.5. 表(list 不可以修改)

类型描述:

<类型> list

6.5.1. 创建

[ e1; e2; ... en;]

ei可以是变量或者常量,但类型必须相同。

6.5.2. 添加

头插:

# 1::[2;3];;
- : int list = [1; 2; 3]

追加:

# List.append [1;2] [3;4] ;;
- : int list = [1; 2; 3; 4]
# [1;2] @ [3;4] ;;
- : int list = [1; 2; 3; 4]

6.5.3. 取表头表尾

表头是第一个元素,表尾是除第一个元素外的子表。

# List.hd [1;2;3] ;;
- : int = 1
# List.tl [1;2;3] ;;
- : int list = [2; 3]

注意:表中元素也不可以修改!!!

6.6. 汇总

OCaml 名字     类型定义的例子                    用法list            int list                       [1; 2; 3]
tuple          int * string                   (3, "hello")
record         type pair =                    { a = 3; b = "hello" }{ a: int; b: string }
变体            type foo =| Int of int                 Int 3| Pair of int * string
变体            type sign =| Positive                   Positive| Zero                       Zero| Negative
参数化变体       type 'a my_list =| Empty                      Cons (1, Cons (2, Empty))| Cons of 'a * 'a my_list

7. 模块

7.1. 文件即模块

如果有一个util.ml文件:

(* util.ml *)
let msg = "helloworld" ;;
let printMsg =print_endline msg
;;

那么,可以在另一个main.ml文件中:

print_endline Util.msg ;;

调用Util模块(即util.ml文件)中的变量或者函数。

如果还存在一个util.mli文件,那它就是util.ml模块的接口。未在util.mli中出现的变量或函数,都不能被调用。同时,util.mli文件需要在util.ml文件编译前编译,如下:

ocamlc -c util.mli -o util.cmi

7.2. 接口和模块定义

接口定义

module type <接口名> =sig<接口定义体>end

<接口名>是一个大写字母开头的标识符,<接口定义体>中包括了 type 定义、函数的类型描述等内容。

模块定义:

module <模块名> [:<模块接口>] =struct<模块体>end

<模块名>是一个大写字母开头的标识符,<模块体>中包括了 type 定义和 let 定义。

8. 命令式程序设计

8.1. 可更改变量

定义:

let <变量> = ref <表达式> ;;

引用:

!<变量>

赋值:

<变量> := <表达式>

8.2. 可更改记录

定义:

type <记录类型> = {...mutable <分量名> : <分量类型>;...
}

赋值:

<记录>.<分量> <- <表达式>

8.3. 可修改的数组(Array)

定义:

let arr = [|1;2;3|] ;;

访问:

<数组>.(<下标>) (* 如:mart.(1).(2) *)

赋值:

<数组>.(<下标>) <- <表达式>

常用函数:

Array.make 4 1; (* 创建长度为4的一维数组,都初始化为1 *)
Array.make_matrix 2 3 0; (* 创建行为2列为3的二维数组,都初始化为0 *)
Array.length arr; (* 返回数组长度 *)
Array.iter print_int [|1;2;3|]; (* 把一个函数作用于数组每个元素上 *)

8.4. for循环

for <变量>=<初始表达式> to <终止表达式> do<表达式1>;...<表达式n>;
done

OCaml从入门到放弃相关推荐

  1. AI从入门到放弃2:CNN的导火索,用MLP做图像分类识别?

    来源 | 腾讯知乎专栏 作者 | AIoys(腾讯员工,后台工程师) 项目文档和代码在此:github项目地址: https://github.com/zsysuper/AI_Notes ▌一.前言 ...

  2. AOP埋点从入门到放弃(二)

    其实人最大悲哀莫过于知道自己想要什么,却不知道怎么坚持!最近迷恋上了死侍 其实和我平时的状态差不多,以一个混子的心态去做任何事情,往往成功的概率会更大!!! 一张图片镇楼!!! 上文说到了Aspect ...

  3. 入门到放弃node系列之网络模块(二)

    为什么80%的码农都做不了架构师?>>>    前言 本文首发[一名打字员] 上一节我们刚刚介绍完node的HTTP和HTTPS模块,相信我们也对nodejs有了更深层次的理解,接下 ...

  4. webpack - vue Component 从入门到放弃(三)

    离上一篇已经一个星期了,人的拖延症是没法救的,今晚趁着蒙蒙春雨,来抒发抒发情感. 上一篇简单介绍了webpack的配置,这里稍微再做一一下延伸 插件 插件可以完成更多 loader 不能完成的功能.插 ...

  5. CYQ.Data 从入门到放弃ORM系列:开篇:自动化框架编程思维

    前言: 随着CYQ.Data 开始回归免费使用之后,发现用户的情绪越来越激动,为了保持这持续的激动性,让我有了开源的念头. 同时,由于框架经过这5-6年来的不断演进,以前发的早期教程已经太落后了,包括 ...

  6. python ** 运算符_Python从入门到放弃运算符(2)

    摘要:上一篇Python从入门到放弃-运算符(1),讲了Python的运算符中的算术运算符.赋值运算符.比较(关系)运算符,这篇继续讲Python的运算符. 逻辑运算符 逻辑运算符是对真和假两种布尔值 ...

  7. python从入门到放弃系列恶搞短片-太惨!学Python方法用错,直接从入门到放弃!...

    原标题:太惨!学Python方法用错,直接从入门到放弃! 从你开始学习编程的那一刻起,就注定了以后所要走的路-从编程学习者开始,依次经历实习生.程序员.软件工程师.架构师.CTO等职位的磨砺:当你站在 ...

  8. 为什么叫python编程-月薪上万的Python编程,为什么你还没入门就放弃了?

    "大家都是怎么学Python 的?我学了一个月,感觉自己怎么学都学不进去啊,还是一脸懵--学习前我有做过规划,现在连入门都不算,我应该怎么办啊!!!求大神给一个学习思路!求大神讲解入门要重点 ...

  9. python从入门到放弃图片大全-Python 从入门到放弃(一)

    Python 从入门到放弃(一) 一个没有逻辑顺序的python笔记,包括日常使用python时的总结与阅读<Python基础教程>([挪] Magnus Lie Hetland 著) 的 ...

最新文章

  1. 与后台通讯,首先要了解AMF协议
  2. Simple Dynamic Strings(SDS)源码解析和使用说明二
  3. 杜克大学和Facebook联手开发更好的光通信
  4. Swift - 异常处理
  5. jpa怎么传参到in中_Spring Boot中的测试
  6. html标签中lang属性及xml:lang属性的作用
  7. pythonzip是迭代器_Python迭代器和zip
  8. linux lsm 程序加载钩函数,LSM在Linux中的实现方式
  9. python变量的输入
  10. I - 免费馅饼-图画详细解析
  11. Android学习---通过内容提供者(ContentProvider)操作另外一个应用私有数据库的内容...
  12. python大神的程序_6年Python大神总结10个开发技巧,80%的人都不会
  13. 广西南宁机器人比赛_第18届广西青少年机器人竞赛闭幕
  14. Android动画总结
  15. 正点原子STM32(基于HAL库)1
  16. uniapp中app、h5、小程序引入高德地图定位,并封装起来调用。
  17. 【JS】网页点击悬浮小苹果+小虫子追踪鼠标位置(可替换)
  18. 软件工程实验:原型设计
  19. java 后台判断浏览器类型,IE11下载乱码解决方案
  20. 【总结】1111- 如何搞定Banner背景自动换色的功能?

热门文章

  1. 制作Flink的Parcel包和csd文件
  2. 51nod_1809 黑白图(DFS+树状数组)
  3. 49股权与51股权的区别有哪些
  4. app调用微信支付接口第一次成功,后面就调取不成功
  5. 水性防腐涂料行业调研报告 - 市场现状分析与发展前景预测
  6. 关于字符串拼接的几种方法
  7. ios--苹果应用商店审核指南中文翻译
  8. 各大搜索引擎网站域名收录登录入口
  9. ByteBuffer注意事项
  10. 三分钟快速开发手机号注册登录功能