F # Проверено Область Арифметики - PullRequest
19 голосов
/ 16 февраля 2010

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 или уменьшив его по возможности?

Ответы [ 2 ]

18 голосов
/ 16 февраля 2010

Вы всегда можете определить отдельный оператор, либо использовать теневое копирование, либо использовать парены, чтобы создать внутреннюю область для временного теневого копирования:

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

16 голосов
/ 16 февраля 2010

Вот сложная (но, возможно, интересная) альтернатива. Если вы пишете что-то серьезное, то вам, вероятно, следует использовать одно из предложений 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

Затем вы можете определить компоновщик выражений вычислений (на самом деле это не монада, потому что типы операций совершенно нестандартны):

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

Когда вы вызываете 'bind', он автоматически оборачивает данное целочисленное значение в целое число, которое должно использоваться с checked операциями, поэтому в остальной части кода будут использоваться проверенные операторы + и *, объявленные как члены , В итоге вы получите что-то вроде этого:

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

Это вызывает исключение, потому что оно переполняется в отмеченной строке. Если вы измените число, оно вернет значение int (поскольку элемент Return разворачивает числовое значение из типа Checked). Это немного сумасшедшая техника :-) но я подумал, что это может быть интересно!

(Примечание checked - ключевое слово, зарезервированное для будущего использования, поэтому вы можете выбрать другое имя)

...