F# GreaterThanZero 传递 int 或 decimal

F# GreaterThanZero passing int or decimal

我想创建一个函数来检查传递的值是否大于零。
传递的值可以是整数或小数(理想情况下是“数值”)。

我刚开始是这样的:

type number =
| I of int 
| D of decimal 

type Checker () =
    member this.Validate value =
        match value with 
        | I x when x > 0 -> "ok"
        | D x when x > 0m -> "ok"
        | _ -> "error"

let a = 1f
let b = 1m
//let a_IsValid = Checker().Validate(a) // does not compile, expect number (not int)
//let b_IsValid = Checker().Validate(b) // does not compile, expect number (not decimal)

发现不能立即传递“数字”,所以尝试了不同的方法... 我找到了这篇文章 (http://tomasp.net/blog/fsharp-generic-numeric.aspx/),我想 “静态成员约束”对我来说是完美的解决方案。 一个基本示例按预期工作:

let inline divideByTwo value = 
    LanguagePrimitives.DivideByInt value 2

divideByTwo 1f |> ignore
divideByTwo 1m |> ignore

但一个不同的场景让我感到非常惊讶:

type Calculator () =
    let divideByTwo value = 
        LanguagePrimitives.DivideByInt value 2

    member this.DivideByTwo value = 
        LanguagePrimitives.DivideByInt value 2

    member this.ValidateGeneric value =
        match LanguagePrimitives.GenericGreaterThan value 0m with
        | true -> "ok"
        | _ -> "error"

//let half = Calculator().DivideByTwo(1) // DivideByInt does not support int !!

// cannot use both the following, the first one will "force" the type, and the other will not work
let a_half = Calculator().DivideByTwo(1f) // ok if used before the "decimal" version
let b_half = Calculator().DivideByTwo(1m) // ok only if comment the previous one

当我想为传递值使用多种类型时,它似乎不起作用。

不仅如此,我需要的功能(GenericGreaterThan)似乎还有另一个“限制”,下面解释。 文章中的示例使用 DivideByInt,正如其名称所示,它将传递的值除以 一个 int,这是一种定义明确的类型。 LanguagePrimitives.GenericGreaterThan 需要 2 个参数,一个传递值和一个用于比较的固定值。函数的签名仅作为两者的一种通用类型,因此如果您传递一个类型 'T,它期望第二个类型也是 'T。
我只想与 zero 比较而不传递它,但是使用“0”强制我的值 为 int 并使用“0m”强制该值为小数。

有一种简单的方法可以让函数检查“数字”值是否大于“零”? 我应该使用 obj 并将其装箱....还是使用 cast ... 或停止尝试并为我需要的每种类型使用不同的函数?

[更新]
我尝试按照建议使用 LanguagePrimitives.GenericZero,但仍然无法为我的特定场景提供有效的解决方案。
我在这里创建了一个新问题: .

这里有很多东西。

您的第一个示例不起作用,因为您需要将您的数字包装在类型编号中(我假设您意识到了这一点?但不希望它那样工作?)

type Checker () =
    member this.Validate value =
        match value with 
        | I x when x > 0 -> "ok"
        | D x when x > 0m -> "ok"
        | _ -> "error"

let a = I 1
let b = D 1m
let a_IsValid = Checker().Validate(a)
let b_IsValid = Checker().Validate(b)

你的第二个例子是in不支持除以int? 是的 1/2 的值是多少?它不是一个整数,所以这是设计正确的。

第三个问题似乎是这段代码无法编译并且 运行?

type Calculator () =
    member inline _.DivideByTwo value = 
        LanguagePrimitives.DivideByInt value 2

let b_half = Calculator().DivideByTwo(1m) // ok for me
let a_half = Calculator().DivideByTwo(1f) // ok for me

但这对我有用。

第四个问题似乎是需要使用静态约束来测试是否大于 0?

但是 0(在数学中)在不同的数字系统中是不同的东西,它也是通用的,所以你需要 LanguagePrimitives.GenericZero。把所有这些放在一起我们得到

type Calculator () =
    member inline _.DivideByTwo value = 
        LanguagePrimitives.DivideByInt value 2

    member inline _.ValidateGeneric value =
        match LanguagePrimitives.GenericGreaterThan 
            value 
            LanguagePrimitives.GenericZero with
        | true -> "ok"
        | _ -> "error"

let b_half = Calculator().DivideByTwo(1m)
let a_half = Calculator().DivideByTwo(1f)
let y = Calculator().ValidateGeneric(1m)
let z = Calculator().ValidateGeneric(1f)

关于除以1/2的问题,你可能需要想想你想让它做什么?输入类型真的是由你想要的输出类型定义的吗?十进制?漂浮?等等

一般地与零进行比较实际上非常简单。以下函数适用于任何数字类型:

let inline isPositive x =
    x > LanguagePrimitives.GenericZero

isPositive 1.0 |> printfn "%A"   // true
isPositive 1m |> printfn "%A"    // true

一般除以二也很容易。您只需要定义自己的通用两个,因为它不是 built-in 原语:

let inline divideByTwo x =
    let two =
        LanguagePrimitives.GenericOne
            + LanguagePrimitives.GenericOne
    x / two

divideByTwo 5.0 |> printfn "%A"   // 2.5
divideByTwo 4m |> printfn "%A"    // 2