Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# multi-condition if/else versus matching

I'm new to F# and have been implementing simple algorithms to learn the language constructs. I implemented the bisection method using if/else and then wanted to learn how to do it using matching.

if fc = 0.0                   then printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
else if ((b - a) * 0.5) < eps then printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
else if new_count = n         then printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
else if fc * fa < 0.0         then bisect a c new_count
else if fc * fb < 0.0         then bisect c b new_count 

I found that using match a, b, fa, fb, fc would cause type errors, where if I took just a single parameter I could essentially ignore the parameter and check my conditions. What is the idiomatic F#/Functional way to use matching for this? Or should I just stick to if/else?

 match a with 
    | a when fc = 0.0              ->  printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
    | a when ((b - a) * 0.5) < eps -> printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
    | a when new_count = n         -> printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
    | a when fc * fa < 0.0         -> bisect a c new_count
    | a when fc * fb < 0.0         -> bisect c b new_count 
like image 683
nathan Avatar asked Dec 11 '22 21:12

nathan


1 Answers

Your conditions all deal with disparate things, unrelated to each other, so the string of ifs is just fine. The only thing I'd recommend is using elif instead of else if.

match should be understood along the lines of "given this thing that can be of different flavors, here's how to handle those flavors". One particular strength of match is that the compiler will figure out, and tell you, if you missed any of the "flavors". In particular, the code you gave in your question should produce a compiler warning, complaining that "Incomplete pattern matches on this expression". Think about it: what would be the result of that expression when none of the cases match?

With ifs, this will also be the case. For example, this doesn't compile:

let x = if a < 5 then 7

Why? Because the compiler knows what the result should be when a < 5 (i.e. it should be 7), but what should it be otherwise? The compiler can't decide for you, so it will generate an error.
This, on the other hand, would compile:

let x = if a < 5 then 7 else 8

But in your particular case, the compiler lets you get away with this, because all your branches return a unit (why? because printf returns unit, and all others are recursive). In other words, the following will compile:

let x = if a < 5 then ()

And the following:

let x = if a < 5 then printf "boo!"

The compiler lets you get away with this, because unit is special: it can only ever have one value (namely, ()), so the compiler can decide for you what the result of the expression would be when the condition isn't true.

One practical upshot of this would be that, if you didn't think about your conditions very carefuly, it could conceivably happen so that none of your conditions are true, and so the whole thing will return unit and not print anything. I can't say if that could happen in your particular case, because I don't see the whole function definition.

like image 132
Fyodor Soikin Avatar answered Dec 16 '22 23:12

Fyodor Soikin