2015-12-02 3 views
8

違法行為を実行不能にするにはどうすればよいですか?違法行為が実行不能であることをどのようにして保証することができますか?

概要:

のF#を学ぶために、私の旅を始めて以来、私はタイプ駆動設計とプロパティベースのテストについて学んでいます。その結果、私は違法な州を表現しえないという考えに恋しました。

しかし、私が本当にやりたいことは、違法行為を実行不能にすることです。

私はブラックジャックゲームを書くことでF#を学んでいます。その結果、ディーラーがカードを配布するときに、ディーラーは「初期手」または「ヒット」しか扱えないようにしたいと考えています。他のすべてのカードの配布は違法です。

C#では、Strategy Patternを実装して、DealHandCommandとDealHitCommandを作成します。それから私は決済するカードの数(戦略ごとに)に一定の整数値をハードコードします。これらの戦略に基づいて

DealHandCommand = 2枚のカード

DealHitCommand = 1カード

、私は、ブラックジャックのゲームのセッションを表現するためにステートマシンを実装します。したがって、最初の手(DealHandCommand)を処理した後、将来の取引で「DealHitCommand」のみを実行できる状態遷移を実行します。

具体的には、実行不能な違法な動作を実現するためにハイブリッド機能の言語内でステートマシンを実装することは理にかなっていますか?

答えて

9

です。

  1. オプション各ケースの遷移関数を定義し、各状態に対する場合と識別型ユニオンを定義:それは通常第三工程は任意であることで、3段階のプロセスを以下の実施全

    0123:二つの状態があるようなコードの残りの部分は

この場合、ステップ1

それは私に聞こえますまた

type Deal = Hand of Card * Card | Hit of Card 

Gameが何であるかを定義します。2枚のカード

    • 初期このDeal差別組合を示唆している余分なカード

    ヒット:

    type Game = Game of Deal list 
    

    単一ケースの区別されたユニオンの使用に注目してください。 there's a reason for that

    ステップ2

    次にGameに各状態からの遷移関数を定義します。

    Handは何開始新しいゲームであるので、それは、あなたがHandケースにから任意のゲームの状態をを移行することができないことが判明。一方、(しゃれが意図した)あなたが手に入るカードを供給する必要がある場合:

    let init c1 c2 = Game [Hand (c1, c2)] 
    

    以外の場合は、ゲームが進行しているとき、あなただけHandHitを許可しますが、ないはずです、ので、この移行を定義します。

    let hit (Game deals) card = Game (Hit card :: deals) 
    

    あなたが見ることができるように、hit機能は、既存のGameに合格する必要があります。例えば、無効なGame価値を創造からクライアントを防ぐ何

    ステップ3

    [Hand; Hit; Hand; Hit; Hit]?はここ

    type Deal 
    type Game 
    val init : Card -> Card -> Game 
    val hit : Game -> Card -> Game 
    val card : Deal -> Card list 
    val cards : Game -> Card list 
    

    、種類DealGameが宣言されているが、彼らの 'コンストラクタは、' ではありません。

    BlackJack.fsi:

    あなたはsignature fileで上記の状態マシンをカプセル化することができます。つまり、これらの型の値を直接作成することはできません。これは、例えば、コンパイルされません。

    let g = BlackJack.Game [] 
    

    与えられたエラーは次のとおりです。

    エラーFS0039:値、コンストラクタ、名前空間または型 'ゲーム' の

    が定義されていません。

    Game値を作成する唯一の方法は、それを作成する関数を呼び出すことです:

    let g = 
        BlackJack.init 
         { Face = Ace; Suit = Spades } 
         { Face = King; Suit = Diamonds } 
    
    あなたは上記の署名ファイルもGameDeal値のうち、カードを取得するために2つの関数を定義していることに気づいたかもしれ

    let g' = BlackJack.hit g { Face = Two; Suit = Spades } 
    

    これはまた、あなたがゲームを継続することができます。ここでの実装は以下のとおりです。

    let card = function 
        | Hand (c1, c2) -> [c1; c2] 
        | Hit c -> [c] 
    
    let cards (Game deals) = List.collect card deals 
    

    クライアントは、このようにそれらを使用することができます。このアプローチは、ほとんどの構造であることを

    > let cs = g' |> BlackJack.cards;; 
    > 
    
    val cs : Card list = [{Suit = Spades; 
             Face = Two;}; 
             {Suit = Spades; 
             Face = Ace;}; 
             {Suit = Diamonds; 
             Face = King;}] 
    

    は注意してください。可動部分はほとんどありません。

    付録

    これらは、上記で使用したファイルです:

    Cards.fs:

    namespace Ploeh.StackOverflow.Q34042428.Cards 
    
    type Suit = Diamonds | Hearts | Clubs | Spades 
    type Face = 
        | Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten 
        | Jack | Queen | King | Ace 
    
    type Card = { Suit: Suit; Face: Face } 
    

    ブラックジャック。FSI:

    module Ploeh.StackOverflow.Q34042428.Cards.BlackJack 
    
    type Deal 
    type Game 
    val init : Card -> Card -> Game 
    val hit : Game -> Card -> Game 
    val card : Deal -> Card list 
    val cards : Game -> Card list 
    

    BlackJack.fs:

    module Ploeh.StackOverflow.Q34042428.Cards.BlackJack 
    
    open Ploeh.StackOverflow.Q34042428.Cards 
    
    type Deal = Hand of Card * Card | Hit of Card 
    
    type Game = Game of Deal list 
    
    let init c1 c2 = Game [Hand (c1, c2)] 
    
    let hit (Game deals) card = Game (Hit card :: deals) 
    
    let card = function 
        | Hand (c1, c2) -> [c1; c2] 
        | Hit c -> [c] 
    
    let cards (Game deals) = List.collect card deals 
    

    Client.fs:

    module Ploeh.StackOverflow.Q34042428.Cards.Client 
    
    open Ploeh.StackOverflow.Q34042428.Cards 
    
    let g = 
        BlackJack.init 
         { Face = Ace; Suit = Spades } 
         { Face = King; Suit = Diamonds } 
    let g' = BlackJack.hit g { Face = Two; Suit = Spades } 
    
    let cs = g' |> BlackJack.cards 
    
  • +0

    おかげマーク。 fsiファイルで、外部から「ゲーム」を明示的に有効にしたければ、それをどのように表現しますか?それはfsiファイル内の "type game()"だけでしょうか? –

    +0

    @ScottNimrodこれを行うには、通常のモジュール宣言が有効なので、最も簡単な方法は '.fsi'ファイルを削除することです。それ以外の場合は、[ドキュメント](https://msdn.microsoft.com/en-us/library/dd233196.aspx)でその方法を教えてください。 –

    1

    の可能性のうちの1つを判別組合を使用することができます

    type DealCommand = 
        | Hand of Card * Card 
        | Hit of Card 
    

    (あなたがタイプCardを持っていると仮定)

    2

    が1人の以上のカードの権利であるヒット?

    ので、その後わずか2 種類使用する場合:

    • type HandDealt = Dealt of Card * Card
    • type Playing = Playing of Cards
    • を(多分もっと - あなたが望むものに依存します)。

    は、その後の代わりコマンドあなたは、単純な機能があります。

    • dealHand :: Card * Card -> HandDealt
    • start :: HandDealt -> Playing
    • dealAnother :: Playing -> Card -> Playing

    あなたが唯一の特定の行動を追跡することができ、この方法は、それが静的ですチェッククーゼの

    あなたはおそらく、複数のプレイヤーにこれらの型を拡張したいが、私はあなたが私が


    PSに行きますものを手に入れると思う:多分あなたも、(HandDealt/startフェーズをスキップしたい場合は、あなたドン

    • dealHand :: Card * Card -> Playing
    • :。)が、私はブラックジャックについての手掛かりを持っていないことを気にしてください - 「tは/分割/などを賭けのようなもののための中間相を必要とします

    それはそれはF#でステートマシンを実装するのは簡単です、あなた次第

    関連する問題