Nim 程式設計教學:變數和資料型別

PUBLISHED ON FEB 7, 2018 — PROGRAMMING

實字

實字 (literal) 是指固定的值,簡單地說,就是在程式碼中寫死資料的值,例如:

  • true (布林)
  • 3.14159 (數字)
  • "Hello World" (字串)

變數

變數 (variable) 用來儲存值,待後續程式呼叫。變數像是附有標籤的盒子,標籤是變數名稱,盒子內存值。

宣告變數

在 Nim 程式中,宣告變數有三種方式:

  • var:宣告後值仍會變動的變數 (mutable)
  • let:宣告後值不再更動 (immutable)
  • const:僅能放入實字,值不能更動 (immutable)

var 使用例如下:

var msg = "Hello World"

echo msg

若修改 const 所宣告的值會引發程式錯誤:

const pi = 3.14159

# Error
pi = 4

由於 const 僅能放入實字,以下程式是錯誤的:

# Error
const input = readLine(stdin)

但可以使用 let,讀取後會轉為唯讀 (read-only) 狀態:

let input = readLine(stdin)

若變數在宣告後就不會更動,建議用 let 取代 var,以避免不當修改。

變數名稱

僅能以英文字母或 UTF-8 字母開頭,不能以底線開頭。以下是一些合法的 Nim 變數:

  • var
  • n1
  • aLongVariable
  • a_long_variable
  • 變數

由於一些 UTF-8 字母無法直接以鍵盤輸入,較不建議使用。

要注意的是,除了第一個字母外,Nim 的變數名稱不區分大小寫 (case-insensitive),也不區分撰碼風格 (style-insensitive)。如下例:

let aLongVariable = 3

# Case-insensitive
assert(alongvariable == 3)

# Style-insensitive
assert(a_long_variable == 3)

這樣的「特性」是好是壞,仍待商榷。目前來說,不過度依賴這個特性,保持一貫的撰碼風格,對維護專案來說較為有利。筆者個人習慣用駝峰式 (camel case) 來撰寫程式,仿照 Java 的命名風格,即類別和型別名稱為大寫開頭,方法為小寫開頭;但讀者可採用自己習慣的風格,Nim 編譯器不會強制規定程式碼風格。

保留字

以下是 Nim 的保留字 (keywords),保留字不能做為變數名稱:

addr and as asm atomic
bind block break
case cast concept const continue converter
defer discard distinct div do
elif else end enum except export
finally for from func
generic
if import in include interface is isnot iterator
let
macro method mixin mod
nil not notin
object of or out
proc ptr
raise ref return
shl shr static
template try tuple type
using
var
when while with without
xor
yield

雖然保留字看起來不少,不需要刻意背誦保留字,在撰寫程式的過程中自然記住即可。編輯器也會協助標出顏色 (syntax highlighting),協助我們辨識保留字。

型別

資料型別 (data type) 規範值在程式中可用的操作,像是數字間可加減或字串可相接等。Nim 支援以下內建型別:

  • 布林 (boolean):bool
  • 數字 (number)
    • 整數 (integer)
      • 有號:int8int16int32int64int
      • 無號:uint8uint16uint32uint64uint
    • 浮點數 (floating point)
      • float32float64float128float
  • 字元 (character):char
  • 字串 (string):string
  • 列舉 (enumeration):enum
  • 陣列 (array) 和序列 (sequence)
  • 元組 (tuple):tuple
  • 集合 (set)
  • 參考 (reference) 和指標 (pointer):refptr
  • 程序 (procedure):proc
  • 物件 (object):object
  • void

我們會簡略地介紹這些型別,先大略看過一次即可,不需強記其中的細節。

布林

布林用於邏輯判斷中,只有 truefalse 兩種。Nim 不會將 0 或空陣列等值自動轉為布林,需明確寫出條件式。

數字

數字型別相對應於電腦內部的數字儲存方式,所以才會有多種型別。對於一般使用下,倒不需要斤斤計較,整數使用 int,無號整數使用 uint,浮點數使用 float 即可,這三種型別的範圍會根據電腦硬體而自動調整。

數字可表示為十進位數、十六進位數 0[xX]、八進位數 0o、二進位數 0[bB] 等。

以下程式可找出整數和無號整數的範圍:

# Using built-in strutils package.
import strutils

echo "int8 max: $1, min: $2".format(int8.high, int8.low)
echo "int16 max: $1, min: $2".format(int16.high, int16.low)
echo "int32 max: $1, min: $2".format(int32.high, int32.low)
echo "int64 max: $1, min: $2".format(int64.high, int64.low)
echo "int max: $1, min: $2".format(int.high, int.low)

echo "" # Line separator

echo "uint8 max: $1, min: $2".format(uint8.high, uint8.low)
echo "uint16 max: $1, min: $2".format(uint16.high, uint16.low)
echo "uint32 max: $1, min: $2".format(uint32.high, uint32.low)
echo "uint64 max: $1, min: $2".format(uint64.high, uint64.low)
echo "uint max: $1, min: $2".format(uint.high, uint.low)

註:目前 Nim 沒有提供浮點數的範圍;一般來說,單精數 (6 位精確度) 約在 10 的 38 次方左右,倍精數 (15 位精確度) 約在 10 的 308 次方左右。

數字計算的過程中,超出其極值,稱為溢位 (overflow) 或下溢 (underflow)。目前 Nim 不會警告程式設計者,需程式設計者自行確認和處理。如下例:

# Wrongly correct.
assert(int.high == int.high + 1)

註:雖然溢位和下溢的結果對程式設計者來說,通常是錯的,但大部分程式語言不會將其提升到錯誤 (error) 或例外 (exception) 的層級,而要使用者自行確認。

對於較大的數字運算,較好的方式是用大數運算套件,如 bignum 等。大數運算是用軟體來模擬數字儲存和運算,不受電腦硬體的限制,但速度相對較慢。

字元

Nim 的字元型別是 1 byte,無法用來表示 UTF-8 字元,僅能用來表示 ASCII 字元。由於 Nim 另外有字串型別,我們較少直接操作字元。Nim 另外有 Rune 型別,即支援 Unicode 字元,該型別位於 unicode 模組中。

字串

Nim 的字串有兩種型別,一種是平常使用的 string,一種則是和 C 字串相容的 cstring,一般情形下,使用前者即可。\ (backslash) 後的字串帶有特殊意義,像是 "\n" 在類 Unix 系統中代表換行字元,因此,要表示 Windows 路徑,需用 \\,像是 C:\\Program Files\\Nim 來表示。若前綴 r,表示原字串 (raw string),如 r"C:\Program Files\Nim"

以下範例表示多行字串:

let text = """foo
  bar
  baz
"""

echo text

# Remove indention
echo text.unindent

要注意的是,雖然字串帶有長度,但 UTF-8 字元一個字元可能對應一到多個 byte,所以 len 方法得到的長度可能不如預期,解決的方法是透過 unicode 套件來呼叫 runeLen 方法。如下例:

import unicode

assert("早安,你好".len == 15)
assert("早安,你好".runeLen == 5)

如果只用到英文的話,就不需要額外引用 unicode 套件。

列舉

列舉主要用於有限個值的型別,像是星期只有七個、月份只有十二個等。使用列舉的好處主要在於透過編譯器提供型別檢查。

陣列和序列

這兩者是線性 (linear) 容器 (collection),我們將於後續文章中介紹。

元組

這是非線性 (non-linear) 容器,我們將於後續文章中介紹。

集合

Nim 內建的集合不是通用的容器,僅能用於少數型別,主要的賣點是效率較佳,可搭配某些演算法來使用。不過,Nim 另外提供一個較為通用的集合容器,但效率則沒內建的集合來得好。我們將於後文介紹。

參考和指標

這個中文譯名可能會引人誤會,筆者在此說明一下。Nim 的參考 (reference) 和指標 (pointer) 在精神上都類似於 C 的指標,差別在於前者有受垃圾回收管理,而後者無。我們將於後文介紹參考和指標。

註:Nim 的參考和 C++ 的參考是不同的概念。

程序

由於 Nim 的程序可做為第一級物件 (first-class object),即值 (value),所以 Nim 也支援函數式程式。我們將於後續文章介紹。

註:Nim 的程序即一般程式語言的函式。

物件

物件是來建立新的自訂型別,也是物件導向程式的基礎。我們將於後續文章介紹。

void

void 用來表示程序沒有回傳值,但 Nim 的程序不需強制標示 void,標示 void 僅是為了程式碼的可讀性。

comments powered by Disqus