F#检查算术范围

con*_*low 19 math f# scope module operators

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

open Checked
let x = 1 + System.Int32.MaxValue // overflow
Run Code Online (Sandbox Code Playgroud)

将导致算术溢出异常.

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

int x = 1 + int.MaxValue;             // ok
int y = checked { 1 + int.MaxValue }; // overflow
Run Code Online (Sandbox Code Playgroud)

如何通过打开Checked模块或尽可能缩小模块来控制操作员重新定义的范围?

Bri*_*ian 19

您始终可以定义单独的运算符,或使用阴影,或使用parens为临时阴影创建内部范围:

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
Run Code Online (Sandbox Code Playgroud)

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

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


Tom*_*cek 17

这是一个复杂的(但也许是有趣的)替代方案.如果你正在写一些严肃的东西,你应该使用其中一个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
Run Code Online (Sandbox Code Playgroud)

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

type CheckedBuilder() = 
  member x.Bind(v, f) = f (Ch v)      
  member x.Return(Ch v) = v
let checked = new CheckedBuilder()  
Run Code Online (Sandbox Code Playgroud)

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

checked { let! a = 10000 
          let! b = a * 10000 
          let! c = b * 21 
          let! d = c + 47483648 // !
          return d }
Run Code Online (Sandbox Code Playgroud)

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

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

  • 虽然不是那么好.您可以编写`checked {return Int32.MaxValue + 1}`并且它实际上是未选中的,因为要创建一个数字_checked_,您需要先将它传递给`let!`... (5认同)