Nim教程【十三】(调查:你会顺手帮博客园点个广告吗?)

简介: 类型转换 Nim支持显示类型转换和隐式类型转换 使用casts操作符完成显示类型转换工作, 显示类型转换工作是编译期完成的工作,是位模式的 隐式类型转换也是编译期完成的工作,不是位模式的 让我们来看一下下面的代码 proc getID(x: Person): int =   Student(x).id 在上面的代码中 如果x变量的类型不是Student类型(Person类型是Student类型的父类型) 那么会抛出一个InvalidObjectConversionError异常。

类型转换

Nim支持显示类型转换和隐式类型转换

使用casts操作符完成显示类型转换工作,

显示类型转换工作是编译期完成的工作,是位模式的

隐式类型转换也是编译期完成的工作,不是位模式的

让我们来看一下下面的代码

proc getID(x: Person): int =
  Student(x).id

在上面的代码中

如果x变量的类型不是Student类型(Person类型是Student类型的父类型)

那么会抛出一个InvalidObjectConversionError异常。

对象变型

通常情况下,可变的对象类型实际意义更大一些

(译注:这是面向对象三大要素之一“多态”的重要组成部分)

来看下面的代码:

# This is an example how an abstract syntax tree could be modelled in Nim
type
  NodeKind = enum  # the different node types
    nkInt,          # a leaf with an integer value
    nkFloat,        # a leaf with a float value
    nkString,       # a leaf with a string value
    nkAdd,          # an addition
    nkSub,          # a subtraction
    nkIf            # an if statement
  Node = ref NodeObj
  NodeObj = object
    case kind: NodeKind  # the ``kind`` field is the discriminator
    of nkInt: intVal: int
    of nkFloat: floatVal: float
    of nkString: strVal: string
    of nkAdd, nkSub:
      leftOp, rightOp: PNode
    of nkIf:
      condition, thenPart, elsePart: PNode

var n = PNode(kind: nkFloat, floatVal: 1.0)
# the following statement raises an `FieldError` exception, because
# n.kind's value does not fit:
n.strVal = ""

从上面的代码中可以看出

有继承关系的对象之间做类型转换非常简单

访问无效的对象属性会引发一个异常

方法

对象的方法有两个缺点

  • 程序员难以在运行期为一个类型增加方法(或者只能用丑陋的方法才能做到)

  • 很多时候方法的参数是不确定的

Nim为了避免类似的问题,不分配方法到类中

后面我们将了解Nim的动态绑定方法的特性

方法调用

可以使用点操作符调用对象的方法

obj.method(args)

而不是

method(obj,args)

如果没有参数,则可以省略小括号

obj.method

方法调用是不受对象限制的,来看看下面的代码:

echo("abc".len) # is the same as echo(len("abc"))
echo("abc".toUpper())
echo({'a', 'b', 'c'}.card)
stdout.writeln("Hallo") # the same as writeln(stdout, "Hallo")

我们再来看看面向对象的写法

import strutils

stdout.writeln("Give a list of numbers (separated by spaces): ")
stdout.write(stdin.readLine.split.map(parseInt).max.`$`)
stdout.writeln(" is the maximum!")

是不是感觉很糟糕呢?

属性

请看如下代码来了解对象的属性

type
  Socket* = ref object of RootObj
    FHost: int # cannot be accessed from the outside of the module
               # the `F` prefix is a convention to avoid clashes since
               # the accessors are named `host`

proc `host=`*(s: var Socket, value: int) {.inline.} =
  ## setter of hostAddr
  s.FHost = value

proc host*(s: Socket): int {.inline.} =
  ## getter of hostAddr
  s.FHost

var s: Socket
new s
s.host = 34  # same as `host=`(s, 34)

这个Socket类型,有一个host属性,获取这个属性的值时

执行第二个方法,设置这个属性的值时,执行第一个方法

(这个例子中也演示了inline方法)

我们可以在类型中重载方括号,以提供与数组相类似的属性

请看如下代码:

type
  Vector* = object
    x, y, z: float

proc `[]=`* (v: var Vector, i: int, value: float) =
  # setter
  case i
  of 0: v.x = value
  of 1: v.y = value
  of 2: v.z = value
  else: assert(false)

proc `[]`* (v: Vector, i: int): float =
  # getter
  case i
  of 0: result = v.x
  of 1: result = v.y
  of 2: result = v.z
  else: assert(false)

动态指派方法

需要使用method关键字来代替proc关键字

才能使用动态指派的特性

来看下面的代码

type
  PExpr = ref object of RootObj ## abstract base class for an expression
  PLiteral = ref object of PExpr
    x: int
  PPlusExpr = ref object of PExpr
    a, b: PExpr

# watch out: 'eval' relies on dynamic binding
method eval(e: PExpr): int =
  # override this base method
  quit "to override!"

method eval(e: PLiteral): int = e.x
method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)

proc newLit(x: int): PLiteral = PLiteral(x: x)
proc newPlus(a, b: PExpr): PPlusExpr = PPlusExpr(a: a, b: b)

echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))

再来看看下面的代码

type
  Thing = ref object of RootObj
  Unit = ref object of Thing
    x: int

method collide(a, b: Thing) {.inline.} =
  quit "to override!"

method collide(a: Thing, b: Unit) {.inline.} =
  echo "1"

method collide(a: Unit, b: Thing) {.inline.} =
  echo "2"

var a, b: Unit
new a
new b
collide(a, b) # output: 2

因为决议是从左到右执行的

所以最后一个collide方法优于前面两个collide方法

毕竟a和b都是Unit类型的

注意:Nim不产生虚方法表(C#.net是需要虚方法表的),

但是会生成调用树(这样做可以提升性能表现)

目录
相关文章
|
安全 Java Linux
市值 200 亿美元的云服务平台引争议;脉脉以旷工为由辞退员工被判赔 24 万;Android 版灵动岛插件上线|思否周刊
市值 200 亿美元的云服务平台引争议;脉脉以旷工为由辞退员工被判赔 24 万;Android 版灵动岛插件上线|思否周刊
179 0
|
数据可视化 程序员 Linux
全球 77.5% 的网站都在用 PHP;百度一程序员删库被判 9 个月;OpenCV 4.6 发布 | 思否周刊
全球 77.5% 的网站都在用 PHP;百度一程序员删库被判 9 个月;OpenCV 4.6 发布 | 思否周刊
152 0
|
SQL 前端开发 JavaScript
前端周刊第三十六期
前端周刊发表每周前端技术相关的大事件、文章教程、一些框架的版本更新、以及代码和工具。每周定期发表,欢迎大家关注、转载。
前端周刊第三十六期
|
数据采集 JavaScript 前端开发
前端周刊第三十三期
前端周刊发表每周前端技术相关的大事件、文章教程、一些框架的版本更新、以及代码和工具。每周定期发表,欢迎大家关注、转载。
前端周刊第三十三期
|
Web App开发 Rust 前端开发
前端周刊第十九期
前端周刊发表每周前端技术相关的大事件、文章教程、一些框架的版本更新、以及代码和工具。每周定期发表,欢迎大家关注、转载。
前端周刊第十九期
|
机器学习/深度学习 前端开发
掘金彩蛋:爱的魔力转圈圈
出于好奇,我就想看看掘金怎么实现的,原来几行非常简单的css就实现了这个好玩的彩蛋。
169 0
|
程序员 图形学 芯片
掘金收割机的年终总结,我用 Three.js 创建了一个"掘金城市" | 2021年终总结征文
我叫大帅,一个老程序猿。本文会在评论中抽一个幸运鹅获得掘金周边鼠标垫1份
247 0
掘金收割机的年终总结,我用 Three.js 创建了一个"掘金城市" | 2021年终总结征文
|
Web App开发 JSON 程序员
吐血推荐 | 珍藏多年的 Chrome 插件,务必收藏
熟话说,工欲善其事,必先利其器,Chrome 作为程序员使用最多的浏览器有着数不清的优点,简洁高效,强大的控制面板,支持各种插件等。当然也有一个一直被我们吐槽的缺点,就是内存占用高,好在现在硬件便宜,可是说是瑕不掩瑜。今天就给大家推荐一些自己常用的 Chrome 插件,让你的开发效率和逼格都提升一个档次。
130 0
吐血推荐 | 珍藏多年的 Chrome 插件,务必收藏
|
程序员
2019 CSDN博客之星第三名,感谢各位支持
2019 CSDN博客之星第三名,感谢各位支持
2019 CSDN博客之星第三名,感谢各位支持
|
存储 编解码 人工智能
首发!《长安十二时辰背后的技术秘籍》正式公开,速来下载
今天,阿里技术重磅发布电子书《长安十二时辰背后的技术秘籍:从智能算法到音视频创新技术》,已正式开放下载。了解在《长安十二时辰》的荧幕后,工程师在敲敲打打的代码之间,创造出智能算法和音视频技术的新高度。
6789 0