Это выражение имеет тип int, но здесь используется с типом unit - PullRequest
3 голосов
/ 07 января 2009

Я пытаюсь получить точный эквивалент (не функциональный) этого кода vb.net в 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

Я написал это:

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" при res я получил ошибку:
Msgstr "Это выражение имеет тип int, но здесь используется с типом unit". Я не могу понять, почему это дает мне эту ошибку.
Редактировать: я также получил предупреждение:
"Это выражение должно иметь тип 'unit', но имеет тип 'int'."
при "if (ex% 2 = 1) then"

Ответы [ 5 ]

8 голосов
/ 07 января 2009

В 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

Намного лучше! Есть еще немного места, чтобы украсить эту функцию, но вы поняли:)

3 голосов
/ 07 января 2009

Проблема состоит в том, что если if разрешается в значение, а не в единицу, вам нужна как часть then, так и часть else, каждая из которых разрешается в один и тот же тип.

Например:

let a = if true then 1;;

Сгенерирует ту же ошибку - выражение имеет тип int, но используется с типом unit.

Тем не менее:

let a = if true then 1 else 0;;

Будет оцениваться как int без ошибки.

1 голос
/ 07 января 2009

Это как можно ближе, так как другие уже говорили, что вы не можете выпрыгнуть из середины функционала, и есть одно место, где вы не обновляете переменную (в конце концов) .

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

Я мог бы быть красивее, если бы он был написан более функционально.

0 голосов
/ 07 января 2009

Спасибо за ответы. Это текущая нефункциональная версия.

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

Теперь, когда у меня есть рабочая версия, я постараюсь сделать ее более функциональной, но это выходит за рамки этого вопроса. РЕДАКТИРОВАТЬ: я получил лучшие результаты, которые я ожидал, поэтому я опубликую рекурсивную версию, оптимизированную по скорости (немного быстрее, чем итеративная версия и примерно на 10% быстрее, чем итерационная версия C # (!!!) на моем компьютере):

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 голосов
/ 07 января 2009

Это означает, что после 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

Я просто выношу вычисления как отдельную функцию, а в конце проверяю аргументы

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...