27

許容可能な最小/最大境界を表すレコードタイプを作成したいとします。

type Bounds = { Min: float; Max: float }

その最小値<最大値を強制する方法はありますか?validateBounds関数を作成するのは簡単ですが、これを行うためのより良い方法があるかどうか疑問に思っていました。

編集:この特定の例では、おそらく2つのプロパティを公開して引数を並べ替えることができることに気付いたので、実行しようとしていたとしましょう。

type Person = { Name: string }

名前には少なくとも1文字が必要です。

4

3 に答える 3

18

保護レベルに基づく別のソリューションを次に示します。

module MyModule =
    type Bounds = private { _min: float; _max: float } with
        // define accessors, a bit overhead
        member public this.Min = this._min
        member public this.Max = this._max
        static member public Make(min, max) =
            if min > max then raise (ArgumentException("bad values"))
            {_min=min; _max=max}

    // The following line compiles fine,
    // e.g. within your module you can do "unsafe" initialization
    let myBadBounds = {_min=10.0; _max=5.0}

open MyModule
let b1 = Bounds.Make(10.0, 20.0) // compiles fine
let b1Min = b1.Min
let b2 = Bounds.Make(10.0, 5.0) // throws an exception
// The following line does not compile: the union cases of the type 'Bounds'
// are not accessible from this code location
let b3 = {_min=10.0; _max=20.0}
// The following line takes the "bad" value from the module
let b4 = MyModule.myBadBounds
于 2012-12-18T02:59:26.657 に答える
7

あなたの最善の策は静的メンバーだと思います:

type Bounds = { Min: float; Max: float }
    with
        static member Create(min: float, max:float) =
            if min >= max then
                invalidArg "min" "min must be less than max"

            {Min=min; Max=max}

そしてそれを次のように使用します

> Bounds.Create(3.1, 2.1);;
System.ArgumentException: min must be less than max
Parameter name: min
   at FSI_0003.Bounds.Create(Double min, Double max) in C:\Users\Stephen\Documents\Visual Studio 2010\Projects\FsOverflow\FsOverflow\Script2.fsx:line 5
   at <StartupCode$FSI_0005>.$FSI_0005.main@()
Stopped due to error
> Bounds.Create(1.1, 2.1);;
val it : Bounds = {Min = 1.1;
                   Max = 2.1;}

ただし、ご指摘のとおり、このアプローチの大きな欠点は、「無効な」レコードの作成を直接妨げるものがないことです。これが大きな懸念事項である場合は、不変条件を保証するためにクラス型を使用することを検討してください。

type Bounds(min:float, max:float) = 
    do
        if min >= max then
            invalidArg "min" "min must be less than max"

    with
        member __.Min = min
        member __.Max = max

レコードで得られるものと同様の利便性のためのアクティブなパターンと一緒に (特にパターン マッチングに関して):

let (|Bounds|) (x:Bounds) =
    (x.Min, x.Max)

すべて一緒に:

> let bounds = Bounds(2.3, 1.3);;
System.ArgumentException: min must be less than max
Parameter name: min
   at FSI_0002.Bounds..ctor(Double min, Double max) in C:\Users\Stephen\Documents\Visual Studio 2010\Projects\FsOverflow\FsOverflow\Script2.fsx:line 4
   at <StartupCode$FSI_0003>.$FSI_0003.main@()
Stopped due to error
> let bounds = Bounds(1.3, 2.3);;

val bounds : Bounds

> let isMatch = match bounds with Bounds(1.3, 2.3) -> "yes!" | _ -> "no";;

val isMatch : string = "yes!"

> let isMatch = match bounds with Bounds(0.3, 2.3) -> "yes!" | _ -> "no";;

val isMatch : string = "no"
于 2012-12-18T02:42:03.307 に答える
0

文字列の例の危険な解決策-DUを使用する

type cleverstring = |S of char * string

これにより、文字列に少なくとも1つの文字が含まれるようになります。cleverstring次に、レコードの代わりに使用できますがstring、文字列のように見えるようにラッパー関数を記述したい場合があります。

于 2012-12-18T02:50:44.543 に答える