2011-02-23 27 views
7

Hola grandes programadores por ahí,Haskell-problema: io cadena -> [int]

que estoy haciendo mis primeros pasos en Haskell y tienen una función que me confunde:

import Data.List.Split 
getncheck_guesslist = do 
    line <- getLine 
    let tmp = splitOneOf ",;" line 
    map read tmp::[Int] 

splitOneOf es en Data.List.Split (i instalado con Cabal instalar dividida) splitOneOf :: (Eq a)=> [a]->[a]->[[a]]

Desde el error que consigo es que hay algún tipo de incorrección - pero no saben cómo resolver esto entra en conflicto como IO sigue siendo un misterio para mí

quiero leer una entrada de números enteros separados por comas o punto y coma y obtener una lista de números enteros de modo:

  • ¿Cómo puedo comprobar si la entrada del usuario es de tipo int
  • cómo puedo "traducir" la entrada que es de tipo "IO String" a [Int]

gracias de antemano por pensamientos y sugerencias - la suya ε/2

+6

Si IO es un misterio, tiene dos opciones: o la revela aprendiéndola, o trabaja sin ella y escribe funciones sin IO. Sugeriría ir con la segunda opción por un tiempo. –

Respuesta

2

Si hay algo dentro de la mónada IO, no puede llevarlo al mundo exterior para su posterior procesamiento. En cambio, pasas tus funciones puras para trabajar dentro de las funciones dentro de IO.

Al final, su programa lee algunas entradas y escribe alguna salida, por lo que su función principal va a funcionar con IO, de lo contrario, no podrá emitir nada. En lugar de leer IO String y crear un [Int], pase la función que consume ese [Int] a su función principal y úselo dentro de do.

12

Cuando está escribiendo una función que utiliza la mónada IO, cualquier valor que desee devolver de la función también debe estar en la mónada IO.

Esto significa que, en lugar de devolver un valor con el tipo [Int], debe devolver algo con el tipo IO [Int]. Para hacer esto, usa la función return, que "envuelve" el valor en IO (en realidad funciona para cualquier mónada).

acaba de cambiar la última línea de envolver su valor con return, así:

getncheck_guesslist = do 
    line <- getLine 
    let tmp = splitOneOf ",;" line 
    return (map read tmp :: [Int]) 
2
import Data.List.Split 
import Control.Applicative 

getncheck_guesslist :: IO [Int] 
getncheck_guesslist = map read . splitOneOf ",;" <$> getLine 

Cada vez pantanos de código a foo >>= return . bar, que el suyo lo hace por desugaring el do-bloque (y corregir el tipo de error), que no está utilizando las características monádicos de su mónada, sino sólo su parte funtor, es decir, sin rodeos dicho esto, no está ensuciando con la parte IO del tipo, pero con la a de IO a:

(<$>) :: (Functor f) => (a -> b) -> f a -> f b 

(fmap sería el nombre no infix para <$>. Ambos están estrechamente relacionados con map.)

Ese código es más o menos idiomática para el código ad hoc, pero el código limpio se vería

import Data.Maybe 

maybeRead :: Read a => String -> Maybe a 
maybeRead = fmap fst . listToMaybe . reads 
           -- That fmap is using "instance Functor Maybe" 

parseGuessList :: String -> [Int] 
parseGuessList = catMaybes . map maybeRead . splitOneOf ",;" 

getncheck_guesslist = parseGuessList <$> getLine 

, o, alternativamente, si usted no quiere hacer caso omiso de entrada no int pero el error cabo,

parseGuessList xs = if success then Just . catMaybes $ ys else Nothing 
    where ys :: String -> [Mabye Int] 
     ys = map maybeRead . splitOneOf ",;" $ xs 
     success = all isJust ys 

(Tenga cuidado, sin embargo, sólo han demostrado que el código correcto, en realidad no probado.)

Si se vuelve más compleja que la que te gustaría utilizar una libra de análisis adecuado ry, creo.

+0

Esto simplemente fue más allá de mi cabeza. No voy a bajar de categoría, pero tengo la sensación de que también pasó por la cabeza del póster original. – Zach

7

La "forma correcta" de hacerlo en Haskell es separar IO de, bueno, todo lo demás. La traducción directa de su código sería el siguiente:

getncheck_guesslist :: IO [Int] 
getncheck_guesslist = do line <- getLine    -- get 
         return (check_guesslist line) -- check 

check_guesslist :: String -> [Int] 
check_guesslist line = let tmp = splitOneOf ",;" line 
         in map read tmp 

en cuenta que getncheck_guesslist es simplemente una acción IO. La función no tiene los parámetros de entrada, aunque requiere la entrada (IO) de getLine.

Observe también que getncheck_guesslist es una modificación simple de la acción IO getLine. ¿No hay un combinador que me permita activar una función para actuar sobre el valor dentro de una mónada? Detener. Hoogle tiempo!

Tengo una función (a -> b). Tengo un valor del tipo de entrada, pero está atascado en una mónada m a. Quiero realizar la función dentro de la mónada, por lo que el resultado inevitablemente estará atascado en la mónada también m b. Poniéndolo todo junto, hoogle (a -> b) -> m a -> m b. Y he aquí, fmap es justo lo que estábamos buscando.

get_guesslist = check_guesslist `fmap` getLine 
-- or, taking it a step further 
get_guesslist = (map read . splitOneOf ",;") `fmap` getLine :: IO [Int] 

Como nota final, siempre que codifique un método con el nombre como somethingAndSomethingElse, por lo general es mejor estilo de codificación para escribir e invocar something y somethingElse como dos métodos distintos. Para las versiones finales, acabo de renombrarlo get_guesslist, ya que conceptualmente eso es lo que hace. Consigue las conjeturas como una lista de Ints.

Como nota final, me quedé en el punto en que comenzó barsoap. ;) fmap es lo mismo que <$>.