此表达式具有int类型,但此处与类型unit一起使用

时间:2009-01-07 13:27:22

标签: syntax f#

我试图在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)然后”

5 个答案:

答案 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))