Traduzione & # 8220; Perché è importante la programmazione funzionale & # 8221; in Haskell
-
05-07-2019 - |
Domanda
Per l'arricchimento culturale e intellettuale, ho deciso di imparare un po 'di Haskell. Ho letto Hughes "" Perché la programmazione funzionale conta " e sto cercando di tradurre il suo codice in vero Haskell. Di seguito ho allegato alcuni dei miei tentativi (per le parti numeriche del documento; l'algoritmo alfa-beta è ancora più interessante ma dovrei anche scrivere un valutatore di gioco da zero!).
A questo punto è stato più un esercizio di sintassi di Haskell che altro. Ho già fatto cose semplici come tradurre repeat
nel iterato
nativo di Haskell, tradurre alcune funzioni che hanno usato molte parentesi per funzionare la composizione (rendendole più prive di punti nel processo), ecc.
Ma sicuramente il mio codice funziona, e mi chiedo se sia sufficientemente "quotato". Qualche esperto là fuori può darmi qualche suggerimento?
-- 4.1 Newton-Raphson square roots
next n x = (x + n/x)/2.0
-- -- this is "iterate::(a->a)->a->[a]"
-- repeat f a = a : iterate f (f a)
within eps (a:b:rest) =
if abs(a-b) <= eps
then b
else within eps (b:rest)
sqroot a0 eps n = within eps (iterate (next n) a0)
relative eps (a:b:rest) =
if abs(a-b) <= eps*abs(b)
then b
else relative eps (b:rest)
relativesqrt a0 eps n = relative eps (iterate (next n) a0)
-- 4.2 numerical differentiation
easydiff f x h = (f (x+h) - f x) / h
differentiate h0 f x = map (easydiff f x) (iterate (/2) h0)
-- diff1a h0 eps f x = within eps (differentiate h0 f x)
diff1 h0 eps f = within eps . differentiate h0 f
elimerror n (a:b:rest) = (b*(2**n)-a)/(2**n-1) : elimerror n (b:rest)
-- need fromIntegral to make a non-integer out of the Int which comes out of round
order (a:b:c:rest) = fromIntegral (round (logBase 2 ((a-c)/(b-c)-1)))
improve s = elimerror (order s) s
--diff2a h0 eps f x = within eps (improve (differentiate h0 f x))
diff2 h0 eps f = within eps . improve . differentiate h0 f
--super s = map second (iterate improve s) -- how can we make this point-free?
super :: (RealFrac t, Floating t) => [t] -> [t]
-- w/o this it wants to be [double]->[double]
super = map second . iterate improve
-- second (a:b:rest) = b
second = head . tail
diff3 h0 eps f = within eps . super . differentiate h0 f
-- 4.3 integration
easyintegrate f a b = (f a + f b)*(b-a)/2
-- addpair becomes (uncurry (+))
integrate f a b = integ f a b (f a) (f b)
integ f a b fa fb =
(fa+fb)*(b-a)/2 : map (uncurry (+)) (zip (integ f a m fa fm) (integ f m b fm fb))
where m = (a+b)/2
fm = f m
-- test: following should be about pi
approxpi eps = within eps (improve (integrate (\x -> 4/(1+x*x)) 0 1))
superpi eps = within eps (super (integrate (\x -> 4/(1+x*x)) 0 1))
-- is there any way to keep track of the number of iterations? state monad, but seems like a lot of work...\
Nessuna soluzione corretta
Altri suggerimenti
4.1 Radici quadrate di Newton-Raphson
Queste due righe
sqroot a0 eps n = within eps (iterate (next n) a0)
relativesqrt a0 eps n = relative eps (iterate (next n) a0)
sono quasi identici, quindi puoi astrarre le cose un ulteriore passo:
sqroot method a0 eps n = method eps (iterate (next n) a0)
relativesqrt = sqroot relative
withinsqrt = sqroot within
4.2 Differenziazione numerica
Non vedo il punto di avere h0
come argomento della funzione differenziare
poiché è solo il punto di partenza per 0
sequenza limitante. (Lo stesso non è vero per a0
nel caso Newton-Rhapson, dove il punto di partenza potrebbe essere importante).
Penso che sia altrettanto appropriato astrarre sulla velocità con cui questo limite si avvicina allo zero:
differentiate rate f x = map (easydiff f x) (iterate rate 1)
Naturalmente si potrebbero fare entrambe le cose:
differentiate rate h0 f x = map (easydiff f x) (iterate rate h0)
In ogni caso, è una decisione piuttosto arbitraria.
4.2 Integrazione
Puoi usare
zipWith (+) (integ f a m fa fm) (integ f m b fm fb)
anziché
map (uncurry (+)) (zip (integ f a m fa fm) (integ f m b fm fb))
che penso sia più leggibile.
Per in
e relative
userei la notazione protetta:
within eps (a:b:rest)
| abs(a-b)<=eps = b
| otherwise = within eps (b:rest)
Per secondo
, puoi scrivere !! 1
. Soprattutto l'ultimo è la preferenza personale, immagino.
Dovresti anche dare le firme del tipo.
Modifica : se sei in offuscamento, prova:
within :: (Ord a, Num a) => a -> [a] -> a
within eps l@(_:xs) = snd. head . filter ((<= eps) . fst) $ zip zs xs
where
zs = zipWith (\ a b -> abs (a-b)) l xs
(Tipo selezionato, non testato e non so mai se il filtro è corretto o se deve essere negato;)
Roger Costello ha scritto un riassunto in due parti del John Hughes paper traduzione del codice Miranda originale in Haskell. Ecco prima parte e seconda parte del suo articolo.
Quindi, si tratta di una traduzione diretta (ad esempio, cerca di mantenere il codice il più simile possibile) o di un incorporato (ad esempio, fai quante più modifiche idiomatiche possibili al codice, senza rendere gli esempi più difficili da capire , ovviamente) uno?
Supponendo che questo "progetto" continua ancora ......