Traduzione & # 8220; Perché è importante la programmazione funzionale & # 8221; in Haskell

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

  •  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 ......

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top