0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Nemerle入門Ⅳ ~tuple,バリアント,もっとパターンマッチ~

Last updated at Posted at 2016-02-28

Tuple

C# 7でやっと使いやすくなったTupleですが,Nemerleは以前より使いやすいです.
Listと同じく,Nemerle独自のTuple型(Nemerle.Builtin.Tuple)で説明していきます.
NemerleのTupleは標準で構造体です.

要素へのアクセスは,Item1, Item2 ...ではなく,インデクサでアクセスします.

tuple_access
def two_pair = (1,2);
WriteLine($"first=$(two_pair[0]), second=$(two_pair[1])");

Nemerleのタプルは,個々のデータは変更できません.readonlyになっています.

tuple_cannnot_change_data
mutable two_pair = (1,2);
two_pair[0] = 2; // Compilation Error!

関数に引数として渡すときにタプルを渡すと自動的に分解されます.

tuple_with_functions
def Add(x : int, y) { x + y };
def tup = (1, 2);
Add(tup) |> WriteLine;

Variant

バリアントは直和型です.VisualBasicのVariantとは全く違います.
まずは基本形.ディスプレイサイズに関係するEnumを作ろうとします.

variant_1
variant DisplaySize {
  | VGA
  | HD
  | FullHD
}

まんまEnumです.ですが,画面のサイズの種類は多く,これだけでは表せません.そこで,Otherを追加します.

variant_2
variant DisplaySize {
  | VGA
  | HD
  | FullHD
  | Other
}

しかし,Otherの時は,縦横のサイズを詳しく取得できません.そんな時に,バリアントが役立ちます.Otherのときだけ,HeightWidthという値を格納することできます.

variant_3
variant DisplaySize {
  | VGA
  | HD
  | FullHD
  | Other {
     Height : uint;
     Width  : uint;
    }
}

バリアントは実際に,以下のように使います.

variant_using
def MainDisplay = DisplaySize.HD();
def SubDisplay = DisplaySize.Other(800, 1280);

IL的に,バリアントは次のような構造になります.

variant_in_OPP
class DisplaySize {
  class VGA : DisplaySize {}
  class HD : DisplaySize {}
  class FullHD : DisplaySize {}
  class Other : DisplaySize {
    uint Height;
    uint Wdith;
    DisplaySize(uint _height, uint _width) {
      Height = _height;
      Width = _width;
    }
  }
}

もっとパターンマッチ

実践編などで,「もっともっとパターンマッチ」を入れようと考えていますが,今回は,Nemerleのパターンマッチの機能の全貌を紹介します.

tuple

基本的にはlistと同じようにします.長さの違うタプルは駄目です.

tuple_patternmatch
def t = (1, 2);
match(t) {
  | (1, _) => //Do Something
  | (1, 2) => //Do Something
  | _ => //Do Something if there aren't any matched condition.
  | (1, 2, 3) => // Compilation ERROR
}

バリアント

さっきのDisplaySizeがあるとしてパターンマッチングします.

variant_patternmatch
def s = DisplaySize.FullHD();
match(s) {
   | DisplaySize.VGA => //Do Something
   | DisplaySize.HD => //Do Something
   | DisplaySize.FullHD => //Do Something
   | DisplaySize.Other(Height = h, Width = w) =>
         WriteLine($"Your display size is h:$h w:$w");
   | DisplaySize.Other(_, 1280) as ss =>
         WriteLine(ss.Height);

値を持たない時は,そのまま,値を持つときは,上の例のように取り出すことができます.
其の時に,_で何でも許容することもできます.as ssはこの後紹介します.

型パターン/型スイッチ

Scala / C# 7.1のように,型パターン / 型スイッチすることができます.

patternmatch_typecheck
def o : object = 12;
match (o) {
  | i is int => WriteLine($"integer:$(i + 12)"); 
  | str is string =>  WriteLine("text:" + str);
  | _ => WriteLine("Unknown!");
}
C#でtypeswitch
object o = 12;
switch(o) {
  case int i: WriteLine($"integer:{i + 12}"); break;
  case string str: WriteLine("text:" + str); break;
  default: WriteLine("Unknown!"); break;
}
Scalaでtypepattern
val o : object = 12
o match {
  case i : int => println(s"integer:${i + 12}")
  case str : string => println("text:" + str)
  case _ => println("Unknown!")
}

レコードパターン

何重にもif文を重ねなくてもいいようになります.

patternmatch_record
class hoge {
  public hogeint : int;
  public hogestr : string;
}

def hogeMatch(h : hoge) {
  match(hoge) {
    | (hogeint = 1, hogestr = "HOGEHOGE") => //DoSomething
    | (hogeint = 2, hogestr = "HAGEHAGE") => //DOSomething
    | _ => //DoElse
  }
}

as

as <変数名>で,パターンマッチングした変数を<変数名>で扱うことができます.

patternmatch_as
def someFunc() { (12, 2) }
match(someFunc()) {
  | (_, 2) as t => WriteLine($"$(t[0])";
  | _ => //DoSomething
}

変数の定義文が減って,コードの視認性が上がると思います.

x::xsでの型指定

x_xs_select_type
match(["aaa", "bbb"]) {
  | (x : string) :: xs => //DoSomething
}

こんなこともできます.

複数条件

multi_condition
match (my_list) {
  | [1]
  | [1, 2] => WriteLine ("one or one, two");
  
  | [x]
  | [7, x] => WriteLine ($ "$x or seven, $x");

  | _ => //Else
}

以下の様なことはできません.(xが定義されない場合があるため)

Error_multicondition
| [1]
| [1, x] => WriteLine($"one, $x");

matchの省略

以下の様な場合,matchを省略できます.

non_write_match
def some(n : list[int]) {
  match(n) {
    | 3 :: _ => //DoSomething
    | [5, _] => //DoSomething
    | _ => //DoSomething
  }
}

            ↓

def some(_) {
  | 3 :: _ => //DoSomething
  | [5, _] => //DoSomething
  | _ => //DoSomething
}

この場合,someは引数にlist[int]を取ることになります.

with

言葉で説明するより見たほうが早いでしょう.

with_patternmatch
def foo (_) {
  | [x] with y = 3
  | [x, y] => x * y
  | _ => 42
}

//foo (3) = 9
//foo (3, 4) = 12
//foo (3, 4, 5) = 42

2行目で,もし,fooに長さ1のlistが渡された場合,2つ目の要素(3行目に定義されているy)を3として,3行目のコードに渡します.このコードは以下のコードと同じです.

with
def foo (_) {
  | [x] => foo_background(x, 3)
  | [x, y] => foo_background(x, y)
  | _ => 42
}
def foo_go(x : int, y) {
  x * y;
}

入門Ⅴへ

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?