2010-02-16 14 views
18

F # umożliwia stosowanie sprawdzonych arytmetyki otwierając moduł Checked, który nowo definiuje standardowe operatorom sprawdza operatory, npF # sprawdzone Arytmetyka Zakres

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

spowoduje wyjątek arytmetyczną przelewowy.

Ale co, jeśli chcę użyć sprawdzonych arytmetyki w niewielkim zakresie, jak C# pozwala z kluczowych checked:

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

Jak mogę kontrolować zakres operatorów redefinicji otwierając moduł Checked lub je pomniejszyć tak możliwy?

+0

Przeciwnie, jest to możliwe, aby wywołać „zaznaczone” na wszystkich instrukcji w projekcie C#? –

+2

@Heath Hunnicutt - Konwersację można wykonać za pomocą opcji kompilatora, albo w IDE lub w linii poleceń. –

Odpowiedz

18

Zawsze można zdefiniować oddzielny operator lub użyj shadowing lub wykorzystanie parens stworzyć wewnętrzną zakresu czasowego cienia:

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 

Wreszcie, patrz również opcję --checked+ kompilatora:

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

+2

miło, dzięki =) – ControlFlow

16

Oto skomplikowana (ale może interesująca) alternatywa. Jeśli piszesz coś poważnego, prawdopodobnie powinieneś użyć jednej z sugestii Briana, ale z czystej ciekawości zastanawiałem się, czy można napisać wyrażenie F #, żeby to zrobić. Można zadeklarować typ reprezentujący int które powinny być używane wyłącznie z kontrolowanych operacji:

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 

Następnie można zdefiniować konstruktora wyrażeń obliczeń (to naprawdę nie jest monada w ogóle, ponieważ typy operacji są całkowicie non-standard):

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

po wywołaniu „wiążą” będzie automatycznie owinąć daną wartość całkowitą do liczby całkowitej, które powinny być używane z checked operacji, więc reszta kodu będzie wykorzystują sprawdzone + i * operatorzy zadeklarowane jako członkowie. Kończy się coś takiego:

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

Zgłasza wyjątek, ponieważ przepełnia się na oznaczonej linii. Jeśli zmienisz numer, zwróci on wartość int (ponieważ element Return odwija ​​wartość liczbową od typu Checked). To trochę szalona technika :-), ale pomyślałem, że może być interesująca!

(Uwaga checked jest kluczowe zarezerwowana do wykorzystania w przyszłości, więc może wolisz wybrać inną nazwę)

+0

+1 za podejście do bezpiecznych typów – Dario

+0

To jest niesamowite. – kvb

+5

Nie jest to jednak wspaniałe. Możesz napisać 'checked {return Int32.MaxValue + 1}', a to będzie w rzeczywistości niezaznaczone, ponieważ aby zrobić number _checked_, musisz najpierw przekazać go do 'let!' ... –