2009-03-26 25 views
5

Estoy tratando de poner una llamada de función 'imprimir' en una función haskell.haskell: error al intentar llamar a putStrLn en la función

(un mensaje de depuración simple).

A continuación se muestra mi código y mensaje de error del compilador (ghc 6.10).

No entiendo por qué está agrupando la llamada puttr y la matriz vacía.

La matriz vacía es el valor de retorno para ese caso en particular (el mensaje de impresión es en realidad solo un stub por ahora).

¿Alguna idea de por qué esto no funciona?

Gracias

Mi Código:

 
isAFactor :: Integer -> Integer -> Bool 
isAFactor x y = x `mod` y == 0 

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num = 
    let quotient = div num 2 
    in 
     if(counter > quotient) 
      then do 
       putStrLn ("factorList is : " ++ show quotient) (*** Line 10***) 
       [] 
     else if(isAFactor num counter) 
      then [counter] ++ [quotient] ++ findFactors (counter + 1) num 
     else 
      findFactors (counter + 1) num 

error de GHC

 
    test.hs:10:4: 
    Couldn't match expected type `[a] -> [Integer]' 
      against inferred type `IO()' 
    In the expression: 
     putStrLn ("factorList is : " ++ show quotient) [] 
    In the expression: 
     do putStrLn ("factorList is : " ++ show quotient) [] 
    In the expression: 
     if (counter > quotient) then 
      do putStrLn ("factorList is : " ++ show quotient) [] 
     else 
      if (isAFactor num counter) then 
        [counter] ++ [quotient] ++ findFactors (counter + 1) num 
      else 
       findFactors (counter + 1) num 
+0

Creo que esto se debe a que findFactors es una función pura, por lo que no puede tener efectos secundarios (como IO). De esto se trata Haskell. Aunque no tengo idea de cómo proceder para depurar tu función. En C++ utilizo el registro de depuración todo el tiempo. –

+0

"do" no significa lo que piensas que significa. Es solo azúcar de sintaxis para la secuenciación de mónadas; no puedes "hacer" nada en Haskell. :-) – ShreevatsaR

Respuesta

18

Es importante recordar que Haskell es un lenguaje funcional pure. Esto significa que las funciones no tienen ningún efecto secundario, incluida la impresión de mensajes de depuración en su pantalla.

Sin embargo, es posible romper esta pureza y puede ser útil en la depuración. Eche un vistazo al módulo Debug.Trace. Allí encontrará una función trace :: String -> a -> a. Se puede utilizar en su código como este:

import Debug.Trace 

isAFactor :: Integer -> Integer -> Bool 
isAFactor x y = x `mod` y == 0 

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num = 
    let quotient = div num 2 
    in 
     if(counter > quotient) 
       then trace ("factorList is: " ++ show quotient) [] 
     else if(isAFactor num counter) 
      then [counter] ++ [quotient] ++ findFactors (counter + 1) num 
     else 
      findFactors (counter + 1) num 

Como los comentarios sugirieron:

Haskell es también una lengua lazy. Una expresión no se evalúa antes de que realmente se necesite el resultado. El uso de la función de rastreo puede ser un poco confuso en una configuración perezosa porque no siempre es fácil de entender cuando el mensaje de seguimiento se imprime en la pantalla (si está impreso en absoluto).

Como haskell es un tipo de lenguaje muy diferente, tal vez sea mejor intentar desarrollar programas de una manera igualmente diferente. Intente razonar acerca de sus funciones en lugar de utilizar trace y construcciones "unpure" similares. Aprenda a aprovechar el poderoso sistema de tipo haskells y use (por ejemplo) QuickCheck para probar su función una vez que pasó el verificador de tipos.

+0

Vale la pena señalar que, debido a que Haskell es un lenguaje perezosa, estas llamadas 'trace' también ocurren de forma perezosa. Debido a que no existe una secuencia explícita de operaciones, las llamadas a 'trace' podrían parecer estar fuera de servicio o incluso no ocurrir. Recuerde que está tratando con un lenguaje lento, también cuando está depurando. –

3

actualiza con aclaraciones

El problema es que IO en Haskell es monádico, el bloque comienza con do es un azúcar sintáctica para combinar expresiones monádicos (a veces llamado declaraciones) con los operadores monádicos. En este caso, la mónada en cuestión es la mónada IO, como se puede inferir de la llamada a putStrLn. El [] en la segunda línea del bloque do es en realidad no el valor del bloque do completo, más bien, se interpreta como el último argumento de putStrLn; no es que acepte un segundo argumento, pero el compilador ni siquiera llega al punto de resolver esto, porque termina antes con el tipo de error que citó. Para hacer que esa línea sea un comando, debe poner, por ejemplo, devolver, otra función monádica, delante de ella (es decir, return []). Sin embargo, no digo que esto te ayude a resolver tu problema.

El error de tipo se deriva del hecho de que las expresiones monádicas IO siempre tienen el tipo IO _; en su caso, el bloque do tiene este tipo también, que obviamente es incompatible con [Integer], el tipo que especificó en la firma.

En general, como Haskell es un lenguaje funcional puro con IO monádico, una vez que estás dentro de la mónada IO, no hay forma de salir de él, es contagioso. es decir, si una función tiene un bloque do con operaciones IO en ella, su firma necesariamente contendrá el tipo IO _, al igual que la firma de todas las otras funciones que invocan esta función, etc. (Otras mónadas proporcionan funciones de "salida", pero la mónada IO no).

+0

Realmente no se interpreta como el último argumento de putStrLn ¿verdad? Es el segundo argumento de (>> =). ¿los bloques no son necesariamente IO(), hay muchos tipos diferentes que los bloques pueden tener, incluyendo [a]! – cthulahoops

+0

Aquí tienes una cantidad de conceptos erróneos. Odio rechazar un mensaje serio, pero esto oscurece más de lo que revela. –

+0

@cthulahoops: El mensaje de error "En la expresión" putStrLn ("factorList es:" ++ mostrar cociente) [] "'parece indicar que mi interpretación es correcta. –

9

La publicación de Jonas cubre muy bien su pregunta, por lo que le daré una reescritura idiomática de su función findFactors. Me pareció útil para mí cuando estaba aprendiendo por primera vez.

Así que quieres encontrar todos los factores de un número dado n examinado cada número de 1 hasta n/2, la comprobación para ver si se trata de un factor de n y la construcción de una lista de los que son.

Su versión (con modificaciones mínimas para conseguir que funcione):

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num = 
    let quotient = div num 2 
    in 
     if(counter > quotient) 
      then [] 
     else if(isAFactor num counter) 
      then [counter] ++ findFactors (counter + 1) num 
     else 
      findFactors (counter + 1) num 

Un par de cambios de formato para que sea un poco más fácil de leer:

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num 
    | counter > div num 2 = [] 
    | otherwise = if num `isAFactor` counter 
       then counter:findFactors (counter+1) num 
       else findFactors (counter + 1) num 

Esto está muy bien, pero es menos que ideal en un par de formas. Primero, recalcula el cociente cada vez que se llama findFactors, que es n/2 divisiones (aunque ghc -O2 parece darse cuenta de esto y calcularlo solo una vez). En segundo lugar, es un poco molesto tener que lidiar con esa variable contraria en todas partes. En tercer lugar, sigue siendo bastante imperativo.

Otra forma de ver el problema sería tomar la lista de enteros de 1 hasta y filtrar por aquellos que son factores de n. Esto se traduce muy directamente en Haskell:

findFactors :: Integer -> [Integer] 
findFactors num = filter (isAFactor num) [1..(num `div` 2)] 

Podría ser una sorpresa encontrar que esto no tiene las mismas características de rendimiento que la versión anterior. Haskell no necesita asignar memoria para toda la lista hasta n/2 a la vez, solo puede generar cada valor según sea necesario.

7

Otros han hecho un buen trabajo explicando su código, así que déjenme ayudarlo a decodificar el mensaje de error.

Couldn't match expected type `[a] -> [Integer]' 
     against inferred type `IO()' 
In the expression: 
    putStrLn ("factorList is : " ++ show quotient) [] 

Me he perdido todas las otras partes "En la expresión"; solo muestran más y más contexto envolvente.

Todo en Haskell es una expresión, por lo tanto, todo tiene un tipo. Eso incluye algo como "putStrLn". Si escribe ": t putStrLn" en GHCi verá en él responda:

putStrLn :: String -> IO() 

Lo que significa que putStrLn es una función que toma una cadena y devuelve una "acción IO", que en este caso es la acción de poniendo el mensaje en la pantalla.En su código le dio a "putStrLn" una cadena, por lo que el compilador dedujo que la expresión "putStrLn (cosas)" tenía el tipo "IO()". Esa es la parte de "tipo inferido" del mensaje de error del compilador.

Mientras tanto, el compilador también estaba haciendo la inferencia de tipos en la otra dirección, de afuera hacia adentro. Entre otras cosas, se dio cuenta de que el presente "putStrLn (cosas)" expresión que parecía ser aplicada a una lista vacía, que tiene escriba "[a]" (es decir, una lista de algo, no sabemos qué). Además, el resultado de la expresión completa debería ser del tipo "[Entero]". Por lo tanto, la expresión "putStrLn (cosas)" debe ser una función para convertir "[]" en una lista de enteros, cuyo tipo está escrito "[a] -> [Entero]". Esa es la parte del "tipo esperado" del mensaje de error.

En este punto, el compilador concluyó que no podía coincidir con estos dos tipos, por lo que informó del error.

"no pudo igualar esperada tipo 'Foo ' contra el tipo inferido ' Bar'" es, probablemente, el mensaje de error más común que se obtiene al intentar compilar Haskell, por lo que su pena tratar de leerlo y entenderlo. Mire el tipo inferido e intente descubrir qué parte de la expresión entrecomillada tiene ese tipo. Luego intente descubrir por qué el compilador esperaba algo más mirando el código circundante.

+0

+1, buena explicación del mensaje de error. No pude entender cómo el código informó el error que cometió. Hasta que me di cuenta de que el interlocutor insertaba un salto de línea para insertar el comentario de la "línea 10". –

Cuestiones relacionadas