Questa espressione ha tipo int ma viene qui utilizzata con unità di tipo
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 "
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))