Nim教程翻译(二)
来源:互联网 发布:chrome淘宝比价插件 编辑:程序博客网 时间:2024/05/30 05:24
原文地址:http://nim-lang.org/docs/tut1.html
Nim教程翻译(一)地址:http://blog.csdn.net/dajiadexiaocao/article/details/46340087
注:本文与Nim教程翻译(一)都属于Nim Tutorial(Part I)中的内容!
迭代器
让我们回到无聊的计数实例:
可以写一个conutup过程支持这个循环吗?让我们试试
然而,这不工作。问题是这个过程应该不仅return,而且需要返回和continue在一个迭代器完成之后。这个返回和继续称为一个yield语句。现在剩下的唯一要做的事情是用iterator替换proc关键字,这是我们的第一个迭代器:
迭代器看起来与过程很像。但这存在几点很大的不同:
- 迭代器只能被循环调用。
- 迭代器不能包含return语句,过程不能包含yield语句。
- 迭代器没有隐含的result变量。
- 迭代器不支持递归。
- 迭代器不能提前声明,因为编译器必须能够内联一个迭代器(这个限制将会在未来版本的编译器中消失)。
然而,你也可以使用一个封闭的迭代器来获得一组不同的限制。详情见第一类迭代器。迭代器可以有相同的名字和参数作为一个过程,基本上它们有自己的命名空间。因此它常见的做法是积累过程中相同名字的迭代器的结果,并将它们作为一个序列返回,例如strutils模块的划分。
基本类型
这个部分详细的讨论基本内置类型,以及可用于它们的操作符。
布尔类型
在nim中boolean类型用被命名为bool,它包含两个预定义的值:true和false。while,if,elif,when语句中的条件类型需要是bool类型。
这些操作符:not,and,or,xor,<, <=, >, >=, !=, ==都为bool类型定义了。and和or操作符执行简捷评估(自己理解:当and或or链接多个表达式的时候,比如:p1 and p1,只有当p1为真时,才会评估p2)。例如:
字符型
在nim中字符类型被命名为char。它的大小是一个字节。因此它不能表示一个UTF-8字符,只是它的一部分。这样做的原因是效率:在绝大多数情况下使用,由此产生的程序仍将会处理UTF-8,因此UTF-8是特意为这设计的。字符常量用单引号括起来。
字符可以用==, <, <=, >, >=操作符作比较。$操作符将一个字符型转化为一个字符串型。字符不能混合整形;为了得到一个字符的序号值使用ord过程,将一个整形转化为一个字符型用chr过程。
字符串
在nim中字符串变量是易变的,所以拓展字符串是很有效率的。nim中的字符串以\0结束,并有一定的长度。可以用内置的len函数取得字符串的长度,字符串长不包括结束的\0.访问终端的\0不是错误的,经并且常可以利用结尾的0来简化程序。
对于字符串的赋值操作是用来复制字符串。你可以使用&操作符用来连结字符串以及add添加字符串。
字符串是按字典序进行比较的。所有的比较操作符都是可以使用的。按照规定,所有的字符串都是UTF-8字符串,但这不是强制的。例如,当从二进制文件读取字符串的时候,它们仅仅是一个字节序列。这个s[i]下标操作意味着字符串s的第i个字节,不是第i个字符。(注:unichar是两字节长的char,代表unicode的一个字符)
字符串变量用一个特殊的值进行初始化,叫做nil。然而,由于性能的原因大多数字符串操作不能处理nil(引起产生一个异常)。你可以用空字符串""而不是nil作为这个空值。但是""经常在堆中创建一个字符串对象,所以在这里要做个权衡。
整型
nim有这些内置的整数类型:int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64。
默认的整数类型是int。整型常量可以有一个类型后缀来标记它们成为另外一种整数类型:
整型经常被用来计算驻留内存里的对象,所以整型的与指针有相同的大小。
常见的操作符+ - * div mod < <= == != > >=都已为整形定义。and,or,xor,not操作符也为整形定义并且提供按位操作。左移位用shl操作,右移位用shr操作。移位操作经常将它们的参数作为无符号数处理。通常的乘法或者除法可以实现算术移位。
无符号操作所有环绕,它们不能导致overflow(上溢)或者underflow(下溢)错误。
在表达中使用不同的整形类型时将会执行自动类型转换。然而,如果类型转换丢失信息,就会抛出EOutOfRange异常(如果这个错误无法在编译时检测)
浮点类型
nim有这些内置的float类型:float float32 float64。
默认的float类型是float。在当前的实现中,float总是占64位。
float字面值可以用一个类型后缀将它们标记成为另一种float类型:
通常的操作符+ - * / < <= == != > >=已经为float定义,并且遵守IEEE规范。
在表达式中用不同的float类型将执行自动类型转换:较小的类型转换成更大的类型。整形不能自动的转换为float类型,反之亦然。toInt和toFloat过程可以用于整型和浮点类型的转换。
类型转换
在nim中的基本类型转换是通过用类型作为一个函数执行的:(基本数据类型的关键字本身就是一个方法,可以用这些方法完成类型转换)
内置类型表示
如前面所提到的,内置的$操作符可以将任意基本类型转化为一个字符串型,然后你可以用echo过程将它们打印输出到屏幕。然而,高级类型或者你自己定义的类型不会与$操作符协同工作,直到你为它们定义了$操作符。有时,你仅仅想调试一个复杂类型的当前值而不必写其$运算符。你可以使用repr过程,它有效于任何类型甚至是带有周期的复杂数据图。下面的例子展示了,即使对于基本类型在$和repr之间也存在不一样的输出。
高级类型
在nim中可以在一个type语句中定义新类型:
枚举和对象类型只能在一个type语句中定义,不能在type语句以外定义。
枚举类型
一个枚举类型变量只能被赋值一个有限的集合。这个集合有有序的符号组成。每个符号内部映射到一个整数值。第一个符号在运行时用0表示,第二个用1表示等等。例如:
所有的比较运算符都可以用枚举类型。
一个枚举符号可以避免歧义:Direction.south。
$操作符可以将任何枚举类型的值转换为它的名字,ord过程转化为它们基本的整数值。
为了更好的连接到其他语言中,枚举类型符号可以被赋值一个明确的序号值。然而,这个序号值必须是升序类型。一个符号的有序值没有明确的给出将会被赋值为前一个符号值+1.
一个明确的有序枚举可以有孔 (注:一个明确的有序类型的序数值可以不是连续的值)
序数类型
连续的枚举类型,整形,字符,以及布尔类型(和子类型)称作序数类型。序数类型有相当多的特殊操作:
操作
ord(x) 返回x的整型值
inc(x) x的值+1
inc(x, n) x的值+n,n为整数
dec(x) x的值-1
dec(x, n) x的值-n,n为整数
succ(x) 返回x的下一个值
succ(x, n) 返回x后的第n个值
pred(x) 返回x的前一个值
pred(x, n) 返回x的第前n个值
inc,dec,succ,pred操作会失败通过产生EOutOfRange(越界),EOverflow(溢出)异常。(如果代码编译已经有适当的运行时检查打开)
子类型
子类型是一个来自整形或者枚举类型(基本类型)的范围值。例如:
Subrange是一个int的子类型,它只能容纳从0到5的值。给Subrange子类型变量赋任何值都会出现编译或运行时错误。从基本类型给它的一个子类型赋值是允许的(反之亦然)。
系统模型定义了重要的自然类型范围[0...high(int)](high返回最大值)。其他编程语言授权自然类型使用无符号数。这通常是错误的:你不想无符号计算仅仅是因为这个数不能为负。nim的自然类型帮助避免这种常见的编程错误。
集合
集合类型是一个集合的数学概念。集合的基类型只能是序数类型。原因是集合是作为高性能的位向量实现。
集合可以通过集合构造器来构造:{}是空集合。空集合与任何具体的集合类型是类型兼容的。构造器也可以用于包含元素(和元素的范围)。
下面是集合类型支持的操作:
操作 含义
A + B 连接两个集合
A * B 两个集合的交集
A - B 两个集合的差集
A == B 集合相等
A <= B 子集关系(A是B的子集或者与B相等)
A < B 强子集关系(A是B的一个真正的子集)
e in A (集合成员) (A contains element e)
e notin A A不包含元素e
contains(A, e) A包含元素e
card(A) A集合的势(A中包含的元素个数)
incl(A, elem) A中添加一个元素
excl(A, elem) A中减少一个元素
集合通常用于在一个过程中定义一个flags类型。这是一个非常清楚(和类型安全)解决方法相比于仅仅定义整形常量。
数组
一个数组是一种简单的固定长度的容器。数组中的每个元素具有相同的类型。数组的索引类型可以是任何序数类型。
数组可以通过[]构造:
标记x[i]是用来访问x的第i个元素。数组访问总是进行边界检查(在编译或者运行时)。这些检查可以禁用通过编译或调用编译器的命令行开关——bound_checks:off command line switch
数组是值类型,像其他nim类型,赋值操作将复制整个数组内容。
内置的len过程返回数组的长度。low(a)返回数组最小有效下标,high(a)返回数组最大有效下标。
嵌套的数组语法在其他语言中是附加更多的括号,因为通常每个维度与其他限制相同的索引类型。在nim中你可以有不同的维度带有不同的索引类型,所以嵌套语法有些不一样。在前面的例子中,一个level被定义为一个数组枚举索引通过另一个枚举类型。我们可以添加以下的代码增加一个light tower类型在高度细分通过访问它们的整形索引:
注意:内置的built-in过程仅仅只返回数组的第一维的长度。另一种方式定义LightTower表现出更好的嵌套性质,将省略前面的LevelSetting类型的定义,相反将它直接嵌入作为第一维度的类型:
这是相当常见的数组从0开始,所以这是一个快捷的语法,指定一个范围从0到指定的下标值减一:
序列
序列类似与数组,但它有动态长度可以在运行时改变(就像字符串)。由于序列是可变大小的,所以它们总是在堆上分配以及垃圾收集。
序列总是带有一个int类型索引从0位置开始。len,low,high操作同样作用于序列。x[i]符号可以用于访问x的第i个元素。
序列可以通过数组构造器[]结合数组序列操作符@来构造。另一种为序列分配空间的方法是调用内置的newSeq过程。
一个序列可以被传递给一个开放数组的参数。
Example:
序列变量用nil初始化。然而,由于性能的原因很多的序列操作不能处理nil(导致引发一个异常)。因此应该用空序列@[]而不是nil作为空值。但是@[]在堆上创建了一个序列对象,所以在这里需要做一个权衡 。
for语句可以使用一个或两个变量当用于一个序列的时候。当你使用一个变量的形式,这个变量将会持有序列提供的值。for语句从系统模型的items()迭代器循环这个结果。但是如果你使用两个变量的形式,第一个变量将保存索引位置,第二个变量将保存值,第二种形式for语句是从系统模型的pairs()迭代器循环结果。
开放数组
注意:Openarrays只能用于参数。
通常固定大小的数组被证明会过于呆板;过程应该能够处理不同大小的数组。openarray类型允许这样。Openarray总是有一个int型索引从位置0开始。len,low,high操作同样适用于open arrays。任何带有兼容基类型的数组都可以被传递到一个openarray参数,索引类型并不重要。
openarray类型不能被嵌套:多维的openarray不被支持因为这很少用到以及它不能有效的进行。
可变参数
一个可变参数就像是一个openarray参数。然而,它也是一种方式实现传递可变的参数个数给一个过程。编译器自动地将参数列表转换为一个数组。
如果变参数在程序头中是最后一个参数这个转变才会发生。它也可能在这样的背景下执行类型转换:
在这个例子中$应用于任何参数,它被传递给参数a。注意:$应用于字符串是一个nop。
片段
片段在语法中类似与子界类型,但是它用于一个不同的背景下。一个片段仅仅是一个类型对象,它包含两个边界,a和b。一个片段本身不是非常有用,但是其他的集合类型定义操作接受片段对象定义范围。
在上面的例子中,片段被用于修改一个字符串的一部分,甚至使用了一个负数索引。片段边界可以容纳任何值的支持通过它们的类型,但是这是过程使用了片段对象,它定义了接受什么样的值。
元组
一个元组类型定义不同的命名域以及域的顺序。构造器()可以用来构造元组。构造器中的域的顺序必须和元组定义的域的顺序一致。不同的元组类型是相同的如果它们的指定域在相同的顺序中有相同的类型以及相同的名字。
元组的赋值操作赋值每个要素。标记t.field用于访问一个元组的部分。另一个符号是t[i]访问第i个部分。这里i需要是一个常数。
即你不需要使用type声明一个元组,元组用不同的域名称创建将会被认为不同的对象,尽管它们有相同的字段类型。
元组可以被拆箱在变量赋值期间(and only then!)。这可以方便的直接分配元组领域的单独命名变量。这方面的一个例子是从OS模块同时返回目录,名字和一个路径的拓展名的splitfile过程。元组拆包工作要围绕你想指定的开箱的值使用括号,否则你将所有的个体变量分配相同的值!例如:
元组拆箱只工作在var或let块。下面的代码不能编译:
引用和指针类型
引用(类似与其他编程语言中的指针)是一种介绍多对一关系的一种方法。这意味着不同的引用可以指向和修改相同的内存单元。
在nim中分为追踪引用和非追踪引用(反追踪)。非追踪引用也叫做指针。跟踪引用指向一个垃圾收集堆对象,反跟踪指向手动分配的对象或内存中其他地方的对象。因此反跟踪引用是不安全的。然而对于某些低级操作(访问硬件)反跟踪引用是不可避免的。
追踪引用用ref关键字声明,反追踪引用用ptr关键字声明。
空[]下标符号可以用于解参考一个引用,引用指向的检索项的意义。.(访问一个元组或者对象域操作)和[](数组/字符串/序列索引操作)操作符执行隐式解引用操作对于引用类型:
为了创建一个新的追踪对象,内置函数new()必须被使用。为了处理反追踪内存函数alloc(),dealloc(),realloc()函数可以使用。系统模块的文档包含更多的信息。
如果一个引用指向空,它的值为nil.
过程类型
一个程序类型是一个(有些抽象)程序的指针。空值是一个允许的程序类型变量的值。Nim用程序类型来实现功能编程技术。
Example:
关于程序类型的一个微妙的问题是:程序的调用约定会影响类型的兼容性。只有具有相同调用约定的程序才具有兼容性。手册中列出了调用约定的不同。
模块
nim支持将一个程序分成若干个模块的概念。每个模块都有自己的文件。模块可以使信息隐藏和分开编译。一个模块可以通过import语句访问其他模块中的符号。只有被星号标记的顶层符号被导出。
上面的模块出口是x和*,不是y。
一个模块的顶层语句在程序开始时执行。例如这可以用来初始化复杂的数据结构。
每个模块都有一个特殊的魔法常数isMainModule,如果这个模块作为主文件被编译,那么这个常数就为真。正如上面的例子所展示的这非常有用对于测试模块内嵌入。
相互依存的模块是可能的,但强烈反对,因为这样没有其他模块时一个模块不能重复使用。
编译模块的算法:
- 像往常一样编译整个模块,在import语句时递归。
- 如果这是一个循环只导入已经解析的符号(那是出口);如果一个未知的标识符出现,然后终止。
通过一个例子这是最好的说明:
一个模块的符号要符合module.symbol的语法。即使一个符号是模糊的,它也必须符合modual.symbol语法。一个符号是模糊的如果它被定在了两个不同的模块中(或者更多)并且它们都被第三个模块导入。
但是这些规则并不适用于过程或者迭代器。这里应用重载规则:
不包括符号
正常情况下import语句会带来所有的输出符号。这可以被限制通过命名符号,它应该使用except修饰语被排除用。
From statement
我们已经看到简单的impoer语句只是导入所有的输出符号。另一种可选择的方法是只导入列出的符号是from import语句:
from语句也可以在符号上强行命名空间限定,从而使符号可用,但需要有资格被使用。
由于模块的名称一般都是很长的描述,你也可以定义一个较短的别名使用当使用限定符号的时候。
Include statement
include语句与导入一个模块有一些根本上的不同:include仅仅包含一个文件的内容,include语句将一个大的模块分割成几个文件是有用的。
Part 2
所以,既然我们已经完成了基础,让我们看看除了关于程序编程的一个好的语法之外nim提供了什么:Part II
更多相关nim内容参见Nim教程翻译(三)
- Nim教程翻译(二)
- Nim教程翻译(三)
- Nim教程翻译(一)
- Unity3D Shader官方教程翻译(二)
- Unity3D Shader官方教程翻译(二)
- Unity3D Shader官方教程翻译(二)
- Unity3D Shader官方教程翻译(二)
- Rabbitmq教程翻译(二)Work Queues
- Unity3D Shader官方教程翻译(二)
- Unity3D Shader官方教程翻译(二)
- Unity3D Shader官方教程翻译(二)(ShaderLab)
- POJ 2068 Nim 已翻译
- POJ 2975 Nim 已翻译
- Nim教程(I )
- nim manual(二)
- Java 3D API官方教程[翻译二]
- [翻译]asp.net ajax xml-script教程(二)
- Java 3D API官方教程[翻译二]
- css3基本选择器
- Skinned Mesh原理解析和一个最简单的实现示例
- [Astar2015]矩形面积解题报告
- 使用Hadoop ACL 控制访问权限
- h5播放音乐
- Nim教程翻译(二)
- 4-1
- vim保存并退出
- css3属性选择器
- UNP chapter3 习题3.3
- 【HAOI 2007】【BZOJ 1053】反素数ant
- C语言笔记
- 番外篇(一)保存打开过的窗体
- ORA-12514 TNS 监听程序当前无法识别连接描述符中请求服务 的解决方法