2010-08-04 8 views
5

He estado aprendiendo Haskell recientemente y estaba hablando con un amigo que está trabajando a través de SICP. Teníamos curiosidad por comparar Common Lisp y Scheme, así que decidí como ejercicio intentar traducir el ejercicio 1.29 en Haskell.Haskell jerarquía de tipo numérico en ejercicios SICP

Este ejercicio utiliza una función sigma que representa la función de suma matemática Sigma. Esta función toma una función f para aplicar a cada término, un límite inferior, una función para aplicar a cada término para obtener el siguiente término y un límite superior. Devuelve la suma de f aplicada a cada término.

simpsonIntegral se supone que usa la regla de Simpson para aproximar la integral de la función f sobre el rango [a, b] usando una "precisión" n. Tengo problemas para que funcione esta función porque parece que hay algo que no entiendo acerca de los tipos involucrados.

Este código se compilará con la versión 6.12.1 de ghc, pero a simpsonIntegral se le asignará un contexto de tipo (Integral a, Fractional a) que no tiene sentido y la función explotará tan pronto como lo llame. Lo he conseguido en un punto, pero lo que hice fue tan obviamente un truco que quería preguntar aquí cómo se manejaría esto idiomáticamente.

¿Cómo se maneja idiomáticamente la conversión Integral -> Fraccional/Real necesaria en h? Leí varias cosas, pero nada parecía obvio y limpio.

sigma :: (Ord a, Num b) => (a -> b) -> a -> (a -> a) -> a -> b 
sigma f a next b = iter a 0 
    where 
    iter current acc | current > b = acc 
        | otherwise = iter (next current) (acc + f current) 

simpsonIntegral f a b n = 1.0 * (h/3) * (sigma simTerm 0 (1+) n) 
    where 
    h = (b - a)/n 
    simTerm k = (yk k) * term 
     where 
     yk k = f (a + h * k) 
     term = 
      case k of 
      0 -> 1 
      1 -> 1 
      otherwise -> if odd k then 4 else 2 

Respuesta

3

Para dar seguimiento a la respuesta de la justicia: si eres curioso acerca de dónde colocar los fromIntegral s, los siguientes compila:

simpsonIntegral :: (Integral a, Fractional b) => (b -> b) -> a -> a -> a -> b 
simpsonIntegral f a b n = 1.0 * (h/3) * (sigma simTerm 0 (1+) n) 
    where 
    h = fromIntegral (b - a)/fromIntegral n 
    simTerm k = (yk k) * term 
     where 
     yk k = f (fromIntegral a + h * fromIntegral k) 
     term = 
      case k of 
      0 -> 1 
      1 -> 1 
      otherwise -> if odd k then 4 else 2 

Y parece que funciona:

*Main> simpsonIntegral (^3) 0 1 100 
0.2533333233333334 
*Main> simpsonIntegral (^3) 0 1 1000 
0.2503333333323334 
6
fromIntegral :: (Integral a, Num b) => a -> b 

r = fromIntegral i 
+0

Pregunta acerca deIntegral. Estaba intentando esto pero no en los lugares correctos según la respuesta de Travis y pensé que no estaba funcionando porque según este http://www.haskell.org/tutorial/numbers.html núm no proporciona un operador de división. ¿Es de Polimórfico Integral en el tipo de retorno e infladores de ghc que quiero un tipo de retorno fraccional? – asm

+0

Sí, 'fromIntegral' es polimórfico en su tipo de devolución' b', en que 'b' (el tipo de devolución) puede ser cualquier tipo de datos que sea miembro de la clase' Num'. Mientras que la clase de tipo 'Num' no proporciona división, algunos tipos de datos que son miembros de esa clase de tipo proporcionan división, como' Double'. – yfeldblum

1

El problema es que la función "impar" espera que su argumento sea de tipo Integral. El compilador luego deduce que su variable "k" es de tipo Integral. Pero al usar la operación "/", el compilador infiere "k" para que también sea de tipo Fraccional. La solución puede ser tan simple como convertir "k" a un entero, donde es realmente necesario:

if odd (round k) then 4 else 2 

Si desea obtener más información acerca de la conversión numérica en Haskell, comprobar Converting_numbers

Como nota al margen, aquí hay otra forma de escribir su función sigma:

sigma f a next b = sum $ map f $ takeWhile (<= b) $ iterate next a