配列
コンストラクタ
// 1次元配列(要素数3)
def intArray : array[int] = array [0, 1, 2];
// 2次元配列(多次元)(result: [[0, 1], [2, 3]])
def matrix : array.[2][int] = array.[2] [[0, 1], [2, 3]];
// 配列の配列(ジャグ)(result: [[0, 1], [2, 3]])
def jagged : array[array[int]] = array [array [0, 1], array [2, 3]];
// 要素数だけ指定(要素数2 * 3 = 6, 2次元配列で夫々要素数3)
def zeroArray : array.[2][int] = array(2, 3);
代入,変更
mutable ar = array(3);
ar[0] = 23;
全ては式である
ブロック
{
から}
までの囲まれた部分を一塊の値とすることができます.ブロックは,関数と同様に,その最後の値が其のブロックの戻り値となります.ブロック内で作られた変数は,ブロック外からは参照できないので,色々と便利です.
def n = {
def m = 1 + 3;
m; // return
}
def m = 14;
/*
result:
n = 4
m = 14
*/
さて,このブロックですが,マクロを利用する上では大変重要なものとなります.if文の{}
などは,すべてブロックです.あまり使いませんが,if(bool)
のboolの部分もブロックを用いることによって,2行以上の処理を記述することができます.
if({
def d = DateTime.Now
d.Day % 2 == 0
}) {
...
} else {
...
}
全ては式である
if match when unlessなどの殆どのマクロは,返り値を持ちます.F#に似ています.
そのため,三項演算子などは,if文で表現されます.
var n = DateTime.Now.Day % 2 == 0 ? "今日の日を2で割り切れる" : "できない";
def n = if(DateTime.Now.Day % 2 == 0) "今日の日を2で割り切れる" else "できない";
Dim n = Switch(DateTime.Now.Day = 1, "今日はついたち", _
DateTime.Now.Day = 2, "今日はふつか", _
True, "今日はそれ以外")
def n = match(DateTime.Now.Day) {
| 1 => "今日はついたち"
| 2 => "今日はふつか"
| _ => "今日はそれ以外"
}
match文があったり,ブロックで複数の処理を記述できたりするので,より短く,よりわかりやすく記述することができます.
色々なマクロ紹介
これまで,言語機能として重要なマクロ(if,unless,リストのコンストラクタなど)を紹介してきましたが,
あんまり重要じゃないけど,便利なヤツを紹介します.
repeat
指定分だけ繰り返します.
repeat(10) {
WriteLine("Hello, Nemerle.");
}
未実装関数
未実装関数を表現するときは,NotImplementedException
を用います.普段筆者は使いませんが.その時に,メソッドの一番最初に throw new NotImplementedException
を書きます.が,面倒で,見づらくね?ということで,未実装関数のマクロが実装されました.属性のように使えます.
[NotImplemented]
public TheSoLongMethod(name : string) : string
{
// TODO: 頑張ってこれから処理を書く
//頑張って書いている処理
}
頑張って書いてる処理をコンパイル時に破棄して,'throw new NotImplementedException'にすり替えてくれます.
遅延評価
例を見てみましょう.
class someClass {
public this() {
WriteLine("someClass was Intialized.");
}
public someMethod() : void {
//HogeHoge
}
}
mutable s = Lazy(someClass);
WriteLine("someClass was Initialized, wasn't it?");
s.value.someMethod();
結果は,こうなります.
someClass was Initialized, wasn't it?
someClass was Initialized.
その変数が必要になった時に初期化されます.
上記の場合は,s.value.someMethod()
と,value
をはさみましたが,マクロで外すことができます.
mutable s = Lazy(someClass);
WriteLine("someClass was Initialized,wasn't it?");
def lambdaMethod([lazy] mm : someClass) {
mm.someMethod();
}
lambdaMethod(s);
契約プログラミング
契約プログラミングとは,コード主体とは別の場所で,変数等の条件指定をするプログラミング手法です.Microsoft ReserchにSpec#というのがあったようですが,Nemerleでも同じことができます.その条件がfalse
になった時,(すなわち契約違反の時)Nemerleでは,Nemerle.Core.AssertionException
が投げられます.条件指定の場所は,以下の主に3つです.
事前条件: 関数の処理前に満たされるべき条件
事後条件: 関数の処理後に満たされるべき条件
不変条件: どのような状況下においても満たされるべき条件
その他
基本
Nemerle.Assertions
をusingする必要があります.契約プログラミングで追加されたマクロ群はメソッドとクラスに付くことができます.書式は以下のようです.
MethodName(paramators) : classType <Assertions> { Code }
メソッド・クラスの宣言とその処理の間に条件を入れます.また,get/setにも付くことができます.
get <Assertions> { Code }
set <Assertions> { Code }
事前条件
requires
キーワードを使います.
以下の様なGetAt(int)
という関数を作ります.
GetAt(n : int) : string {
someArray[n]; //someArray is Array[string]
}
Array型は,0未満のインデックスはありません.自分で作った例外を起こしたいときはこうするでしょう.
GetAt(n : int) : string {
when(n > 0)
throw MyException();
someArray[n];
}
こんな時に,事前条件です.こんな感じに書きます.
GetAt(n : int) : string
require n > 0 {
someArray[n];
}
また,自分で作ったMyException
を使いたいので,otherwise
で,起こす例外を設定します.
GetAt(n : int) : string
require n > 0 otherwise throw MyException() {
someArray[n];
}
事後条件
ensures
キーワードを使います.コード本体の後ろに書くのではなく,やっぱり前に書きます
private cache : string;
GetAt(n : int) : string
require n > 0 otherwise throw MyException("You selected valid num.")
ensures cache != null otherwise throw MyException("Return value is null.") {
cache == someArray[n];
cache;
}
不変条件
invariant
キーワードを使います.同じようにすればいいでしょう.
事前条件でnull
でないか調べる
GetAt([NotNull] n : int) : string
require n > 0 otherwise throw MyException() {
someArray[n];
}
まあ,このコードではintはnull許容型ではないので,エラーになります.
Getterの便利マクロ
using Nemerle.Utitlity;
[Accessor(SomeField)]
mutable someField : int;
このようにクラス変数に使うマクロです.このマクロをつけると上のような変数が,
public SomeField : int { get { someField } }
mutable someField : int;
のように,Getterが生成されます.また,Flags
引数に色々指定することで様々なGetterを生成できます.
[Accessor(SomeField, Flags = Internal | WantSetter, attributes(System.CLSCompliant(true), AnyOther)))]
mutable someField : int;
↓
mutable someField : int;
[System.CLSCompliant(true), AnyOther]
internal SomeField : int {
get {
someField
} set {
someField = value
}
}
〆
次回からマクロ編です.