Traducción & # 8220; Por qué la programación funcional importa & # 8221; en Haskell

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

  •  05-07-2019
  •  | 
  •  

Pregunta

Para el enriquecimiento cultural e intelectual, he decidido aprender un poco de Haskell. He estado leyendo Hughes '" ¿Por qué importa la programación funcional " y estoy tratando de traducir su código en verdadero Haskell. A continuación, adjunto algunos de mis intentos (para las partes numéricas del documento; el algoritmo alfa-beta es aún más interesante, pero también tendría que escribir un evaluador de juegos desde cero).

En este punto, ha sido más un ejercicio en la sintaxis de Haskell que cualquier otra cosa. Ya he hecho cosas simples como traducir repetir en el nativo de Haskell iteración , traducir algunas funciones que usaban muchos paréntesis para la composición de la función (lo que las hace más libres de puntos). en el proceso), etc.

Pero mi código ciertamente funciona, y me pregunto si es lo suficientemente " Haskell-ish " ;. ¿Puede algún experto por ahí darme algunos consejos?

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

No hay solución correcta

Otros consejos

4.1 Raíces cuadradas de Newton-Raphson

Estas dos líneas

sqroot a0 eps n = within eps (iterate (next n) a0)
relativesqrt a0 eps n = relative eps (iterate (next n) a0)

son casi idénticos, por lo que podría abstraer las cosas un paso más allá:

sqroot method a0 eps n = method eps (iterate (next n) a0)
relativesqrt = sqroot relative
withinsqrt   = sqroot within

4.2 Diferenciación numérica

No veo el punto de tener h0 como un argumento para la función diferenciar , ya que es solo el punto de partida para el 0 secuencia limitante (Lo mismo no es cierto para a0 en el caso de Newton-Rhapson, donde el punto de partida puede ser importante).

Creo que es igualmente apropiado abstraer la tasa a la que este límite se aproxima a cero:

differentiate rate f x = map (easydiff f x) (iterate rate 1)

Por supuesto, uno podría hacer ambas cosas:

differentiate rate h0 f x = map (easydiff f x) (iterate rate h0)

En cualquier caso, es una decisión bastante arbitraria.

4.2 Integración

Puedes usar

zipWith (+) (integ f a m fa fm) (integ f m b fm fb)

en lugar de

map (uncurry (+)) (zip (integ f a m fa fm) (integ f m b fm fb))

que creo que es más legible.

Para dentro de y relativo usaría la notación protegida:

within eps (a:b:rest)
  | abs(a-b)<=eps = b
  | otherwise = within eps (b:rest)

Para segundo , ¡podrías escribir ! 1 . Supongo que especialmente esta última es una preferencia personal,

También debe dar las firmas de tipo.

Editar : si te gusta la ofuscación, prueba:

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 marcado, no probado, y nunca sé si el filtro es correcto o si tiene que ser negado;)

Roger Costello ha escrito un resumen en dos partes del John Hughes paper traduciendo el código original de Miranda a Haskell. Aquí están parte uno y parte dos de su informe.

Entonces, ¿es esta una traducción directa (es decir, tratar de mantener el código tan similar como sea posible) o incrustado (es decir, hacer tantos ajustes idiomáticos al código como sea posible, sin hacer que los ejemplos sean más difíciles de entender?) , por supuesto) uno?

Suponiendo que este " proyecto " todavía está en marcha ......

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top