更多nim教程和技巧, 可以关注:) 「www.angluca.com」
函数名的使用
proc my_func(a:int): int = return a + 100 echo m_y_f_UN_C(1) echo myFunc(2) echo myfunc(3)
上面的代码编译是正确的. 原来函数名是忽略大小写和下划线的, 所以爱怎么用函数名就怎么用吧, 峰驼和下划线党不会起冲突了. (注意: nim里名对首字母大小写敏感, 而且首字不能用下划线.)
使用pragma: discardable 的函数可以直接使用了.
proc test(): int {.discardable.} = return 9 test() #discard test() #如果没有discardable需这样写.
var int_array : array[42..44, int] int_array[43] = 100 # int_array[45] = 1 #错误 超过定义的范围了 echo int_array[42] #0 echo int_array[43] #100 int_array = [12,42,66] #同样也可以这样赋值 for i,v in int_array: echo i, "=", v #i是索引, v是值 #结果 #42=12 #43=13 #44=14 var ch_array : array['A'..'z', int] #从A-Z-a-z ch_array['a'] = 111 echo ch_array['a'] #111###### 枚举值的查找 nim可以直接用字符串得到枚举值, 省掉不少麻烦.
import strutils type MyEnum = enum enA, enB, enC, enuD, enE doAssert parseEnum[MyEnum]("enu_D") == enuD #还记得使用函数可名不分下划线和大小写么. doAssert MyEnum(0) == enA doAssert enA.ord == 0 doAssert $enA == "enA"###### enum的复杂使用 很多时候可以简化功能的实现方法.
type MyEnum = enum valueA = (0, "my value A"), valueB = "value B", valueC = 2, valueD = (3, "abc") echo ord(valueA) echo ord(valueB) echo ord(valueC) echo ord(valued) echo (valueA) echo (valueB) echo (valueC) echo (valued)
显示结果
0 1 2 3 my value A value B valueC abc
type ObjA = object of RootObj ObjB = object of ObjA ObjC = object of ObjB method pt(obj:ObjA) = echo "A" method pt(obj:ObjB) = echo "B" method pt(obj:ObjC) = echo "C" proc testObj(obj:ObjA)= obj.pt var a = ObjA() b = ObjB() c = ObjC() a.testObj b.testObj c.testObj #结果 #A #B #C
proc test(a:int, b:var int) = #a为不可改, b为可修改参数 a += 1 #错误! a是不可改的 b += 1 #正确
from os import nil echo os.isHidden("~/") #正常 echo isHidden("~/") #错误, 必须加os模块名
import strutils var num = parseInt("123") var fnum = parseFloat("5.23") echo num echo fnum #123 #5.23
var str: string = "Hello!" var cstr: cstring = str var newstr: string = $cstrstring是可以直接转为cstring使用, 而cstring转string则需要加上$符号.
import typetraits var sequ = @['h', 'e', 'l', 'l', 'o'] sequ.add('!') echo sequ.type.name echo sequ let str = cast[cstring](addr(sequ[0])) echo str.type.name echo str #seq[char] #@[h, e, l, l, o, !] #cstring #hello!
import typetraits type Person = object of RootObj name*: string # * 表示能在其它模块里使用, 公有 age: int # 只能本文件用, 私有 Student = ref object of Person id: int var student: Student person: Person assert(student of Student) # of 值为 true, is 也可以 assert(student of Person) # of 可以判断继承类型所以为 true, is 则类型必须相同所以为false. echo student.type.name # 名为"Student" var str = "test" assert(str is string) #值为 true
of可以判断是否为继承类, is只能判断是否为当前类型.
type Data = tuple[x, y: int, s: string] # 为 Data 在堆上分配内存 var d = cast[ptr Data](alloc0(sizeof(Data))) # 通过 GC 在堆上创建一个新的字符串 d.s = "abc" # 告诉 GC 该字符串不再需要 reset(d.s) # 释放内存 dealloc(d)
如果在 dealloc(d) 之前没有调用 reset(d.s), 那么这个字符串内存永远不会释放. 上面的例子也给出了底层编程的两个重要特点: sizeof() 函数返回一个类型的字节大小, cast[] 可以使编译器强制把内存用某个特定类型表示, 只有必要的情况下使用 cast[], 它破坏类型安全且容易滋生 bug.
注意: 上面的例子可以工作是因为其内存已经通过 alloc0() 初始化为 0, 也因此 d.s 被初始化为 nil 并且可以对其赋值.
type PObject = ref TObj not nil TProc = (proc (x, y: int)) not nil proc p(x: PObject) = echo "not nil" p(nil) # 这里提示异常 var x: PObject p(x) # 这里也是 var str:string not nil = nil # 这还是
在开发操作系统内核时,划分用户空间和内核空间是非常有用的.
type Kernel = object Userspace = object var a: Kernel ptr Stat var b: Userspace ptr Stat # 下面的内容不能编译,因为指针类型不兼容. a = b如例子所示,ptr 也能用做二元操作符,region ptr T 是 ptr[region, T] 的简写. 为了简化泛型编码,ptr T 表示 ptr[R, T] (任意 R) 的派生子类. 作为特殊的类型规则,ptr[R, T] 不兼容 pointer.
# from system proc dealloc(p: pointer) # wrap some scripting language type PythonsHeap = object PyObjectHeader = object rc: int typ: pointer PyObject = ptr[PythonsHeap, PyObjectHeader] proc createPyObject(): PyObject {.importc: "...".} proc destroyPyObject(x: PyObject) {.importc: "...".} var foo = createPyObject() # type error here, how convenient: dealloc(foo)未来方向: 内存区域也可以对 string 和 seq 有效 内置区域比如 private global local 在即将到来的 OpenCL 会更有用 内置区域可以构造 lent 和 unique 指针 赋值操作符可以附加到区域,以生成合适的写屏障.这意味着 GC 可以彻底在用户空间实现. ###### 函数类型的约定 函数类型是一个指向函数地址的指针。允许把 nil 作为一个函数的值。Nim 语言运用函数类型来达成函数式编程语言的技巧。
proc printItem(x: int) = ... proc forEach(c: proc (x: int) {.cdecl.}) =... forEach(printItem) # 不能编译,因为调用约定不同
type OnMouseMove = proc (x, y: int) {.closure.} proc onMouseMove(mouseX, mouseY: int) = # 使用默认的调用约定 echo "x: ", mouseX, " y: ", mouseY proc setOnMouseMove(mouseMoveEvent: OnMouseMove) = discard # ok,onMouseMove 使用默认的调用约定,该约定兼容 {.closure.} setOnMouseMove(onMouseMove)
一个极其微妙的细节是,函数的调用约定会影响类型的兼容:拥有相同调用约定的函数类型才能够彼此兼容。作为一个特殊的扩展,一个使用 {.nimcall.} 调用约定的函数,可以作为参数传递给一个期望调用约定是 {.closure.} 的函数参数。
var x = 0 var y: type(x) # y has type int
type 也可以用来确定一个 proc/iterator/converter 调用 c(X) 的结果类型:
import strutils # strutils contains both a ``split`` proc and iterator, but since an # an iterator is the preferred interpretation, `y` has the type ``string``: var y : type("a b c".split)
proc getInt(): ptr int= var inst {.global.} = cast[ptr int](alloc(4)) inst[] = 100 result = inst echo repr getInt() echo repr getInt()
proc test(v: array or seq) = #或者 array|seq echo repr v return var sz:array[9999999, int32] var sq:seq[string] test(sz) test(sq)用这种方法可以直接使用array或seq而不用定义变量类型了哦(当然也可以使用auto).
type class matches object any object type tuple any tuple type enum any enumeration proc any proc type ref any ref type ptr any ptr type var any var type distinct any distinct type array any array type set any set type seq any seq type auto any type需要两个以上类型合并才能使用这个方法, 其它类型也可以, 具体还得自己去体验.
var f = open("numbers.txt") defer: close(f) f.write "abc" f.write "def"
类似这个效果
var f = open("numbers.txt") try: f.write "abc" f.write "def" finally: close(f)
for i in countup(0, 10, 2): #降阶就是countdown echo i
类似c的
for(int i=0; i<=10 i+=2) printf("%d\n", i)
dumpTree: echo "en!" var x = 100 write(stdout, "x") writeln(stdout, x)
结果:
StmtList
Command
Ident !"echo"
StrLit en!
VarSection
IdentDefs
Ident !"x"
Empty
IntLit 100
Call
Ident !"write"
Ident !"stdout"
StrLit x
Call
Ident !"writeln"
Ident !"stdout"
Ident !"x"
import xxx as x
使用 from导入模块同样也可以制定别名哦, 这样不用写比较长的模块名了.
from xxx as x import nil
from导入模块的内容可以导入多个, 用逗号分隔.
#导入xxx里的x,y,z from xxx import x,y,z
nim c --cpu:i386 -t:-m32 -l:-m32 xxx.nim如果不加后面的两个-m32就会弹错, 也许以后会改的容易些. ###### 快速得到文件,路径,后缀名
import os var appname = getAppFilename() var (dir, name, ext) = splitFile(appname) echo dir, ", ", name, ", ", ext # c:\users\xxx, test, .exe echo appname.extractFilename() # test.exe
var inputs = cast[ptr array[10,int]](alloc0(sizeof(int) * 10)) inputs[0] = 99 echo repr inputs #echo cast[cstring](inputs) dealloc(inputs) var test = alloc0(10) zeroMem(test, 10) echo repr cast[ptr array[10,char]](test) #echo cast[cstring](test) dealloc(test)
ref 000000000018F050 --> [99, 0, 0, 0, 0, 0, 0, 0, 0, 0] ref 0000000000191050 --> ['\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0']如果没有ptr, 上面的数组就不是正确的地址, 而是指在错误的区域显示一堆乱数字, 所以不要用c的思想来看nim的类型变量.