Questa espressione ha tipo int ma viene qui utilizzata con unità di tipo

StackOverflow https://stackoverflow.com/questions/420294

  •  05-07-2019
  •  | 
  •  

Domanda

Sto cercando di ottenere l'esatto equivalente (non funzionale) di questo codice vb.net in 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

Ho scritto questo:

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

ma nella riga "se ex = 0 allora res " a res ho ricevuto un errore:
" Questa espressione ha tipo int ma è qui usata con unità di tipo " ;. Non riesco a capire perché mi dia questo errore.
Modifica: ho anche ricevuto un avviso:
" Questa espressione dovrebbe avere il tipo 'unit', ma ha il tipo 'int'. "
su " if (ex% 2 = 1) quindi "

È stato utile?

Soluzione

In F #, il valore restituito di una funzione è l'ultima espressione valutata nella funzione. Quindi, concentriamoci su quanto segue:

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

Inoltre, se le istruzioni hanno valori di ritorno, che è anche l'ultimo valore eseguito nell'istruzione if. Se un'istruzione if non è il valore restituito di una funzione, dovrebbe avere il tipo restituito unit . Si noti che l'assegnazione delle variabili ha un tipo restituito di unità .

Dobbiamo riscrivere il codice per soddisfare il tuo ritorno anticipato, quindi possiamo farlo:

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

Abbiamo ancora un bug, anche se penso che F # stia segnalando l'errore nel posto sbagliato. L'espressione exp > > > 1 restituisce un int, non assegna alcuna variabile, quindi non è equivalente al tuo codice C # originale. Penso che volessi usare invece la variabile ex . Possiamo correggere il tuo codice come segue:

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

Ora la tua funzione è fissa, ma è davvero brutta. Consente di convertirlo in F # più idiomatico. Puoi sostituire l'istruzione if con pattern matching e sostituire il ciclo while con ricorsione:

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

Molto meglio! C'è ancora un po 'di spazio per abbellire questa funzione, ma hai l'idea :)

Altri suggerimenti

Il problema è che un'istruzione if si risolva in un valore piuttosto che in un'unità, è necessario sia il "quot" quindi il " parte e "altro"; parte, entrambi risolti nello stesso tipo.

Ad esempio:

let a = if true then 1;;

Genererà lo stesso errore - espressione ha tipo int ma usata con unità di tipo.

Tuttavia:

let a = if true then 1 else 0;;

Valuterà int senza un errore.

Questo è il più vicino possibile, come altri hanno già detto che non puoi saltare fuori dal mezzo di un funzionale e c'è un posto dove non aggiorni una variabile (in fondo al tempo) .

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

Potrei essere più bello se fosse scritto in modo più funzionale.

Significa che dopo quindi dovrebbe esserci qualche espressione, ma hai un valore intero. Non è possibile saltare fuori dal centro della funzione.

Modifica

" Se " non ha funzionato a causa di

ex >>> 1

should be

ex <- ex >>> 1

Ecco il codice che funziona:

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

Prendo semplicemente il calcolo come funzione separata e alla fine c'è la verifica degli argomenti

Grazie per le risposte. Questa è l'attuale versione non funzionale.

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

Ora che ho una versione funzionante, proverò a renderla più funzionale ma non rientra nell'ambito di questa domanda. EDIT: ho ottenuto risultati migliori di quanto mi aspettassi, quindi pubblicherò la versione ricorsiva ottimizzata per la velocità (leggermente più veloce della versione iterativa e circa il 10% più veloce rispetto alla versione iterativa C # (!!!) sul mio computer):

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))
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top