¿Diferencia entre `(Integer a) => a -> Bool` y ` Integer -> Bool`?

Escribí mi primer programa en Haskell hoy. Se compila y se ejecuta con éxito.. Y como no es un típico "Hola Mundo" programa, de hecho hace mucho más que eso, así que por favor felicítame :D

De todos modos, tengo algunas dudas con respecto a mi código y la sintaxis en Haskell.

Problema:

Mi programa lee un entero N de la entrada estándar y luego, para cada entero i en el rango [1,N], imprime si i es un número primo o no. Actualmente no comprueba si hay errores de entrada. :-)

Solución: (también dudas/preguntas)

Para resolver el problema, escribí esta función para probar la primalidad de un número entero:

is_prime :: Integer -> Bool
is_prime n = helper n 2
        where
          helper :: Integer -> Integer -> Bool
          helper n i  
              | n < 2 * i = True
              | mod n i > 0 = helper n (i+1)
              | otherwise = False

Funciona muy bien. Pero mi duda es que la primera línea sea el resultado de muchos intentos, como leí en este tutorial no funcionó, y dio este error (yo suponer esto es un error, aunque no lo dice):

prime.hs:9:13:
    Type constructor `Integer' used as a class
    In the type signature for `is_prime':
      is_prime :: Integer a => a -> Bool

Según el tutorial (que, por cierto, es un tutorial muy bien escrito), la primera línea debería ser: (el tutorial dice (Integral a) => a -> String, así que pensé (Integer a) => a -> Bool debería funcionar también.)

is_prime :: (Integer a) => a -> Bool

que no funciona, y da el error publicado anteriormente (?).

¿Y por qué no funciona? ¿Cuál es la diferencia entre esta línea (que no funciona) y la línea (que funciona)?


Además, ¿cuál es la forma idiomática de recorrer 1 a N? No estoy completamente satisfecho con el bucle en mi código. Por favor sugiera mejoras. Aquí está mi código:

--read_int function
read_int :: IO Integer
read_int = do
     line <- getLine
     readIO line

--is_prime function
is_prime :: Integer -> Bool
is_prime n = helper n 2
        where
          helper :: Integer -> Integer -> Bool
          helper n i  
              | n < 2 * i = True
              | mod n i > 0 = helper n (i+1)
              | otherwise = False

main = do
       n <- read_int
       dump 1 n
       where
           dump i x = do 
                 putStrLn ( show (i) ++ " is a prime? " ++ show (is_prime i) )
                 if i >= x 
                    then putStrLn ("")
                  else do
                    dump (i+1) x

preguntado el 30 de junio de 12 a las 17:06

Quieres decir Integral a? -

@Heatsink: No. Ni siquiera sé si Integral existe ¿Lo hace? ¿Se supone que debe ser Integral? -

Probablemente se supone que debe ser Integral. Además, sus dos hipervínculos van a la misma URL, ¿es intencional? -

@Heatsink: se corrigen los hipervínculos. -

Solo algunas cosas que no están estrictamente relacionadas con el problema que tiene: la aplicación de funciones en Haskell es solo una yuxtaposición, por lo que show (i) = show i, putStrLn ("") = putStrLn ""; la convención de nomenclatura dominante es camelCase (básicamente todas las bibliotecas lo usan) y read_int ya esta en Prelude bajo el nombre readLn. -

3 Respuestas

Estás leyendo mal el tutorial. Diría que la firma de tipo debe ser

is_prime :: (Integral a) => a -> Bool
--       NOT Integer a

Estos son de diferentes tipos:

  • Integer -> Bool
    • Esta es una función que toma un valor de tipo Integer y devuelve un valor de tipo Bool.
  • Integral a => a -> Bool
    • Esta es una función que toma un valor de tipo a y devuelve un valor de tipo Bool.
    • Qué es a? Puede ser cualquier tipo de elección de la persona que llama que implementa el Integral clase de tipo, como Integer or Int.

(Y la diferencia entre Int y Integer? El último puede representar un número entero de cualquier magnitud, el primero se ajusta eventualmente, similar a ints en C/Java/etc.)


La forma idiomática de hacer un bucle depende de lo que haga tu bucle: será un mapa, un pliegue o un filtro.

Tu bucle en main es un mapa, y debido a que está haciendo E/S en su ciclo, necesita usar mapM_.

let dump i = putStrLn ( show (i) ++ " is a prime? " ++ show (is_prime i) )
 in mapM_ dump [1..n]

Mientras tanto, tu bucle en is_prime es un pliegue (específicamente all en este caso):

is_prime :: Integer -> Bool
is_prime n = all nondivisor [2 .. n `div` 2]
        where
          nondivisor :: Integer -> Bool
          nondivisor i = mod n i > 0

(Y en un punto menor de estilo, es convencional en Haskell usar nombres como isPrime en lugar de nombres como is_prime.)

Respondido 01 Jul 12, 09:07

quisquilloso: coprime es un nombre engañoso. Resulta que en realidad solo se llama para i donde el resultado corresponde al nombre, pero sin corto circuito, is_prime 18 llamaría coprime 8 que luego se evaluaría como True aunque 8 y 18 no son coprimos. Llámalo nondivisor más o menos. - Daniel Fischer

@dave4420: No entendí tu definición de is_prime . Que es i in [2 .. i `div` 2]? ¿Cómo se decide su valor inicial? ¿Cómo se incrementa? y hasta que valor? - Nawaz

Ohh... creo que te referías a n en lugar de i (en la lista)? - Nawaz

@Nawaz Sí, lo siento, eso es exactamente lo que quise decir, simplemente lo escribí mal. Reparado. - dave4420

Parte 1: Si mira el tutorial nuevamente, notará que en realidad proporciona firmas de tipo en las siguientes formas:

isPrime :: Integer -> Bool
-- or
isPrime :: Integral a => a -> Bool
isPrime :: (Integral a) => a -> Bool -- equivalent

Aquí, Integer es el nombre de un tipo concreto (tiene una representación real) y Integral es el nombre de una clase de tipos. los Integer tipo es un miembro de la Integral clase.

La restricción Integral a significa que cualquier tipo a pasa a ser, a tiene que ser miembro de la Integral clase.

Parte 2: Hay muchas formas de escribir una función de este tipo. Su definición recursiva se ve bien (aunque es posible que desee usar n < i * i en lugar de n < 2 * i, ya que es más rápido).

Si está aprendiendo Haskell, probablemente querrá intentar escribirlo usando funciones de orden superior o listas de comprensión. Algo como:

module Main (main) where
import Control.Monad (forM_)

isPrime :: Integer -> Bool
isPrime n = all (\i -> (n `rem` i) /= 0) $ takeWhile (\i -> i^2 <= n) [2..]

main :: IO ()
main = do n <- readLn
          forM_ [1..n] $ \i ->
              putStrLn (show (i) ++ " is a prime? " ++ show (isPrime i))

Respondido el 30 de junio de 12 a las 19:06

@Vitus: Ohh, necesito importar eso. no me he encontrado import todavía. Gracias :-) - Nawaz

Por cierto, ¿qué significa esta sintaxis? \i -> (n `rem` i) /= 0 ¿media? - Nawaz

\i -> introduce una función anónima tomando un argumento (llamado i dentro del cuerpo de la función). n `rem` i es solo otra forma de escribir rem n i. Puede convertir cualquier función en un operador infijo rodeándola de acentos graves. - Vito

@Vitus: Y esto función anónima es más como lambda que capturas variables locales, como n, ¿derecho? - Nawaz

Es básicamente como si declararas una función local dentro where cláusula. Y sí, captura su contexto, es decir, todas las variables libres. - Vito

  1. Para los propietarios de Istanbul E-pass el Museo de Madame Tussauds de Estambul es Integral ano, Integer a. Vea http://www.haskell.org/haskellwiki/Converting_numbers.

  2. map y amigos es cómo haces un bucle en Haskell. Así es como volvería a escribir el bucle:

    main :: IO ()
    main = do
            n <- read_int
            mapM_ tell_prime [1..n]
            where tell_prime i = putStrLn (show i ++ " is a prime? " ++ show (is_prime i))
    

Respondido el 30 de junio de 12 a las 17:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.