代码之家  ›  专栏  ›  技术社区  ›  Oleksii G.

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

  •  3
  • Oleksii G.  · 技术社区  · 17 年前

    我试图在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那么res”行中,我得到了一个错误:
    “此表达式具有int类型,但此处与type unit一起使用”。 我不明白为什么它会给我这个错误。

    此表达式应具有类型“unit”,但具有类型“int”

    5 回复  |  直到 17 年前
        1
  •  8
  •   Juliet    17 年前

    在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 单元 .

    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
    

    exp >>> 1 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
  •  3
  •   Chris Ballard    17 年前

    问题在于,如果要将if语句解析为值而不是单位,则需要“then”部分和“else”部分,这两个部分解析为相同的类型。

    例如:

    let a = if true then 1;;
    

    将生成相同的错误-表达式的类型为int,但与类型unit一起使用。

    然而:

    let a = if true then 1 else 0;;
    

        3
  •  1
  •   Robert    17 年前

    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
  •  0
  •   zendar    17 年前

    这意味着 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
  •  0
  •   Oleksii G.    17 年前

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

    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
    

    现在我有了一个工作版本,我将尝试使它更实用,但这超出了这个问题的范围。

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