2012-04-06 11 views
8

Las dos funciones siguientes son extremadamente similares. Leen elementos de [String] n, ya sea [Int] o [Float]. ¿Cómo puedo factorizar el código común? No conozco ningún mecanismo en Haskell que admita pasar tipos como argumentos.Pass Types como argumentos para una función en Haskell?

readInts n stream = foldl next ([], stream) [1..n] 
    where 
    next (lst, x:xs) _ = (lst ++ [v], xs) 
     where 
     v = read x :: Int 

readFloats n stream = foldl next ([], stream) [1..n] 
    where 
    next (lst, x:xs) _ = (lst ++ [v], xs) 
     where 
     v = read x :: Float 

Estoy en el nivel de principiante de Haskell, por lo que cualquier comentario sobre mi código es bienvenido.

+2

No es necesario a veces aquí, se puede llegar a funcionar con un mapa sencillo. p.ej. 'map read stream :: [Int]' También es posible que desee ver por qué quiere usar foldr en Haskell en lugar de foldl. –

+1

@EdwardKmett Gracias por su sugerencia. Lo que realmente quiero es leer solo n primeros elementos y devolver la lista y el resto de la secuencia. Ayer estuve soñoliento y no pude pensar bien. Creo que quieres decir que con foldr puedo usar el constructor: directamente ¿no? Más tarde lo reescribí como '(map read firstn, rest) donde (firstn, rest) = splitAt n stream', bastante similar a lo que sugeriste. –

+0

No necesita anidar 'donde'; puede poner 'next (lst, x: xs) _ = ...' y 'v = ...' en líneas consecutivas. – sdcvvc

Respuesta

14

Haskell admite un alto grado de polimorfismo. En particular

readAny n stream = foldl next ([], stream) [1..n] 
    where 
    next (lst, x:xs) _ = (lst ++ [v], xs) 
     where 
     v = read x 

tiene por tipo

readAny :: (Enum b, Num b, Read a) => b -> [String] -> ([a], [String]) 

tanto

readInts :: (Enum b, Num b, Read a) => [String] -> ([Int], [String]) 
readInts = readAny 

readFloats :: (Enum b, Num b, Read a) => [String] -> ([Float], [String]) 
readFloats = readAny 

que no necesita especializarse tipo. Haskell inferirá automáticamente el tipo más general posible, y aquí readAny hará lo que quiera.

No es posible pasar tipos como argumentos en Haskell. Rara vez lo necesitarías. Para esos pocos casos donde es necesario, puede simular el comportamiento pasando un valor con el tipo deseado.

Haskell tiene "polimorfismo de retorno" por lo que realmente no debe preocuparse por "pasar el tipo" - lo más probable es que las funciones harán lo que usted quiera sin que usted se lo indique.

+3

Irónicamente, * al pasar el tipo * es de alguna manera dependiente de la implementación lo que sucede detrás de escena, por lo tanto, la pregunta no está tan lejos de la realidad ... – Ingo

+1

Muchas implementaciones no pasan tanto el tipo como pasan un tipo de testigo consistente para los valores de clase de tipo. Algo como 'id' no necesita saber en absoluto el tipo de sus argumentos (siempre y cuando todo sea del mismo tamaño) –

+1

Eso es lo que quise decir con * implementación de manera dependiente *. Pero * id *, al no tener restricciones, no pasa nada en absoluto, supongo. El tipo garantiza que id no va a hacer nada con su argumento que necesitaría más información que solo "es algún valor", pero esto debería ser implícito. – Ingo

9

Básicamente lo que quiere es no declarar explícitamente el tipo. En su lugar, difiera declarando el tipo y permita que el motor de inferencia tome el control por usted. Además, creo que estás combinando fold con map. Así es como lo abordaría.

readList' :: Read a => [String] -> [a] 
readList' = map read 


ints = readList' ["1", "2"] :: [Int] -- [1, 2] 

floats = readList' ["1.0", "2.0"] :: [Float] -- [1.0, 2.0] 

leer sólo cosas n de la corriente, utilice take

Cuestiones relacionadas