【问题标题】:This expression has type int but is here used with type unit此表达式具有 int 类型,但在这里与类型 unit 一起使用
【发布时间】:2009-01-07 13:27:21
【问题描述】:

我正在尝试在 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

但是在 res 的“if ex = 0 then res”行中我收到了一个错误:
“此表达式具有 int 类型,但在这里与类型 unit 一起使用”。 我不明白为什么它会给我这个错误。
编辑:我实际上也收到了警告:
“这个表达式应该有 'unit' 类型,但有 'int' 类型。”
在“如果(例如 % 2 = 1)那么”

【问题讨论】:

    标签: syntax f#


    【解决方案1】:

    在 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 &gt;&gt;&gt; 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
    

    好多了!还有一些空间可以美化这个功能,但你明白了:)

    【讨论】:

      【解决方案2】:

      问题在于 if 语句要解析为值而不是单位,您需要“then”部分和“else”部分,两者都解析为相同的类型。

      例如:

      let a = if true then 1;;
      

      将产生相同的错误 - 表达式的类型为 int 但与类型单元一起使用。

      但是:

      let a = if true then 1 else 0;;
      

      将评估为 int 而不会出错。

      【讨论】:

        【解决方案3】:

        这几乎是你所能得到的,因为其他人已经说过你不能跳出函数的中间并且有一个地方你没有更新变量(在 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
        

        如果写得更实用,我会更漂亮。

        【讨论】:

          【解决方案4】:

          这意味着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
          

          我只是将计算作为单独的函数取出,最后检查参数

          【讨论】:

            【解决方案5】:

            感谢您的回答。这是当前的非功能版本。

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-06-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多