Nim教程【十四】

liulun發表於2015-06-30

網友@沉沒捕魚,贊助了一臺伺服器

這個系列的教程寫完之後,我們就要開始著手搭建Nim的社群了~

異常

Nim中的異常型別是物件型別

根據慣例,Nim中的異常型別的命名都應該以Error字尾結尾

在system模組中定義了異常型別的基類

所有的異常都應該派生自system.Exception型別

由於我們不清楚異常物件的生命週期,

所以必須在記憶體堆上為異常的例項分配空間

編譯器不允許開發人員在棧上為異常分配空間

你如果想丟擲一個異常,你必須為這個異常的msg屬性賦值

按照約定,只有在非常特殊的情況下才應該引發異常

打個比方:你不應該為打不開一個檔案而引發異常,

因為這個檔案有可能是不存在的。

 

raise語句引發異常

你可以使用raise語句引發一個異常

請看下面的程式碼

var
  e: ref OSError
new(e)
e.msg = "the request to the OS failed"
raise e

如果raise關鍵字後面美元后跟著一個異常的例項

那麼將再次引發最後一個異常

system模組中還為我們定義了一個newException的方法

請看如下程式碼:(是不是簡化了很多呢)

raise newException(OSError, "the request to the OS failed")

 

try語句捕獲異常

可以用try語句捕獲異常

# read the first two lines of a text file that should contain numbers
# and tries to add them
var
  f: File
if open(f, "numbers.txt"):
  try:
    let a = readLine(f)
    let b = readLine(f)
    echo "sum: ", parseInt(a) + parseInt(b)
  except OverflowError:
    echo "overflow!"
  except ValueError:
    echo "could not convert string to integer"
  except IOError:
    echo "IO error!"
  except:
    echo "Unknown exception!"
    # reraise the unknown exception:
    raise
  finally:
    close(f)

如果try程式碼塊中的程式碼,執行的時候引發了一個異常

那麼就會執行相應的except語句

如果後面的except語句沒有明確列出這個異常

那麼就會後自行最後一個空except語句

這看起來類似if else語句

如果存在finally語句,

那finally語句塊內的程式碼無論如何都會被執行的

如果一個異常沒有得到處理

那麼這個異常會從堆疊向上傳播

這就意味著,呼叫鏈上的方法有可能不會被執行

(如果他被執行了,那麼他一定在一個finally子句中)

如果你需要訪問異常物件

可以使用system模組中的getCurrentException方法或者getCurrentExceptionMsg方法

來看下面的示例程式碼

try:
  doSomethingHere()
except:
  let
    e = getCurrentException()
    msg = getCurrentExceptionMsg()
  echo "Got exception ", repr(e), " with message ", msg

 

在方法上做關於異常的註解

如果你用{.raises.}對某一個方法進行了註解

那麼在編譯期就會檢測這個方法(或這個方法所呼叫到的方法)會不會丟擲了某個異常

如果會,則編譯不通過

示例程式碼如下:

proc complexProc() {.raises: [IOError, ArithmeticError].} =
  ...

proc simpleProc() {.raises: [].} =
  ...

這一段我也沒怎麼看明白,大家自己看原文吧先

 

泛型

Nim語言的方法引數化、迭代器、等特性都是靠語言本身的泛型特性實現的

這個特性對於強型別容器是非常有用的

來看一下程式碼

type
  BinaryTreeObj[T] = object # BinaryTree is a generic type with
                            # with generic param ``T``
    le, ri: BinaryTree[T]   # left and right subtrees; may be nil
    data: T                 # the data stored in a node
  BinaryTree*[T] = ref BinaryTreeObj[T] # type that is exported

proc newNode*[T](data: T): BinaryTree[T] =
  # constructor for a node
  new(result)
  result.data = data

proc add*[T](root: var BinaryTree[T], n: BinaryTree[T]) =
  # insert a node into the tree
  if root == nil:
    root = n
  else:
    var it = root
    while it != nil:
      # compare the data items; uses the generic ``cmp`` proc
      # that works for any type that has a ``==`` and ``<`` operator
      var c = cmp(it.data, n.data)
      if c < 0:
        if it.le == nil:
          it.le = n
          return
        it = it.le
      else:
        if it.ri == nil:
          it.ri = n
          return
        it = it.ri

proc add*[T](root: var BinaryTree[T], data: T) =
  # convenience proc:
  add(root, newNode(data))

iterator preorder*[T](root: BinaryTree[T]): T =
  # Preorder traversal of a binary tree.
  # Since recursive iterators are not yet implemented,
  # this uses an explicit stack (which is more efficient anyway):
  var stack: seq[BinaryTree[T]] = @[root]
  while stack.len > 0:
    var n = stack.pop()
    while n != nil:
      yield n.data
      add(stack, n.ri)  # push right subtree onto the stack
      n = n.le          # and follow the left pointer

var
  root: BinaryTree[string] # instantiate a BinaryTree with ``string``
add(root, newNode("hello")) # instantiates ``newNode`` and ``add``
add(root, "world")          # instantiates the second ``add`` proc
for str in preorder(root):
  stdout.writeln(str)

上面的示例展示了一個泛型二叉樹

通過這個例子,您可以看到,可以用方括號來完成方法的泛型化、泛型迭代器等特性

 

相關文章