関数は、無効なデータセットをどのように扱うべきですか?関数は、無効なデータセットをどのように扱うべきですか?
私はいつも例外をOOP内のオブジェクトと考えていました。 FP内で、無効なデータをどのように扱うべきですか?
私は鉄道指向プログラミングを調べました。しかし、私はこの技術は、システムの境界インターフェースとそのコアではないと考えています。
次の関数は(順番に)演劇のリストを受け取り、拠点最終状態を指示演劇の順序集合に基づいて拠点を割り当てます。
let assignBases (plays:Play list) =
let initializedBases = { First=None; Second=None; Third=None }
match plays with
| [] -> initializedBases
| _ -> let move bases play =
match (bases, play.Hit) with
| { First= None; Second=None; Third=None }, Single -> { bases with First= Some play.Player }
| { First= None; Second=None; Third=None }, Double -> { bases with Second= Some play.Player }
| { First= None; Second=None; Third=None }, Triple -> { bases with Third= Some play.Player }
| { First= firstPlayer; Second=None; Third=None }, Single -> { First=Some play.Player; Second=firstPlayer; Third=None }
| { First= firstPlayer; Second=None; Third=None }, Double -> { First=None; Second=Some play.Player; Third=firstPlayer }
| { First= firstPlayer; Second=None; Third=None }, Triple -> { First=None; Second=None; Third=Some play.Player }
| { First= None; Second=firstPlayer; Third=None }, Single -> { First=Some play.Player; Second=None; Third=firstPlayer }
| { First= None; Second=firstPlayer; Third=None }, Double -> { First=None; Second=Some play.Player; Third=None }
| { First= None; Second=firstPlayer; Third=None }, Triple -> { First=None; Second=None; Third=Some play.Player }
| { First= None; Second=None; Third=firstPlayer }, Single -> { First=Some play.Player; Second=None; Third=None }
| { First= None; Second=None; Third=firstPlayer }, Double -> { First=None; Second=Some play.Player; Third=None }
| { First= None; Second=None; Third=firstPlayer }, Triple -> { First=None; Second=None; Third=Some play.Player }
| { First= secondPlayer; Second=firstPlayer; Third=None }, Single -> { First=Some play.Player; Second=secondPlayer; Third=firstPlayer }
| { First= secondPlayer; Second=firstPlayer; Third=None }, Double -> { First=None; Second=Some play.Player; Third=secondPlayer }
| { First= secondPlayer; Second=firstPlayer; Third=None }, Triple -> { First=None; Second=None; Third=Some play.Player }
| { First= None; Second=secondPlayer; Third=firstPlayer }, Single -> { First=Some play.Player; Second=None; Third=secondPlayer }
| { First= None; Second=secondPlayer; Third=firstPlayer }, Double -> { First=None; Second=Some play.Player; Third=None }
| { First= None; Second=secondPlayer; Third=firstPlayer }, Triple -> { First=None; Second=None; Third=Some play.Player }
| { First= secondPlayer; Second=None; Third=firstPlayer }, Single -> { First=Some play.Player; Second=secondPlayer; Third=None }
| { First= secondPlayer; Second=None; Third=firstPlayer }, Double -> { First=None; Second=Some play.Player; Third=secondPlayer }
| { First= secondPlayer; Second=None; Third=firstPlayer }, Triple -> { First=None; Second=None; Third=Some play.Player }
| _ -> initializedBases // Haven't identified any other cases...
は考察:
プロパティベースのテストを作成すると、私は関数内で異常を発見しました:
同じプレーヤーを複数の拠点に割り当てているとどうなりますか?
私のプロパティテストでは、このケースのシナリオが特定されました。
質問:
機能に供給された無効なデータを管理する現在の慣行とは何ですか?
実際のシステムでは、ドメインモデル内でこの機能を使用する前に、無効なデータが検証時に失敗するはずです。
しかし、無効なデータが厳しい規則を持つ関数にどのように届くかどうか?
例外をスローしますか?
例外をスローせずに例外を管理する追加のロジックが必要なのでしょうか?
マイ性試験は以下の通りです:
module Properties
open Model
open FsCheck
open FsCheck.Xunit
[<Property(QuietOnSuccess = true)>]
let ``Any hit results in a base beeing filled``() =
let values = Arb.generate<Play list> |> Gen.suchThat (fun plays -> plays.Length > 0)
|> Arb.fromGen
Prop.forAll values <| fun plays ->
// Test
let actual = plays |> assignBases
// Verify
actual <> { First=None; Second=None; Third=None }
障害がこのでした:関数内で具体的に
、私のパターンマッチロジック:失敗の
Test Name: Properties.Any hit results in a base beeing filled
Test FullName: Properties.Any hit results in a base beeing filled
Result Message:
FsCheck.Xunit.PropertyFailedException :
Falsifiable, after 37 tests (0 shrinks) (StdGen (543307172,296154334)):
Original:
<null>
[{Player = Brian;
Hit = Double;}; {Player = Scott;
Hit = Triple;}; {Player = Cherice;
Hit = Double;}; {Player = Brian;
Hit = Single;};
{Player = Cherice;
Hit = Double;}; {Player = Brian;
Hit = Double;}; {Player = Cherice;
Hit = Triple;}; {Player = Brian;
Hit = Single;};
{Player = Cherice;
Hit = Double;}; {Player = Brian;
Hit = Single;}; {Player = Cherice;
Hit = Triple;}; {Player = Brian;
Hit = Single;};
{Player = Brian;
Hit = Triple;}; {Player = Cherice;
Hit = Triple;}; {Player = Brian;
Hit = Double;}; {Player = Brian;
Hit = Triple;};
{Player = Cherice;
Hit = Triple;}; {Player = Cherice;
Hit = Single;}; {Player = Scott;
Hit = Single;}; {Player = Scott;
Hit = Single;};
{Player = Scott;
Hit = Double;}]
概要テスト中のプレイヤーは、複数の基地に割り当てられた同じプレイヤーを考慮していませんでした。
全体のコードはここにある:
module Model
(*Types*)
type Position =
| First
| Second
| Third
type Player =
| Scott
| Brian
| Cherice
type Hit =
| Single
| Double
| Triple
type Play = { Player: Player; Hit: Hit }
type Bases = {
First:Player option
Second:Player option
Third:Player option
}
(*Functions*)
let assignBases (plays:Play list) =
let initializedBases = { First=None; Second=None; Third=None }
match plays with
| [] -> initializedBases
| _ -> let move bases play =
match (bases, play.Hit) with
| { First= None; Second=None; Third=None }, Single -> { bases with First= Some play.Player }
| { First= None; Second=None; Third=None }, Double -> { bases with Second= Some play.Player }
| { First= None; Second=None; Third=None }, Triple -> { bases with Third= Some play.Player }
| { First= firstPlayer; Second=None; Third=None }, Single -> { First=Some play.Player; Second=firstPlayer; Third=None }
| { First= firstPlayer; Second=None; Third=None }, Double -> { First=None; Second=Some play.Player; Third=firstPlayer }
| { First= firstPlayer; Second=None; Third=None }, Triple -> { First=None; Second=None; Third=Some play.Player }
| { First= None; Second=firstPlayer; Third=None }, Single -> { First=Some play.Player; Second=None; Third=firstPlayer }
| { First= None; Second=firstPlayer; Third=None }, Double -> { First=None; Second=Some play.Player; Third=None }
| { First= None; Second=firstPlayer; Third=None }, Triple -> { First=None; Second=None; Third=Some play.Player }
| { First= None; Second=None; Third=firstPlayer }, Single -> { First=Some play.Player; Second=None; Third=None }
| { First= None; Second=None; Third=firstPlayer }, Double -> { First=None; Second=Some play.Player; Third=None }
| { First= None; Second=None; Third=firstPlayer }, Triple -> { First=None; Second=None; Third=Some play.Player }
| { First= secondPlayer; Second=firstPlayer; Third=None }, Single -> { First=Some play.Player; Second=secondPlayer; Third=firstPlayer }
| { First= secondPlayer; Second=firstPlayer; Third=None }, Double -> { First=None; Second=Some play.Player; Third=secondPlayer }
| { First= secondPlayer; Second=firstPlayer; Third=None }, Triple -> { First=None; Second=None; Third=Some play.Player }
| { First= None; Second=secondPlayer; Third=firstPlayer }, Single -> { First=Some play.Player; Second=None; Third=secondPlayer }
| { First= None; Second=secondPlayer; Third=firstPlayer }, Double -> { First=None; Second=Some play.Player; Third=None }
| { First= None; Second=secondPlayer; Third=firstPlayer }, Triple -> { First=None; Second=None; Third=Some play.Player }
| { First= secondPlayer; Second=None; Third=firstPlayer }, Single -> { First=Some play.Player; Second=secondPlayer; Third=None }
| { First= secondPlayer; Second=None; Third=firstPlayer }, Double -> { First=None; Second=Some play.Player; Third=secondPlayer }
| { First= secondPlayer; Second=None; Third=firstPlayer }, Triple -> { First=None; Second=None; Third=Some play.Player }
| _ -> initializedBases // Haven't identified any other cases...
(initializedBases, plays) ||> List.fold (fun bases play ->
play |> move bases)
UPDATE:掲載の勧告に基づいて
、私は私のモデルにいくつかの更新を行いました。
私はにステータスタイプを追加しました処理中のデータの状態を示します。
type Status =
| Valid of Bases
| Invalid of Play list
は、私はその後、期待される状態が「有効」と予期しないとしてマークされていることをこのような塩基の値が「無効」としてマークされているこの状態を適用:エラー状態を返す対例外をスロー
let assignBases (plays:Play list) =
let initializedBases = { First=None; Second=None; Third=None }
match plays with
| [] -> Valid initializedBases
| _ -> let move bases play =
match (bases, play.Hit) with
| Valid { First= None; Second=None; Third=None }, Single -> Valid { First= Some play.Player; Second=None; Third=None }
| Valid { First= None; Second=None; Third=None }, Double -> Valid { First=None; Second= Some play.Player; Third=None }
| Valid { First= None; Second=None; Third=None }, Triple -> Valid { First=None; Second=None; Third= Some play.Player }
| Valid { First= firstPlayer; Second=None; Third=None }, Single -> Valid { First=Some play.Player; Second=firstPlayer; Third=None }
| Valid { First= firstPlayer; Second=None; Third=None }, Double -> Valid { First=None; Second=Some play.Player; Third=firstPlayer }
| Valid { First= firstPlayer; Second=None; Third=None }, Triple -> Valid { First=None; Second=None; Third=Some play.Player }
| Valid { First= None; Second=firstPlayer; Third=None }, Single -> Valid { First=Some play.Player; Second=None; Third=firstPlayer }
| Valid { First= None; Second=firstPlayer; Third=None }, Double -> Valid { First=None; Second=Some play.Player; Third=None }
| Valid { First= None; Second=firstPlayer; Third=None }, Triple -> Valid { First=None; Second=None; Third=Some play.Player }
| Valid { First= None; Second=None; Third=firstPlayer }, Single -> Valid { First=Some play.Player; Second=None; Third=None }
| Valid { First= None; Second=None; Third=firstPlayer }, Double -> Valid { First=None; Second=Some play.Player; Third=None }
| Valid { First= None; Second=None; Third=firstPlayer }, Triple -> Valid { First=None; Second=None; Third=Some play.Player }
| Valid { First= secondPlayer; Second=firstPlayer; Third=None }, Single -> Valid { First=Some play.Player; Second=secondPlayer; Third=firstPlayer }
| Valid { First= secondPlayer; Second=firstPlayer; Third=None }, Double -> Valid { First=None; Second=Some play.Player; Third=secondPlayer }
| Valid { First= secondPlayer; Second=firstPlayer; Third=None }, Triple -> Valid { First=None; Second=None; Third=Some play.Player }
| Valid { First= None; Second=secondPlayer; Third=firstPlayer }, Single -> Valid { First=Some play.Player; Second=None; Third=secondPlayer }
| Valid { First= None; Second=secondPlayer; Third=firstPlayer }, Double -> Valid { First=None; Second=Some play.Player; Third=None }
| Valid { First= None; Second=secondPlayer; Third=firstPlayer }, Triple -> Valid { First=None; Second=None; Third=Some play.Player }
| Valid { First= secondPlayer; Second=None; Third=firstPlayer }, Single -> Valid { First=Some play.Player; Second=secondPlayer; Third=None }
| Valid { First= secondPlayer; Second=None; Third=firstPlayer }, Double -> Valid { First=None; Second=Some play.Player; Third=secondPlayer }
| Valid { First= secondPlayer; Second=None; Third=firstPlayer }, Triple -> Valid { First=None; Second=None; Third=Some play.Player }
| _ -> Invalid plays // Haven't identified any other cases...
(Valid initializedBases, plays) ||> List.fold (fun bases play -> play |> move bases)
おそらく、あなたがベースにいる場合、あなたがバットすることはできないというルールが必要です。または、これを2つのキューとして見ることができます.1つは打者のため、もう1つはベースのプレイヤーのためです。または、プレイヤーを変更して、jusよりも多くの状態を持つことができます。ベース、バッティング、ベンチ、フィールディング。 –