我试图在F#中获得此vb.net代码的完全等效(不起作用):
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
我写了这个:
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
但在“如果ex = 0然后res”行中我得到一个错误:
“此表达式具有int类型,但此处与类型单位一起使用”。
我无法理解为什么它会给我这个错误
编辑:我实际上也收到了警告:
“这个表达式应该有'unit'类型,但是类型为'int'。”
在“if(ex%2 = 1)然后”
答案 0 :(得分:8)
在F#中,函数的返回值是函数中计算的最后一个表达式。所以,让我们关注以下内容:
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 *)
此外,if语句具有返回值,这也恰好是if语句中执行的最后一个值。如果if语句不是函数的返回值,则它应该具有返回类型unit
。请注意,变量赋值的返回类型为unit
。
我们需要重写您的代码以适应您的早期回报,因此我们可以这样做:
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
我们仍然有一个错误,但我认为F#在错误的地方报告错误。表达式exp >>> 1
返回一个int,它不分配任何变量,因此它不等同于原始的C#代码。我认为您打算使用ex
变量。我们可以按如下方式修改您的代码:
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
现在你的功能已修复,但真的很难看。让我们把它转换成更惯用的F#。您可以使用模式匹配替换if语句,并使用递归替换while循环:
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
好多了!还有一些空间来美化这个功能,但你明白了这一点:)
答案 1 :(得分:3)
问题在于if语句要解析为值而不是单位,你需要“then”部分和“else”部分,两者都解析为相同的类型。
例如:
let a = if true then 1;;
将生成相同的错误 - 表达式具有类型int但与类型单位一起使用。
然而:
let a = if true then 1 else 0;;
将在没有错误的情况下评估为int。
答案 2 :(得分:1)
这是你可以得到的尽可能接近,因为其他人已经说过你不能跳出功能的中间,而且有一个地方你没有更新变量(在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
如果功能更强,我会更漂亮。
答案 3 :(得分:0)
这意味着在then
之后应该有一些表达式,但是你有整数值。你不能从功能的中间跳出来。
修改强>
“如果”由于
而无效ex >>> 1
should be
ex <- ex >>> 1
以下代码有效:
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
我只是将计算作为单独的函数取出,最后检查参数
答案 4 :(得分:0)
感谢您的回答。这是当前的非功能版本。
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
既然我有一个正在运行的版本,我将尝试使其更具功能性,但这超出了这个问题的范围。 编辑:我得到了更好的结果,我预计所以我会发布针对速度优化的递归版本(比迭代版本略快,比我计算机中的C#迭代版本(!!!)快10%):
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))