`

OCaml基础知识

 
阅读更多

 

注释

OCaml的注释是用(* and *)来分隔的,如下:

(* 这是一个单行注释 *)
 
(* 这是一个
 * 多行
 * 注释
 *)

换句话说,注释的方式和原始的C(/* ... */)一样。

目前还没有单行注释的语法(就是类似Perl的# ...或者C99/C++/Java的// ...)。是否使用##...还没有确定,而且我极力推荐OCaml的人以后能将其加入到语言中。

OCaml可以处理嵌套的(* ... *),这可以让你很方便地注释某个代码区域:

(* 这段代码坏的……
 
(* 质数测试. *)
let is_prime n =
  (* 对自己说:在邮件列表上问一下 *) XXX;;
 
*)

函数调用

假设你已经写了一个函数——叫它repeated吧——,它需要一个字符串s和一个数字n作为参数,并返回一个新的字符串,包含了s重复了n次的结果。

在大部分C派生的语言中,调用这个函数类似于:

repeated ("hello", 3)  /* C代码 */

这表示“使用两个参数调用函数repeated,第一个参数是字符串hello,第二个参数是数字3”。

OCaml,和其他函数式语言一样,在函数调用的写法和括号的用法是有所不同的,所以容易产生很多错误。在OCaml中同样的函数调用是:

repeated "hello" 3  (* OCaml代码 *)

注意——不需要括号,参数之间也不需要逗号。

那么,repeated ("hello", 3)在OCaml中则有另外一番深刻的含义。它表示“使用一个参数调用函数repeated,该参数为包含两个元素的一个‘pair’(偶对)结构”。这当然是错误的,因为repeated函数需要两个参数,而非一个,同时在任何情况下第一个参数都应该是一个字符串,而非一个偶对。不过目前还不用管偶对(元组“tuple”)只要记住在函数调用的参数两边加上括号和中间加上逗号是错误的。

再看另外一个函数——get_string_from_user——它输入一个提示字符串并返回由用户输入的字符串。我们希望将这个字符串传入repeated。下面是C和OCaml的版本:

/* C代码: */
repeated (get_string_from_user ("请输入一个字符串。"), 3)
(* OCaml代码: *)
repeated (get_string_from_user "请输入一个字符串。") 3

仔细看一下括号和逗号的用法。一般规则是:“括号要放在整个函数调用两边——不要将括号放在函数调用的参数周围”。下面还有一些例子:

f 5 (g "hello") 3    (* f有三个参数,g有一个参数 *)
f (g 3 4)            (* f有一个参数,g有两个参数 *)
 
# repeated ("hello", 3);;     (* OCaml 将会指出这个错误 *)
This expression has type string * int but is here used with type string

定义一个函数

你已经知道如果在其他主流语言中如何定义函数(对熟悉Java的来说,静态方法)。那么在OCaml要怎么做呢?

OCaml的语法十分简练,令人感觉愉悦。下面是一个输入两个浮点数并计算平均数的函数:

let average a b =
  (a +. b) /. 2.0;;

将这段内容输入OCaml“顶层”(在Unix上,从外壳中输入ocaml),然后就应该看到如下内容:

# let average a b =
  (a +. b) /. 2.0;;
val average : float -> float -> float = <fun>

如果你仔细看函数定义,然后看看OCaml给你输出的内容,你就会产生一系列疑问:

  • 代码中额外的句号是干什么用的?
  • float -> float -> float这串东西是什么意思?

在下一节中,我会来回答这些问题,不过首先我要继续在C中定义同样的函数(Java中的定义应该会和C十分相似),而且这也很可能会引发更多的疑问。下面是average的C版本:

double
average (double a, double b)
{
  return (a + b) / 2;
}

现在再回头看一下简短得多的OCaml定义。你很有可能会问:

  • 为什么在OCaml的版本中并没有定义ab的类型?OCaml是如何知道它们是何种类型的(更进一步问,OCaml是否知道它们的类型,或者OCaml完全就是动态类型的)?
  • 在C中,2会隐式转换成一个double,但是为何OCaml不能做同样的事情?
  • OCaml写return的方式是什么?

好吧,现在就来回答其中一些问题。

  • OCaml是一个静态强类型语言(换句话说,类型上没有任何像Perl中出现的那种动态的东西)。
  • OCaml使用类型推断来计算出类型,所以无需进行声明。如果像上面那样在OCaml顶层输入代码,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位平台,int是63位)。实际上除了一些特殊的场合,这并不成为一个问题。例如,如果你是对循环进行计数,OCaml会限制数到10亿而非20亿。这也不会成为一个问题,因为如果在任何语言中,要使用到接近这个上限的数字,你就应该使用大数字bignum(OCaml中的NatBig_int模块)。不过,如果你确实需要32位类型来处理一些事情的话(eg.写加密代码或者网络栈相关的),OCaml还提供了一个nativeint类型,匹配了你的平台上本地的整型。

OCaml没有基本的无符号整数类型,但是你可以使用nativeint来达到相同的效果。目前我只能说OCaml完全没有单精度浮点数的支持。

OCaml提供了一个用于字符的char类型,例如,写作'x'。不幸的是char类型并不能支持Unicode或者UTF-8。这在OCaml中是一个很严重的瑕疵,应该被修正,但是现在可以使用comprehensive Unicode libraries来解决这个问题。

字符串不仅仅是字符的列表。它们有自己更加有效的内部表示方式。

unit类型是一种类似于C中void的东西,but we'll talk about it more below.

隐式转换对比显式转换

在C派生的语言中int在某些特定的环境下会自动提升为浮点数。例如,如果写1 + 2.5,那么第一个参数(是一个整数)会提升为一个浮点数,结果也同样是一个浮点数。就好像你写了((double) 1) + 2.5,不过都是隐含完成的。

OCaml则从不像这样进行隐式转换。在OCaml中,1 + 2.5则是一个类型错误。OCaml中的+操作符要求两个整数作为参数,而这里我们给出了一个整数和一个浮点数,所以它会报告这样的错误:

# 1 + 2.5;;
         ^^^
This expression has type float but is here used with type int

要将两个浮点数相加,你则需要另外一个不同的操作符,+.(注意后面的点)。

OCaml不会自动将整数提升为浮点数,所以这也是一个错误:

# 1 +. 2.5;;
     ^
This expression has type int but is here used with type float

这里OCaml在抗议第一个参数。

如果你确实需要将一个整数和一个浮点数相加,要怎么做?(假设它们存储在叫做if)。在OCaml中你需要显式转换:

float_of_int i +. f;;
例子:正确的方法# float_of_int 10+.15.5;;
- : float = 25.5
错误:# 10+.5.5;;系统提示信息:Characters 0-2:
    10+.5.5;;
    ^^
This expression has type int but is here used with type float
# 

float_of_int是一个输入了一个int并返回一个float。除此之外还有很多此类函数,名字诸如int_of_floatchar_of_intint_of_charstring_of_int等等,同时功能基本和名字吻合。

由于将int转换为float是一个特别常用的操作,float_of_int函数有一个较短的别名:以上例子可以简单地写为:

float i +. f;;

(注意和C不一样,一个类型和一个函数有同样的名称在OCaml中这是完全有效的。)

隐式转换好,还是显式转换好?

你可能会想这些显式转换很丑陋,甚至很耗费时间,。首先,OCaml需要显式转换才能进行类型推断(见下文),同时类型推断又是一个奇妙的、节省时间的特点,可以很方便地抵消显式转换所带来的额外的键盘输入,如果你以前花了很长时间来调试C程序的话,你就会知道(a)隐式的类型转换所造成的错误很难被发现,(b)你常常会花很多时间来计算在哪里要发生隐式转换。让类型转换明确化就可以帮助进行调试。第三,某些转换(尤其是 int <-> float)实际上是十分昂贵的操作,所以你应该自己来做而不是隐藏。

#

普通的函数和递归函数

和C派生的语言不同,除非你明确使用let rec替代let来声明一个函数,否则这个函数就不能是递归的。下面是一个递归函数的例子:

let rec range a b =
  if a > b then []
  else a :: range (a+1) b
     ;;

注意range调用了其自身。

let 和 let rec之间唯一的区别就在于函数名的范围。如果上面的函数仅仅是用let来定义的话,当调用range时就会尝试调用一个已经存在的(前面定义过的)叫做range的函数,而不是当前被定义的函数。使用let和使用let rec定义的函数之间没有任何性能上的差别,所以如果你愿意,你可以总是使用let rec形式来进行定义,就可以获得类似于C语言的语义。

函数的类型

因为类型推断的存在,你可能很少甚至从不需要明确写出函数的类型。不过,OCaml常常会输出它所认为的函数的类型,所以你需要知道它的语法。对于一个带有参数arg1arg2、……的函数,编译器会输出:

f : arg1 -> arg2 -> ... -> argn -> rettype

箭头语法现在看起来很奇怪,不过之后当我们接触所谓的“currying”,你就会明白为何要选它。现在我就要给出几个例子。

我的函数repeated输入一个字符串和一个整数并返回一个字符串的类型为:

repeated : string -> int -> string

我们的函数average输入两个浮点数并返回一个浮点数的,类型为:

average : float -> float -> float

OCaml标准的int_of_char类型转换函数:

int_of_char : char -> int

如果一个函数什么也不返回(对C和Java程序员来说应该是void),那么我们写为返回unit类型。例如,下面是OCaml中等同于fputc的是:

output_char : out_channel -> char -> unit

多态函数

现在对于某些东西有些奇怪。如何才能使一个函数可以输入任何类型作为其参数呢?下面是一个输入一个参数的奇怪函数,但是忽略了这个参数,仅返回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中都是有效的表达式。

多态函数到底能有什么用现在你可能还不清楚,不过它们确实十分有用也十分常见,所以我们将稍后讨论它们。(提示:多态时一种类似于C++或Java 1.5中的模版和泛型的东西)。

类型推断

那么本教程的主题是函数式变成有很多十分神奇的特点,同时OCaml则是包含了所有这些神奇特点的一门语言,这就使其对于真正的程序员的使用来说是一们非常实用的语言。但是奇怪的是这些大部分神奇的特点和“函数式编程”都毫无关系。实际上,我已经讲述了第一个神奇特点,而且我还未讲解为何函数式变成要叫做“函数式”的。不管怎样,下面是第一个神奇特点:类型推断。

只要记住:你不需要声明你的函数和变量的类型,因为OCaml会为你计算出来

另外OCaml会继续检查所有的类型匹配(甚至在不同的文件之间)。

但是OCaml也是一个实用的语言,因此它在类型系统中包含了一些后门可以让你在特殊的必要场合中绕过它。一般只有大牛们可能会绕过类型检验。

让我们回到前面我们输入到OCaml顶层的函数average

# let average a b =
  (a +. b) /. 2.0;;
val average : float -> float -> float = <fun>

说来奇怪!OCaml能自己计算出该函数输入两个float参数并输出一个float

它是如何做到的呢?首先它看在哪里用到了ab,即在表达式(a +. b)中。现在,+.则是一个总是输入两个float参数,所以通过简单的探测,ab一定都是float类型的。

第二,/.函数返回一个float,同时它也是average的返回值,所以,average必然返回一个float。最后结果就是average会拥有以下类型签名:

average : float -> float -> float

类型推断明显对于这类短小的程序很方便,不过甚至对于大型程序,都很有效,同时它是个重要的省时特点,因为它可以避免其他语言中的一大类错误,如段错误(segfault)、的NullPointerExceptionClassCastException(或者是一些重要的但常常被忽略的运行时警告,如Perl)。

来自(http://hi.baidu.com/cg51/blog/item/12eea86e5aee1ddb81cb4a02.html

分享到:
评论

相关推荐

    ocaml book

    本书是关于OCaml这门函数式编程语言的全面介绍,作者Jason Hickey以其深厚的理论基础和丰富的实践经验为读者提供了深入浅出的学习指南。本书旨在帮助读者理解OCaml的核心概念,并通过实际编程练习加深对这些概念的...

    awesome-ocaml:精选的OCaml工具,框架,库和文章精选

    - OCaml教程:一系列介绍OCaml基础知识的文章,包括语法、类型系统和模块化。 - Advanced OCaml:深入讨论OCaml的高级特性,如类型系统、GADTs(通用代数数据类型)和模式匹配。 - OCaml在工业中的应用:案例研究...

    Developing Applications With OCaml

    《Developing Applications With OCaml》是一本专注于使用OCaml编程语言进行应用开发的书籍,它为读者提供了深入理解和掌握OCaml所需的知识体系。OCaml,全称Objective Caml,是Caml的一种方言,属于ML语言家族的一...

    langage ocaml

    从目录结构中可以看出,本书首先介绍了OCaml编程的基础知识,接着深入到递归和命令式编程的高级概念。具体到知识点上,可以细分为以下几个方面: 1. 初识Caml:这部分应该包含了Cam语言的基本概念、如何与Cam进行...

    cv:我的Melt OCaml简历

    **OCaml基础知识** 1. **类型系统**:OCaml拥有一个强大的静态类型系统,这意味着在编译时就能发现许多类型错误,提高了代码的稳定性和可靠性。它的类型推断机制使得程序员不必为每个变量都声明类型。 2. **模块...

    OCaml from the Very Beginning

    ### OCaml从零开始:全面解析 #### 一、OCaml简介 ...通过本书的学习,你将掌握OCaml的基础知识,并能编写出具有一定复杂度的程序。此外,本书丰富的习题和解答也能帮助你更好地理解和运用所学知识。

    用OCaml开发的国际象棋程序

    1. **OCaml的基础语法**:了解OCaml的基本数据类型、表达式、函数定义和控制流语句。 2. **类型系统**:OCaml是静态类型的,学习如何定义和使用各种类型,如int、string、list、record和variant等。 3. **模式匹配**...

    ocaml reference manual

    ### OCaml 参考手册知识点概述 #### 一、OCaml简介与核心语言特性 **1.1 基础概念** - **OCaml(Objective Caml)**: 是一种功能强大的函数式编程语言,同时也支持命令式编程风格。 - **语法特点**:简洁明了,...

    使用,理解和分解OCaml语言Using, Understanding, and Unraveling The OCaml Language

    《使用,理解和分解OCaml语言》是一套针对对现代编程语言、特别...提供了从基础到高级的渐进学习路径,强调了理论与实践结合的重要性,使得学习者可以通过实际编程练习和理论学习相结合的方式来深化对OCaml语言的理解。

    More OCaml

    本书不仅适合已有一定OCaml基础的程序员进一步提升技能,也适用于希望探索函数式编程语言如OCaml的有经验的程序员。 #### 二、函数式编程基础回顾 为了确保所有读者都能跟上节奏,本书在开头部分提供了OCaml的基础...

    ocaml for scientists

    《OCaml for Scientists》这本书由Jon Harrop撰写,作为一本面向科学计算领域的OCaml教程,它不仅介绍了OCaml语言的基础知识,还深入探讨了如何利用该语言进行高效的数据处理和数值分析。作者通过独立发行的方式出版...

    对于OCaml识字的编程工具.gz

    1. OCaml语言基础:包括其静态类型系统、模块系统、函数式编程特性以及面向对象编程的支持。 2. LaTeX基础:了解如何创建文档结构、插入公式、引用和列表等基本操作。 3. 编译和构建工具:如何使用`gcc`或`ocamlc/...

    ocaml:Objective Caml编译器和编程环境

    要学习和使用这个压缩包,你需要具备一定的C语言基础和构建工具链知识,因为OCaml是用C和OCaml自身编写的。你可以按照以下步骤操作: 1. 解压`ocaml-master`到一个目录。 2. 配置环境,确保拥有C编译器(如GCC)和...

    socc:OCaml中的简单C编译器

    【标题】"socc:OCaml中的简单C编译器" 涉及的主要知识点是计算机科学中的编译原理和实现,以及特定编程语言和技术的使用。socc是一个使用OCaml语言开发的C语言编译器,OCaml是一种功能强大的多范式、静态类型的编程...

    ocaml:OCaml核心系统:编译器,运行时系统,基础库

    OCaml是一种多范式、静态类型的编程语言,以其强大的类型系统和高效的编译器而闻名。这个主题聚焦于OCaml的核心...这涵盖了语言的各个方面,从编写安全的代码到构建高效的应用程序,都是OCaml开发者不可或缺的知识。

    raygun4ocaml:在OCaml中暂定raygun API的实现

    2. **OCaml语言**:理解OCaml的基础语法、类型系统、模块系统、函数式编程特性以及如何进行编译和调试是非常重要的,这对于使用raygun4ocaml库的开发者来说是基础。 3. **错误追踪**:raygun4ocaml可以帮助开发者...

    ocaml-diary:OCaml 练习和例子的日记

    本资料集合——"ocaml-diary",提供了一系列OCaml编程的练习和示例,涵盖了基础到进阶的数据结构,包括列表、队列、堆和树,以及欧拉文件等。以下是对这些主题的深入解析: 1. **列表(List)**:OCaml中的列表是不可...

Global site tag (gtag.js) - Google Analytics