LoginSignup
0
0

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-02-28

入門Ⅱから,だいぶ関数型言語の色が強くなってきました.今回は関数型な書き方の最大の山場です.頑張りましょう!

Tuple

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

tuple_compare
// C# 6以前
var two_pair = Tuple.Create(1,2);
var three_pair = Tuple.Create(1 + 3, 2, 3);
// C# 7以降
var two_pair = (1,2);
var three_pair = (1 + 3, 2, 3);
tuple_compare
def two_pair = (1,2);
def three_pair = (1 + 3, 2, 3);

カッコだけで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; //Error!

関数に引数として渡すとき,Tuple[T1,T2]を,T1, T2に分解することが来ます.

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

Variant

バリアントはOcamlらへんから来た概念です.VisualBasicのVariantとは全く違います
まずは基本形.ディスプレイサイズに関係するEnumを作ろうとします.

variant_1
variant DisplaySize {
  | VGA
  | HD
  | FullHD
}

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

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

しかし,Otherの時は,縦横のサイズを詳しく取得できません.そんな時に,バリアントが役立ちます.

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

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

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

C#的に,バリアントは次のような構造になるそうです.(publicとか邪魔なので省いてます)

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) => //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_background(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