LoginSignup
1
0

Nemerle入門Ⅵ~配列,マクロの基本,遅延初期化,契約プログラミング~

Last updated at Posted at 2016-03-14

配列

コンストラクタ

array
// 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);

代入,変更

array
mutable ar = array(3);
ar[0] = 23;

全ては式である

ブロック

{から}までの囲まれた部分を一塊の値とすることができます.ブロックは,関数と同様に,その最後の値が其のブロックの戻り値となります.ブロック内で作られた変数は,ブロック外からは参照できないので,色々と便利です.

block
def n = { 
  def m = 1 + 3;
  m; // return
}
def m = 14;
/*
result:
  n = 4
  m = 14
*/

さて,このブロックですが,マクロを利用する上では大変重要なものとなります.if文の{}などは,すべてブロックです.あまり使いませんが,if(bool)のboolの部分もブロックを用いることによって,2行以上の処理を記述することができます.

if_block
if({
  def d =  DateTime.Now
  d.Day % 2 == 0
}) {
 ...
} else {
 ...
}

全ては式である

if match when unlessなどの殆どのマクロは,返り値を持ちます.F#に似ています.
そのため,三項演算子などは,if文で表現されます.

C#
var n = DateTime.Now.Day % 2 == 0 ? "今日の日を2で割り切れる" : "できない";
Nemerle
def n = if(DateTime.Now.Day % 2 == 0) "今日の日を2で割り切れる" else "できない";
VB.net
Dim n = Switch(DateTime.Now.Day = 1, "今日はついたち", _
               DateTime.Now.Day = 2, "今日はふつか", _
               True,                 "今日はそれ以外")
Nemerle
def n = match(DateTime.Now.Day) {
  | 1 => "今日はついたち"
  | 2 => "今日はふつか"
  | _ => "今日はそれ以外"
}

match文があったり,ブロックで複数の処理を記述できたりするので,より短く,よりわかりやすく記述することができます.

色々なマクロ紹介

これまで,言語機能として重要なマクロ(if,unless,リストのコンストラクタなど)を紹介してきましたが,
あんまり重要じゃないけど,便利なヤツを紹介します.

repeat

指定分だけ繰り返します.

repeat
repeat(10) {
  WriteLine("Hello, Nemerle.");
}

未実装関数

未実装関数を表現するときは,NotImplementedExceptionを用います.普段筆者は使いませんが.その時に,メソッドの一番最初に throw new NotImplementedExceptionを書きます.が,面倒で,見づらくね?ということで,未実装関数のマクロが実装されました.属性のように使えます.

notImplemented
[NotImplemented]
public TheSoLongMethod(name : string) : string
{
  // TODO: 頑張ってこれから処理を書く
  //頑張って書いている処理
}

頑張って書いてる処理をコンパイル時に破棄して,'throw new NotImplementedException'にすり替えてくれます.

遅延評価

例を見てみましょう.

lazy
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をはさみましたが,マクロで外すことができます.

lazy_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する必要があります.契約プログラミングで追加されたマクロ群はメソッドとクラスに付くことができます.書式は以下のようです.

assertions
MethodName(paramators) : classType <Assertions> { Code }

メソッド・クラスの宣言とその処理の間に条件を入れます.また,get/setにも付くことができます.

get_set_assert
get <Assertions> { Code }
set <Assertions> { Code }

事前条件

requiresキーワードを使います.
以下の様なGetAt(int)という関数を作ります.

requires
GetAt(n : int) : string {
  someArray[n]; //someArray is Array[string]
}

Array型は,0未満のインデックスはありません.自分で作った例外を起こしたいときはこうするでしょう.

requires
GetAt(n : int) : string {
  when(n > 0)
    throw MyException();
  someArray[n];
}

こんな時に,事前条件です.こんな感じに書きます.

require
GetAt(n : int) : string
    require n > 0  {
  someArray[n];
}

また,自分で作ったMyExceptionを使いたいので,otherwiseで,起こす例外を設定します.

require_otherwise
GetAt(n : int) : string
    require n > 0 otherwise throw MyException() {
  someArray[n];
}

事後条件

ensuresキーワードを使います.コード本体の後ろに書くのではなく,やっぱり前に書きます

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でないか調べる

require_otherwise
GetAt([NotNull] n : int) : string
    require n > 0 otherwise throw MyException() {
  someArray[n];
}

まあ,このコードではintはnull許容型ではないので,エラーになります.

Getterの便利マクロ

Getter_Macro
using Nemerle.Utitlity;

[Accessor(SomeField)]
mutable someField : int;

このようにクラス変数に使うマクロです.このマクロをつけると上のような変数が,

Getter_Macro
public SomeField : int { get { someField } }
mutable someField : int;

のように,Getterが生成されます.また,Flags引数に色々指定することで様々なGetterを生成できます.

Accerssor_Flags
[Accessor(SomeField, Flags = Internal | WantSetter, attributes(System.CLSCompliant(true), AnyOther)))]
mutable someField : int;

After
mutable someField : int;

[System.CLSCompliant(true), AnyOther] 
internal SomeField : int {
  get {
    someField
  } set {
    someField = value
  }
}

次回からマクロ編です.

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0