【问题标题】:F# Checked Arithmetics ScopeF# 检查算术范围
【发布时间】:2011-01-17 06:58:38
【问题描述】:

F# 允许通过打开 Checked 模块来使用检查算术,该模块将标准运算符重新定义为检查运算符,例如:

open Checked
let x = 1 + System.Int32.MaxValue // overflow

会导致算术溢出异常。

但是如果我想在一些小范围内使用检查算术怎么办,比如 C# 允许使用关键字 checked:

int x = 1 + int.MaxValue;             // ok
int y = checked { 1 + int.MaxValue }; // overflow

如何通过打开Checked模块来控制算子重定义的范围或者尽可能的变小?

【问题讨论】:

  • 相反,是否可以在 C# 项目中的所有语句上调用“checked”?
  • @Heath Hunnicutt - 反过来可以通过编译器选项来完成,无论是在 IDE 中还是在命令行中。

标签: f# module scope operators math


【解决方案1】:

你总是可以定义一个单独的操作符,或者使用阴影,或者使用括号来为临时阴影创建一个内部范围:

let f() =
    // define a separate operator
    let (+.) x y = Checked.(+) x y
    try 
        let x = 1 +. System.Int32.MaxValue
        printfn "ran ok"
    with e ->
        printfn "exception"
    try 
        let x = 1 + System.Int32.MaxValue
        printfn "ran ok"
    with e ->
        printfn "exception"
    // shadow (+)
    let (+) x y = Checked.(+) x y
    try 
        let x = 1 + System.Int32.MaxValue
        printfn "ran ok"
    with e ->
        printfn "exception"
    // shadow it back again
    let (+) x y = Operators.(+) x y
    try 
        let x = 1 + System.Int32.MaxValue
        printfn "ran ok"
    with e ->
        printfn "exception"
    // use parens to create a scope
    (
        // shadow inside
        let (+) x y = Checked.(+) x y
        try 
            let x = 1 + System.Int32.MaxValue
            printfn "ran ok"
        with e ->
            printfn "exception"
    )            
    // shadowing scope expires
    try 
        let x = 1 + System.Int32.MaxValue
        printfn "ran ok"
    with e ->
        printfn "exception"


f()    
// output:
// exception
// ran ok
// exception
// ran ok
// exception
// ran ok

最后,另请参阅--checked+ 编译器选项:

http://msdn.microsoft.com/en-us/library/dd233171(VS.100).aspx

【讨论】:

    【解决方案2】:

    这是一个复杂(但可能很有趣)的替代方案。如果您正在写一些严肃的东西,那么您可能应该使用 Brians 的建议之一,但出于好奇,我想知道是否可以编写 F# 计算表达式来执行此操作。您可以声明一个表示 int 的类型,该类型只能用于检查操作:

    type CheckedInt = Ch of int with
      static member (+) (Ch a, Ch b) = Checked.(+) a b
      static member (*) (Ch a, Ch b) = Checked.(*) a b
      static member (+) (Ch a, b) = Checked.(+) a b
      static member (*) (Ch a, b) = Checked.(*) a b
    

    然后你可以定义一个计算表达式构建器(这根本不是一个真正的 monad,因为操作的类型是完全非标准的):

    type CheckedBuilder() = 
      member x.Bind(v, f) = f (Ch v)      
      member x.Return(Ch v) = v
    let checked = new CheckedBuilder()  
    

    当您调用“绑定”时,它会自动将给定的整数值包装成一个整数,该整数应与checked 操作一起使用,因此其余代码将使用声明为成员的检查+* 运算符.你最终会得到这样的结果:

    checked { let! a = 10000 
              let! b = a * 10000 
              let! c = b * 21 
              let! d = c + 47483648 // !
              return d }
    

    这会引发异常,因为它在标记的行上溢出。如果您更改数字,它将返回一个 int 值(因为 Return 成员解开 Checked 类型的数值)。这是一个有点疯狂的技术 :-) 但我认为它可能很有趣!

    (注意checked 是保留供将来使用的关键字,因此您可能更愿意选择其他名称)

    【讨论】:

    • 虽然不是很好。你可以写checked { return Int32.MaxValue + 1 },它实际上是未选中的,因为要使一个数字选中,你需要先将它传递给let!...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多