I'm trying to get the exact equivalent (not functional) of this vb.net code in F#:
Function FastPow(ByVal num As Double, ByVal exp As Integer) As Double
Dim res As Double = 1
If exp < 1 Then
If exp = 0 Then Return res
exp = -exp
num = 1 / num
End If
Do While exp > 1
If exp Mod 2 = 1 Then
res = res * num
num = num * num
exp = exp >> 1
Loop
Return res * num
End Function
I wrote this:
let FastPow num exp =
let mutable ex = exp
let mutable res = 1
let mutable n = num
if ex < 1 then
if ex = 0 then res
ex <- -ex
n <- 1 / n
while ex > 1 do
if (ex % 2 = 1) then
res <- res * n
n <- n * n
exp >>> 1
res * n
but in the line "if ex = 0 then res" at res I got an error:
"This expression has type int but is here used with type unit".
I cannot understand why it gives me that error.
Edit: i actually got a warning as well:
"This expression should have type 'unit', but has type 'int'."
at "if (ex % 2 = 1) then"
-
It means that after
then
there should be some expression, but you have integer value. You cannot jump out from the middle of the function.Edit
"If" didn't work because of
ex >>> 1 should be ex <- ex >>> 1
Here's code that works:
let FastPow num exp = let calcExp num exp = let mutable res = 1.0 let mutable n = num let mutable ex = exp while ex > 1 do if ((ex % 2) = 1) then res <- res * n n <- n * n ex <- ex >>> 1 res * n match exp with | ex when ex = 0 -> 1.0 | ex when ex < 0 -> calcExp (1.0/num) -exp | _ -> calcExp num exp
I just take out calculation as separate function, and at the end there is checking for arguments
-
The problem is for an if statment to resolve to a value rather than unit, you need both the "then" part and the "else" part, both of which resolve to the same type.
For example:
let a = if true then 1;;
Will generate the same error - expression has type int but used with type unit.
However:
let a = if true then 1 else 0;;
Will evaluate to int without an error.
-
This is about as close as you can get, as others have already said you can't jump out of the middle of a functional and there's one place were you don't update a variable (at the bottom of the while).
let FastPow num exp = let mutable exp = exp let mutable res = 1 let mutable n = num match exp with | O -> n <- num | _ when exp < 1 -> exp <- -exp n <- 1 / n | _ -> while exp > 1 do if (exp % 2 = 1) then res <- res * n n <- n * n exp <- exp >>> 1 res * n
I could be more beautiful if it was written more functionally.
-
In F#, a function's return value is the last expression evaluated in the function. So, lets focus on the following:
if ex < 1 then if ex = 0 then res (* <--- this is not an early return *) ex <- -ex (* <--- F# evaluates this code after the *) n <- 1 / n (* if statement *)
Additionally, if statements have return values, which also happens to be the last value executed in the if statement. If an if statement isn't the return value of a function, it should have the return type
unit
. Notice that variable assignment has a return type ofunit
.We need to rewrite your code to accomodate your early return, so we can do this:
let FastPow2 num exp = if exp = 0 then 1 else let mutable ex = exp let mutable res = 1 let mutable n = num if ex < 1 then ex <- -ex n <- 1 / n while ex > 1 do if (ex % 2 = 1) then (* still have a bug here *) res <- res * n n <- n * n exp >>> 1 (* <--- this is not a variable assignment *) res * n
We still have a bug, although I think F# is reporting the error in the wrong place. The expression
exp >>> 1
returns an int, it does not assign any variables, so its not equivalent to your original C# code. I think you meant to use theex
variable instead. We can fix your code as follows:let FastPow2 num exp = if exp = 0 then 1 else let mutable ex = exp let mutable res = 1 let mutable n = num if ex < 1 then ex <- -ex n <- 1 / n while ex > 1 do if (ex % 2 = 1) then res <- res * n n <- n * n ex <- ex >>> 1 res * n
Now your function is fixed, but its really really ugly. Lets convert it to more idiomatic F#. You can replace the if statement with pattern matching, and replace the while loop with recursion:
let FastPow2 num exp = match exp with | 0 -> 1 | _ -> let rec loop ex res n = if ex > 1 then let newRes = if ex % 2 = 1 then res * n else res loop (ex >>> 1) newRes (n * n) else res * n let ex, n = if exp < 1 then (-exp, 1 / num) else (exp, num) loop ex 1 n
Much better! Theres still some more room to beautify this function, but you get the idea :)
-
Thanks for the answers. This is the current non-functional version.
let FastPow num exp = let mutable ex = exp let mutable res = 1.0 let mutable n = num if ex = 0 then 1.0 else if ex < 1 then ex <- -ex n <- 1.0 / n while ex > 1 do if (ex % 2 = 1) then res <- res * n n <- n * n ex <- ex >>> 1 res * n
Now that I have a working version I will try to make it more functional but that's outside the scope of this question. EDIT: I got better results that I expected so I will post the recursive version optimized for speed (slightly faster than the iterative version and about 10% faster than the C# iterative version (!!!) in my computer):
let rec loop res num exp = if exp = 0 then res elif (exp % 2) = 1 then loop (res * num) (num * num) (exp / 2) else loop res (num * num) (exp / 2) let FP num exp = let n = if exp < 0 then 1.0 / num else num loop 1.0 n (Math.Abs(exp))
0 comments:
Post a Comment