`

OCaml程序的结构

阅读更多

原文地址:http://www.ocaml-tutorial.org/the_structure_of_ocaml_programs 翻译:ShiningRay

现在我们花些时间从一个更高的层次来看看实际的OCaml程序。我想教一下关于局部和全局定义,什么时候使用;;,什么时候是;,以及模块、嵌套函数和引用。这样我们就要看这些以前毫无概念没见过的OCaml概念了,不过现在还不用担心细节问题。首先关注程序整体结构以及我指出的一些特点。

局部“变量”(实际上是 局部表达式,local expressions)

让我们先拿average函数看一下,并在C语言中添加一个局部变量(可以拿它和我们之前第一次的定义进行一下比较)。

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

现在我们在OCaml的版本中也做同样的事情:

let average a b =
  let sum = a +. b in
   sum /. 2.0;;
# let average a   b= (a+.b)/.2.0;; 注意优先级别.
val pjz : float -> float -> float = <fun>
# pjz 10.5 21.5;;
- : float = 16.
# 

标准短语let name = expression in可以用于定义一个局部表达式,然后name就可以在后面的函数中使用,来替代expression了,直到结束该代码块的;;出现。注意在in后面我们并没有缩进。就把let...in当作是一个语句。

现在将C的局部变量和这些命名的局部表达式进行比较,好像是差不多的。其实他们是两种不同的东西。C变量sum在栈上有一个分配给它的槽。如果需要,你可以在函数中以后给sum分配值,甚至可以获取sum的地址。但是这对于OCaml版本却不正确。在OCaml版本中,sum只是表达式a +. b的缩写。不可能对sum赋值或者改变它的值。(稍后会给你看如何才能制造真正的变量)。

下面是另一个例子,可以把事情讲的更加清楚。下面两个代码片断将返回同样的值(也就是 (a+b) + (a+b)2):

let f a b =
  (a +. b) +. (a +. b) ** 2.
   ;;
let f a b =
  let x = a +. b in
   x +. x ** 2.
   ;;

第二个版本可能会快一点(不过现在大多数编译器都应该可以直接为你完成“消除重复子表达式”工作),同时它也更加容易阅读。第二个例子中的x仅仅是a +. b的缩写。

全局“变量”(实际上是全局表达式)

你也可以在最顶层为某些东西定义全局名字,同时与上面我们所说的局部“变量”一样,这些都完全不是真正的变量,仅仅是某些东西的别名。下面是一个实际应用的例子(做了删减):

let html =
  let content = read_whole_file file in
   GHtml.html_from_string content
   ;;
 
let menu_bold () =
  match bold_button#active with
    true -> html#set_font_style ~enable:[`BOLD] ()
   | false -> html#set_font_style ~disable:[`BOLD] ()
   ;;
 
let main () =
  (* 代码省略 *)
   factory#add_item "Cut" ~key:_X ~callback: html#cut
   ;;

在这段实际的代码中,html是一个HTML编辑部件(来自lablgtk库的一个对象),它是由第一行语句let html=一次性在程序开始的时候创建的。然后在后面的函数中被多次引用。

注意上面的代码段中的html名字不能当作是一个和C或者其他命令式语言中的实际的全局变量。并没有为“html指针”分配任何空间进行“存储”。也不能对html分配任何值,例如重新将其分配指向另一个不同的部件。在下面的一节中,我们将讨论引用,这才是真正的变量。

Let-绑定

任何let ...的使用,无论在最顶层(全局的),或在一个函数中,一般都称之为let-绑定

引用:真正的变量

如果你需要一个真正的变量对其进行赋值,并可在程序中使用、更改,那要怎样呢?这时候就需要用到引用(reference)。引用和C/C++中的指针十分类似。在Java中,所有保存对象的变量实际上都是对象的引用(指针)。在Perl中,引用就是引用——和OCaml中的是同一个东西。

下面是我们如何在OCaml中创建指向一个int值得引用:

ref 0;;

事实上,这个语句并没有什么大的用途。我们仅仅创建了一个引用,但因为我们并没有给它命名,所以垃圾收集器会过来将其立刻回收!(实际上,它也可能在编译期就被扔掉了)。让我们来给这个引用命名吧:

let my_ref = ref 0;;
# let myvar= ref "hello";;
val myvar : string ref = {contents = "hello"}
# 

这个引用目前存储了一个整数零。下面我们再将一些别的东西放进去(赋值):

my_ref := 100;;

同时看看现在这个引用里面包含什么:

# !my_ref;;
- : int = 100
显示变量数值
# !myvar
   ;;
- : string = "hello"
# 

所以,:=操作符是用于为引用赋值的,同时!操作符可以解除引用获取实际的内容。 下面是一个与C/C++大致的比较:

OCaml                   C/C++

let my_ref = ref 0;;    int a = 0; int *my_ptr = &a;
my_ref := 100;;         *my_ptr = 100;
!my_ref                 *my_ptr

引用有他们的用途,但是你也可能发现并不会经常用到引用。更多的时候,你会在函数定义中使用let name = expression in来命名局部表达式。

嵌套函数

C实际上并没有嵌套函数的概念。GCC支持C程序的嵌套函数,但我还不知道有什么程序会实际使用这个扩展。不管怎样,先看看gcc info页面是如何解释嵌套函数的:

一个“嵌套函数”是指定义在另一个函数中的函数。(GNU C++并不支持嵌套函数。)嵌套函数的名称是局限于它所定义的代码块中的。例如,下面我们定义一个叫做`square'的嵌套函数,并调用两次:

foo (double a, double b)
{
  double square (double z) { return z * z; }
 
  return square (a) + square (b);
}

嵌套函数可以访问任何包含它的函数中定义时所能看到的变量。这叫做“词法范围”(lexical scoping)。例如,下面我们展示一下使用了叫做`offset'的继承了的变量的嵌套函数:

bar (int *array, int offset, int size)
{
  int access (int *array, int index)
    { return array[index + offset]; }
  int i;
  /* ... */
  for (i = 0; i < size; i++)
    /* ... */ access (array, i) /* ... */
}

你应该有点明白了。不过,嵌套函数在OCaml中是十分有用而且十分常用的。下面是从一些实际应用代码中截取的嵌套函数的例子:

let read_whole_channel chan =
  let buf = Buffer.create 4096 in
  let rec loop () =
    let newline = input_line chan in
    Buffer.add_string buf newline;
    Buffer.add_char buf '\n';
     loop ()
  in
  try
     loop ()
  with
     End_of_file -> Buffer.contents buf;;

先无需关心这段代码干了什么——它还包含了尚未在本教程中讨论的很多概念。先关注中间的叫做“loop”的嵌套函数,它只有一个单元参数。你可以调用在函数read_whole_channel中调用loop (),但它并没有在这个函数外边定义。嵌套函数可以访问定义在主函数中的变量(这里loop可以访问局部名称buf)。

嵌套函数的形式和局部命名表达式的形式是一样的:let name 形参 = 函数定义 in

一般来说,你要将在新的一行上缩进函数定义,如上面的例子所示,同时记住如果函数是递归的,要使用let rec而非let(如上面例子所示)。

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

分享到:
评论

相关推荐

    用于微控制器 的 OCaml 通用虚拟机_OCaml_代码_相关文件_下载

    OMicroB 是一个 OCaml 虚拟机,专门用于在资源非常有限的设备上运行 OCaml 程序,例如 AVR Atmega32u4 微控制器(2.5 ko RAM)。 OMicroB 执行多次静态分析,以减少生成的最终可执行文件。 要求 OPAM2 + OCaml (&gt;=...

    ocaml-prog-pats:OCaml编程模式-随机技巧,“设计模式”等

    该软件包包含一些随机的编程技巧,“设计模式”,以及随着时间的推移,我在OCaml程序中实现高抽象水平的其他有用或至少启发性的想法。 一些可能展示了如何实现或多或少具有理论意义的概念(例如,箭头,monad),另...

    ollvm:用于OCaml的高级LLVM绑定

    ollvm在处理OCaml结构(列表,记录,变体等)上的方式有所不同。 提供一个Ez接口,以使LLVM IR写入愉快。 您可能还想使用LLVM IR,而不是整个LLVM编译器基础结构。 ollvm允许您独立于llvm库。 您可能希望使用提供...

    custom-lang-compiler:用OCaml编写的x86程序集的编译器

    编译器构建开发 掌握: 开发人员: ... 解析-这将解析源文件并输出解析后的结构。 评估-这将评估源文件并返回该函数的输出。 解释-这会将源文件解释为程序集。 codegen-这将生成类似于汇编的代码。 codegenx

    ocaml-algebra:用OCaml编写的代数系统

    ocaml-algebra-用OCaml编写的代数系统 该代数系统广泛使用OCaml模块和函子,可以很好地表示抽象的代数类型:群,环,场,代数... 它目前实现: 多项式(单变量和多元)。 线性结构,例如矩阵和向量。 分数字段...

    logs:OCaml的记录基础架构

    日志-OCaml的日志记录基础结构 %%版本%% 日志为OCaml提供了日志记录基础结构。 在可以独立设置报告级别的源上执行日志记录。 日志消息报告与日志记录分离,由报告程序处理。 基本库中分发了一些可选的日志报告...

    ppx_lens:OCaml预处理程序扩展和用于创建和使用镜头的小型库

    ppx_lens ppx_lens是OCaml PPX,用于在记录上生成“镜头”操作,这有助于促进对深层嵌套数据结构的功能更新。例子生成镜头功能以进行记录: # type glasses = { len : int ; bridge : int ; diam : int } [ @@ lens ...

    rust-ocaml-derive

    最重要的是,它每晚仅执行一个程序宏ocaml-ffi以简化编写存根函数的样板。 #[derive(Debug, Default, ToValue, FromValue)]#[ocaml(floats_array)]pub struct Vec3 { x: f32, y: f32, z: f32,}#[ocaml_ffi]pub fn ...

    ocaml-effects-tutorial:使用效果处理程序的并发编程(CUFP'17)

    配置安装多核OCaml编译器。大纲本教程的结构如下: 1.1。 1.2。 3.1。 4.1。 4.2。 4.3。 4.4。 溪流合作并发。 5.1。 协程5.2。 异步/等待异步I / O。 6.1。 阻止回显服务器6.2。 异步回显服务器结论。 本教程还...

    SPOC:使用OCaml进行流处理

    我目前在工作SPOC已在多种体系结构和系统(主要是64位Linux和64位OSX系统)上进行了测试。 它也应该与Windows一起工作。 为了能够使用SPOC,您将需要一台能够运行OCaml(显然)并且还与OpenCL或Cuda兼容的计算机。 ...

    res:OCaml库用于可调整大小的数组和字符串

    该OCaml库由一组模块组成,这些模块实现自动调整大小(=重新分配)消耗内存连续部分的数据结构。 这允许在数组(盒装和未装箱),字符串(缓冲区),位字符串和弱数组之间添加和删除元素,同时仍保持对元素的快速...

    Serialize and Deserialize Java 示例程序

    Serialize and Deserialize Java 示例程序。简单来讲,它的数据格式与json类似,但是在存储时对数字、多字节字符、数组等都做了很多优化,减少了无用的字符,二进制格式,也保证不用字符化带来额外的存储空间的增加...

    ppx_deriving_protobuf:用于OCaml的协议缓冲区编解码器生成器

    [@@ deriving protobuf] 派生protobuf是一个插件,可生成序列化程序并从OCaml类型定义中反序列化。 由赞助。 赞助的协议出口。安装可以通过安装派生protobuf : $ opam install ppx_deriving_protobuf用法为了使用...

    asmdot:[不稳定]快速,零复制且轻量级(Arm | Mips | x86)汇编程序,位于(C | C ++ | C#| Go | Haskell | Javascript | Nim | OCaml | Python | Rust)

    提供一个可扩展的Python框架来构建快速的零拷贝汇编程序。历史和目标该项目最初旨在以C语言创建一个快速,极简且不受限制的汇编器,该汇编器可以存在于单个文件中,并支持多种体系结构。 因此,构建了一个Python库...

    F# for Scientists

    (本书与《OCaml for Scientist》一书的组织结构类似) F#设计者Don Syme为该书作序。他在其中提到,“过去的30年中,我们看到了一个新职业的不断发展:科学领域的程序员(scientific programmer)。”一个好的...

    bin_prot:二进制协议生成器

    Bin_prot已成功部署在关键任务型金融应用程序中,存储了数千兆字节的结构化数据,这些结构化数据源自数千种类型定义,通常对于不崩溃的低延迟应用程序每天实时处理数百万条消息。 从版本2开始,该库应与OCaml当前...

    aqa-compiler:引入了从AQA伪代码编译为AQA程序集的功能,从而带来无限乐趣和收益!

    这是针对的编译器,既针对又针对ARM子集 。 编译器当前可以编译大多数AQA伪代码规范(几乎所有内容减去IO和数组/字符串文字)。 但是,AQA程序集没有用于间接跳转的功能,使得子例程不... 程序的结构类似于C,是一系列

    theta-idl:使用代数数据类型定义应用程序之间的通信协议

    Theta最初的想法是受启发的, 是一种使用OCaml类型指定JSON模式的工具。 例子 这是一个示例Theta模式,分为两个文件( ids.theta )和( music.theta ),可在guide/example目录中使用。 ids.theta : language-...

    咋把dex文件变成java源码-redexer:Dalvik字节码的Redexer二进制检测框架

    应用程序二进制文件。 该工具能够将 DEX 文件解析为内存中的数据结构; 推断应用程序使用哪些参数使用某些权限(我们将此功能命名为 RefineDroid); 修改和解析该数据结构以生成输出 DEX 文件(我们将这些功能命名...

    opam-arm-repository:ARM交叉编译器

    这个OPAM信息库包含一个用于ARM的OCaml交叉编译器,以及一些为linux-armhf编译的OCaml库和程序。 请注意,这里找到的大多数食谱都是改编。 我会调整所需的内容,但我很乐意接受PR。 以下注释也改编自原始存储库:...

Global site tag (gtag.js) - Google Analytics