69 votos

Cómo crear un polyvariadic función haskell?

Necesito una función que toma un número arbitrario de argumentos (Todos del mismo tipo), hace algo con ellos y luego se da un resultado. Una lista de argumentos es imposible en mi caso específico.

Mientras miraba a través de la haskell libs, no es la función printf de módulo Text.Printf, el cual se utiliza una táctica similar. Mal que yo no podía entender que la magia mirando la fuente.

Puede alguien explicar cómo logran esto o sabe de alguna página web/papel/lo que sea, donde puedo encontrar una buena descripción de este?

Razón:

La razón es muy fácil de llevar. Para la escuela (de la clase de informática), se debe escribir un módulo que es capaz de "registro" de una expresión matemática y expresarlo como una cadena (a Través de la escritura una instancia de Num/Real/etc para un mismo tipo de datos) y puede realizar varias operaciones en la misma.
Este tipo de datos contiene un especial constructor de una variable, que podrá ser sustituido por un valor o lo que sea por una función determinada. Uno de los objetivos es escribir una función que toma este tipo de expresión, las variables como pares de tipo (Char,Rational) y calcula el resultado de la expresión. Debemos mirar, cómo expresar el objetivo de la función mejor. (Mi idea: La función devuelve otra función que toma exactamente como tantos argumentos como vars se definen en la función - parece imposible).

104voto

KennyTM Puntos 232647

Los puntos clave de la printf es la capacidad para devolver una Cadena o una función. Copiado de http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/Text-Printf.html,

printf :: (PrintfType r) => String -> r
printf fmts = spr fmts []

class PrintfType t where
    spr :: String -> [UPrintf] -> t

instance (IsChar c) => PrintfType [c] where
    spr fmts args = map fromChar (uprintf fmts (reverse args))

instance (PrintfArg a, PrintfType r) => PrintfType (a -> r) where
    spr fmts args = \a -> spr fmts (toUPrintf a : args)

y la estructura básica podemos extraer fuera de es

variadicFunction :: VariadicReturnClass r => RequiredArgs -> r
variadicFunction reqArgs = variadicImpl reqArgs mempty

class VariadicReturnClass r where
   variadicImpl :: RequiredArgs -> AccumulatingType -> r

instance VariadicReturnClass ActualReturnType where
   variadicImpl reqArgs acc = constructActualResult reqArgs acc

instance (ArgClass a, VariadicReturnClass r) => VariadicReturnClass (a -> r) where
   variadicImpl reqArgs acc = \a -> variadicImpl reqArgs (specialize a `mappend` acc)

Por ejemplo:

class SumRes r where 
    sumOf :: Integer -> r

instance SumRes Integer where
    sumOf = id

instance (Integral a, SumRes r) => SumRes (a -> r) where
    sumOf x = sumOf . (x +) . toInteger

entonces podríamos usar

*Main> sumOf 1 :: Integer
1
*Main> sumOf 1 4 7 10 :: Integer
22
*Main> sumOf 1 4 7 10 0 0  :: Integer
22
*Main> sumOf 1 4 7 10 2 5 8 22 :: Integer
59

9voto

lispc Puntos 1

KennyTM la respuesta es excelente. A continuación es un ejemplo de la exec proceso de sumOf 1 4 7 10 :: Integer para dar una mejor ilustración.

sumOf 1 4 7 10
(( \ x -> ( sumOf . (x +) . toInteger ) 1 ) 4 7 10
((sumOf . (1 + ) . toInteger) 4 ) 7 10
( sumOf 5 ) 7 10
( sumOf . (5 + ) . toInteger ) 7 10
sumOf 12 10
sumOf . (12 + ) . toInteger 10
sumof 22
id 22
22

7voto

delnan Puntos 52260

En el artículo de wiki en variadic funciones, en este artículo se hace referencia. Supongo que esto es lo que printf, pero no la entiendo bien. De todos modos, esta es sin duda una exageración, especialmente desde que sus argumentos son todos del mismo tipo. Acaba de poner a todos en una sola lista. Eso es lo que las listas son buenas - un arbitrarias número de cosas del mismo tipo. Bueno, no muy bonito, pero difícilmente será más feo que un completo polyvariadic función.

6voto

Daniel Pratt Puntos 8151

Eché un vistazo a un ejemplo vinculado en el artículo que delnan que se hace referencia. Después de mirar fijamente durante un rato, creo que por fin de comprender lo que está pasando:

Se inicia con este tipo de clase:

class BuildList a r  | r-> a where
    build' :: [a] -> a -> r

Que poco después de que el pipe (|) es una dependencia funcional. Se dice que el tipo representado por a puede ser determinado por el tipo representado por r. En otras palabras, se le impide la definición de dos instancias de la BuildList typeclass con el mismo r (tipo de retorno), pero diferente, a.

Saltar un poco a donde el build' función es en realidad utilizado:

> build True :: [Bool]

Desde build es simplemente llamando a la build' con una lista vacía como el primer parámetro, esto es igual a:

> build' [] True :: [Bool]

En este ejemplo, build' es claramente devolver una lista. Debido a la dependencia funcional, que sólo puede ser que el enlace a esta instancia de la BuildList tipo de la clase:

instance BuildList a [a] where
    build' l x = reverse$ x:l

Bastante sencillo. El segundo ejemplo es más interesante. Ampliar la definición de" buildse convierte en:

> build' [] True False :: [Bool]

¿Cuál es el tipo de build' en este caso? Así, las reglas de prioridad de Haskell significa que el anterior también se podría haber escrito como este:

> (build' [] True) False :: [Bool]

Ahora queda claro que estamos pasando dos parámetros a build' y, a continuación, aplicar el resultado de la expresión a un parámetro con el valor 'False'. En otras palabras, la expresión (build' [] True) que se espera para devolver una función de tipo Bool -> [Bool]. Y que nos une a la segunda instancia de la BuildList typeclass:

instance BuildList a r => BuildList a (a->r) where
    build' l x y = build'(x:l) y

En esta invocación, l = [] y x = True y y = False, por lo que la definición se amplía a build' [True] False :: [Bool]. Que la firma se une a la primera instancia de build', y es bastante obvio a donde se va de allí.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X