第10回オフラインリアルタイムどう書くの参考問題をNemerleで

[第10回オフラインリアルタイムどう書くの参考問題](http://qiita.com/Nabetani/items/d819d1e5f2378317511e)

[第10回オフラインリアルタイムどう書くの参考問題をF#で](http://qiita.com/matarillo/items/80c2c22d3224cb7c448c)

以前にF#で書いたコードをNemerleに移植した。なので解き方は同じ。ただしテストケース部分は省略した。

```nemerle
using Nemerle.Collections;
using Nemerle.Utility;

using System.Collections.Generic;
using System.Console;

module Program
{
    Main() : void
    {
        WriteLine(Test("Qs9s3dJd10h"));
        _ = ReadLine();
    }
    
    enum Suit { | Spade | Heart | Diamond | Club }
    enum Rank { | R2 | R3 | R4 | R5 | R6 | R7 | R8 | R9 | R10 | RJ | RQ | RK | RA }
    type Card = Suit * Rank;
    type Hand = list[Card];

    DirectProduct[T](xss : list[list[T]]) : list[list[T]]
    {
        xss.FoldRight([[]], (xs, xss) => xs.Map(y => xss.Map(ys => [y :: ys]).Flatten()).Flatten())
    }

    CardToNumbers(c : Card) : list[int]
    {
        def (_, r) = c;
        match (r)
        {
            | R2 => [2]
            | R3 => [3]
            | R4 => [4]
            | R5 => [5]
            | R6 => [6]
            | R7 => [7]
            | R8 => [8]
            | R9 => [9]
            | R10 => [10]
            | RJ => [11]
            | RQ => [12]
            | RK => [13]
            | RA => [1, 14]
        }
    }

    AreSequential(list : list[int]) : bool
    {
        match (list.Sort(Comparer.Default.Compare))
        {
            | []        => true
            | [_]       => true
            | x0::x1::_ => if (x0 + 1 == x1) AreSequential(list.Tail) else false
        }
    }

    IsStraight(h : Hand) : bool
    {
        DirectProduct(h.Map(CardToNumbers)).Exists(AreSequential)
    }

    IsFlush(h : Hand) : bool
    {
        def (headNum, _) :: tail = h;
        tail.ForAll((num, _) => num == headNum)
    }

    IsStraightFlush(h : Hand) : bool
    {
        IsStraight(h) && IsFlush(h)
    }

    IsRoyal(h : Hand) : bool
    {
        def list = h.Map(Pair.Second).Sort(Comparer.Default.Compare);
        match (list)
        {
            | [Rank.R10, Rank.RJ, Rank.RQ, Rank.RK, Rank.RA] => true
            | _ => false
        }
    }

    IsRoyalFlush(h : Hand) : bool
    {
        IsRoyal(h) && IsFlush(h)
    }

    RemoveOne[T](list : list[T]) : list[list[T]]
    {
        def removeOne(former, acc, list)
        {
            match (list)
            {
                | [] => acc.Rev()
                | (head : T) :: tail =>
                {
                    def former' = head :: former;
                    def acc' = former.RevAppend(tail) :: acc;
                    removeOne(former', acc', tail)
                }
            }
        }
        removeOne([], [], list)
    }

    ForAllExceptOne[T](list : list[T], pred : list[T] -> bool) : bool
    {
        RemoveOne(list).Exists(pred)
    }

    IsFourFlush(h : Hand) : bool
    {
        ForAllExceptOne(h, IsFlush)
    }

    IsFourStraight(h : Hand) : bool
    {
        ForAllExceptOne(h, IsStraight)
    }

    IsFourStraightFlush(h : Hand) : bool
    {
        ForAllExceptOne(h, IsStraightFlush)
    }

    Show (h : Hand): string
    {
        match (h)
        {
            | _ when IsRoyalFlush(h)        => "RF"
            | _ when IsStraightFlush(h)     => "SF"
            | _ when IsFlush(h)             => "FL"
            | _ when IsStraight(h)          => "ST"
            | _ when IsFourStraightFlush(h) => "4SF"
            | _ when IsFourFlush(h)         => "4F"
            | _ when IsFourStraight(h)      => "4S"
            | _                             => "-"
        }
    }
    
    Parse(s : string) : option[Hand]
    {
        def parseSuit(cs)
        {
            match (cs)
            {
                | 's' :: cs' => Some (Suit.Spade,   cs')
                | 'h' :: cs' => Some (Suit.Heart,   cs')
                | 'd' :: cs' => Some (Suit.Diamond, cs')
                | 'c' :: cs' => Some (Suit.Club,    cs')
                | _ => None ()
            }
        }
        def parseRank(cs)
        {
            match (cs)
            {
                | '2' :: cs' => Some (Rank.R2, cs')
                | '3' :: cs' => Some (Rank.R3, cs')
                | '4' :: cs' => Some (Rank.R4, cs')
                | '5' :: cs' => Some (Rank.R5, cs')
                | '6' :: cs' => Some (Rank.R6, cs')
                | '7' :: cs' => Some (Rank.R7, cs')
                | '8' :: cs' => Some (Rank.R8, cs')
                | '9' :: cs' => Some (Rank.R9, cs')
                | '1' :: '0' :: cs' => Some (Rank.R10, cs')
                | 'J' :: cs' => Some (Rank.RJ, cs')
                | 'Q' :: cs' => Some (Rank.RQ, cs')
                | 'K' :: cs' => Some (Rank.RK, cs')
                | 'A' :: cs' => Some (Rank.RA, cs')
                | _ => None ()
            }
        }
        def parseCards(acc, cs)
        {
            match (cs)
            {
                | [] => acc.Rev()
                | _  => match (parseRank(cs))
                {
                    | Some ((r, cs')) => match (parseSuit(cs'))
                    {
                        | Some ((s, cs'')) => parseCards ((s, r) :: acc, cs'')
                        | _ => []
                    }
                    | _ => []
                }
            }
        }
        def cards = parseCards([], s.NToList());
        match (cards)
        {
            | [_, _, _, _, _] => Some (cards)
            | _ => None ()
        }
    }

    Test(s : string) : string
    {
        match (Parse(s))
        {
            | Some (h) => Show (h)
            | _        => ""
        }
    }
}
```