F # Проверяемая область арифметики
F # позволяет использовать проверенную арифметику, открывая модуль Checked
, который переопределяет стандартные операторы для проверки операторов, например:
open Checked
let x = 1 + System.Int32.MaxValue // overflow
приведет к исключению арифметического переполнения.
Но что, если я хочу использовать проверенную арифметику в небольшом объеме, например, С# с ключевым словом Checked
:
int x = 1 + int.MaxValue; // ok
int y = checked { 1 + int.MaxValue }; // overflow
Как я могу контролировать область переопределения операторов, открыв модуль Checked
или уменьшив его насколько возможно?
Ответы
Ответ 1
Вы всегда можете определить отдельный оператор или использовать затенение или использовать 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
Наконец, см. также параметр компилятора --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
Затем вы можете определить построитель выражений вычислений (это совсем не монада, потому что типы операций полностью нестандартные):
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
- это ключевое слово, зарезервированное для использования в будущем, поэтому вы можете выбрать другое имя)