函数式的语言比较热门的有微软的F#,Erlang,Ocaml,我推荐Ocaml。
据说编译成二进制代码,有和C一样的运行高效率。这是其他两个不能比的。
这是一篇入门基础,针对面向过程(如C语言)和面向对象(如Java)的读者。
(节选自 http://www.ocaml-tutorial.org/%E5%9F%BA%E7%A1%80)
注释
OCaml的单行多行注释是用(*和*)括起,且可以使用嵌套(* … *)块。
调用函数
假设你已经写好了一个函数repeated,它的参数是一个字符串s和一个数n,返回值是把s重复n遍形成的新字符串。
在大多数源于C的语言中,调用这一函数会象下面这样:
repeated (“hello”, 3) /* this is C code */
下面是OCaml中的函数调用:
repeated “hello” 3 (* this is OCaml code *)
注意:这里没有括号, 参数中间没有逗号。
我们来看另一个函数get_string_from_user,它返回从键盘读入的一个字符串。我们想把这个返回的字符串输入repeated.下面是C和OCaml的两种版本:
/* C code: */
repeated (get_string_from_user (“Please type in a string.”), 3)
(* OCaml code: *)
repeated (get_string_from_user “Please type in a string.”) 3
请注意括号的不同和逗号的有无。一般情况下,规则是:“括号只括起整个函数调用,不要括起函数调用的参数。
函数定义
OCaml的语法很简洁。下面是一个函数输入两个浮点数后计算它们的平均值:
let average a b =
(a +. b) /. 2.0;;
把它输入OCaml的“toplevel”(在Unix中,在shell中输入命令ocaml)中后你会看到:
# let average a b =
(a +. b) /. 2.0;;
val average : float -> float -> float =
可能大家会问:
* 为什么在OCaml中无需定义a和b的类型?OCaml怎么知道它们的类型?或者OCaml是否知道它们的类型呢?难道OCaml是完全动态类型的语言吗?
* C中2隐式转换成double类型, 可OCaml为什么不这样做?
* OCaml中return的方式是怎样?
我们来看答案:
* OCaml是强静态类型的语言。(也就是说没有如perl中的动态类型)。
* OCaml用类型推导(type inference)来找出类型,所以大家无需注明类型。如果你用上述OCaml的toplevel,那么OCaml会显示出它认为的函数类型。
* OCaml不做任何的隐式转换。如果你需要浮点数,你必须写2.0因为2是一个整数。
* 因为OCaml不允许操作符重载,它用不同的运算符来表示“两个整数相加”(用+)和“两个浮点数相加”(用+.)。注意后者有一个点号。其他算术运算符也是这样。
* OCaml返回函数的最后的表达式值,因此我们没有必要如C中一样写return。
基本类型
OCaml中的基本类型是:
OCaml 类型 范围
int 在32位处理器上是31位有符号整数(约+/- 10亿),
在64位处理器上是63位有符号整数。
float IEEE双精度浮点数,相当于C中的double。
bool 布尔变量,值为true或false
char 8位字符
string 字符串
unit 写作()
OCaml内部使用int中的一位来自动管理内存(垃圾收集)。因此基本 int类型是31位而非32位(如果你用64位平台,那就是63位)。但如果你的应用一定需要处理32位类型(比如你要写一些加密程序或者网络协议栈),OCaml提供nativeint类型。
OCaml基本类型中没有提供无符号整数类型。但是你可以使用nativeint来达到同样效果。另外就我所知,OCaml没有单精度浮点数。
OCaml提供char类型来表示字符。但可惜的是char类型不支持Unicode或者UTF-8。这是一个需要改进的很严重的缺点。但是当前我们可以使用 comprehensive Unicode libraries来处理。
unit类型有点象C中的void类型。
隐式转换和显式转换的比较
OCaml从不执行隐式转换。在OCaml中,1 + 2.5犯了类型错误。操作符+要求两个整数作为参数,而我们这里给出了一个整数一个浮点数,因此它会报错如下:
# 1 + 2.5;;
^^^
This expression has type float but is here used with type int
如果我们确实需要让一个整数与一个浮点数相加该怎么办呢?(假设它们存储于变量(i和f)。在OCaml中我们需要显式转换:
float_of_int i +. f;;
因为从int转换到float是个特别常用的操作,float_of_int函数有个简短的别名。上面的例子可以简单地写作:
float i +. f;;
(注意和C不一样的是,类型和函数同名在OCaml中是完全合法的。)
普通函数和递归函数
和源于C的语言不同的是,OCaml中的函数一般不是递归的,除非你用let rec代替let来定义递归函数。下面是一个递归函数的例子:
let rec range a b =
if a > b then []
else a :: range (a+1) b
;;
注意这里range调用它自身。
多态函数
现在我们来看一个比较奇怪的问题。如果一个函数的参数可以是任何类型怎么办?下面就是一个奇怪的函数,它接受任何参数但总是返回3。
let give_me_a_three x = 3;;
那么这样的函数的类型是什么呢?OCaml使用一个特殊的占位符来表示“任意类型”,这就是一个单引号后加一个字母。 上述函数的类型可以写作:
give_me_a_three : ‘a -> int
这里’a表示任意类型。我们可以如give_me_a_three “foo”这样也可以象give_me_a_three 2.0这样来调用这个函数。它们都是OCaml合法的表达式。
类型推导
简单地说:你不需要声明函数和变量的类型,因为OCaml自己会知道。
而且OCaml会一直检查所有的类型匹配(甚至在不同的文件之间)。
我们再次来看average函数,我们在OCaml的toplevel中输入它,
# let average a b =
(a +. b) /. 2.0;;
val average : float -> float -> float =
神奇吧?OCaml自己判断出了这个函数需要两个浮点数参数和返回一个浮点数。
它是如何做到的呢?首先它看a和b在哪里使用,这里是在表达式 (a +. b)中。这里+.本身是一个需要两个浮点数参数的函数,所以通过简单推导,a和b两个都是浮点数。
类型推导不仅适用于短程序,也适用于大规模的程序。它是一个主要的节省时间的特性,因为它消除了一系列在其他语言中常见的造成segfault,NullPointerException和ClassCastException的错误。
No related posts.
以上关联文章由 Yet Another Related Posts Plugin 提供支持。
No related posts.
以上关联文章由 Yet Another Related Posts Plugin 提供支持。