プロシージャ
プロシージャは他の多くの言語で言うところの関数です。プロシージャはproc
を使って新しく定義できます。
proc add(a, b: int): int =
a + b
echo add(1, 2) #=> 3
proc
は値を返すことが出来るので、まるで関数のようです。Nimにはfunc
というキーワードもあります。これは関数型プログラミングの純粋関数により近いものです。また、method
というキーワードもあります。現段階ではfunc
とmethod
は忘れていおいてproc
に注力します
戻り値
先のaddの例では、最後の式であるa + b
が戻り値になりました。プロシージャの戻り値がどのように決定されるかは、次のルールに従います。
result
という名前の変数が使用されていた場合、その値が戻り値となる- return文がある場合、それに続く式の値が戻り値になる
- 式を伴わないreturn文がある場合、
return result
と同じ意味になる result
もreturn文もない場合、プロシージャの最後の式が戻り値となる
先のaddの例では、最後のルールが適用されました。他のルールが適用されるように書き直すことが出来ます。
result
という名前の変数が使用されていた場合、その値が戻り値となる:
proc add(a, b: int): int =
result = a + b
return文がある場合、それに続く式の値が戻り値になる:
proc add(a, b: int): int =
return a + b
式を伴わないreturn文がある場合、return result
と同じ意味になる:
proc add(a, b: int): int =
result = a + b
return
このプロシージャをadd(1, 2)
と呼び出した場合、いずれも最初の例と同じである3
が戻り値になります。
パラメーター
パラメーター(parameters)とはプロシージャの引数を指します。プロシージャの呼び出し時に渡される値はアーギュメント(arguments)と読んで区別することが出来ます。Nimに限ったことではなくプログラミング一般において、必要なときにのみparametersとargumentsを使い分けることが多いです1)。どちらを指しているかは文脈から明らかなことが多いので、最近は単に引数とだけ呼ばれるのが普通です。
先のaddを例に取ると、aとbがパラメーターで、その型はintです。パラメーターはイミュータブルです。つまり、プロシージャ内で変更することは出来ません。
次のコードはコンパイルに失敗します。
proc setToOne(x: int) =
x = 1 # コンパイルエラー
エラーメッセージは次のとおりです。
Error: 'x' cannot be assigned to
var
キーワードを付けることで、プロシージャ内で値を変更できます。
proc setToOne(x: var int) =
x = 1
var x = 100
setToOne(x)
echo x #=> 1
プロシージャ内での変更が呼び出し元にまで影響を与えている、すなわち、参照渡しとなっていることに注意してください。
戻り値を無視する
Nimでは、値を返すプロシージャの呼び出しで、その戻り値を無視することは出来ません。
proc add(a, b: int): int =
a + b
add(1, 2) # コンパイルエラー
このプログラムはコンパイルできません。エラーメッセージは次のようになります。
Error: expression 'add(1, 2)' is of type 'int' and has to be used (or discarded)
エラーメッセージで指摘されている通り、戻り値を使用するか、破棄される(discarded)必要があります。破棄するにはdiscard文を使います。
discard add(1, 2)
これはコンパイルエラーになりません。
その他のプロシージャの特徴
プロシージャには便利に使えるようにするために、いくつか便利で重要な特徴があります。
名前付き引数
通常のプロシージャ呼び出しは、引数は左から順番に渡されます。名前付き引数を使うことで、プロシージャ定義の引数の順序に依存せず引数を渡すことが出来ます。
デフォルト値
プロシージャの定義で、パラメーターにデフォルト値を設定することが出来ます。
オーバーロード
パラメーターが異なる、同じ名前のプロシージャを複数定義することが出来ます。
演算子のオーバーロード
+
や==
となどの演算子にプロシージャを定義することで、その演算子を使った式でプロシージャが呼び出されるように出来ます。
前方宣言
プロシージャは、使用されるところでそのプロシージャが見えていなければなりません。プロシージャの本体が見えている必要はなく、そのシグネチャさえ見えていれば良いです。プロシージャを定義せずに宣言だけ行う方法があります。
proc add(a, b: int): int # 前方宣言
proc addOne(a: int): int =
add(a, 1)