2011-09-19 7 views
6

Así que estoy jugando con esto:Conseguir un valor de tipo de una Integral => [a] a partir de un valor de una integral => ([a], [a], [a])

factors :: Integral a => a -> [a] 
factors n = filter (\d -> n `rem` d == 0) . takeWhile (\d -> d*d <= n) $ [ 1 .. ] 

abundants_perfects_deficients :: Integral a => ([a],[a],[a]) 
abundants_perfects_deficients = foldr switch ([],[],[]) [1..] 
    where switch :: Integral a => a -> ([a],[a],[a]) -> ([a],[a],[a]) 
     switch n (as,ps,ds) = 
      let t = sum (factors n) in 
       if t < n then (as,ps,n:ds) 
      else if t == n then (as,n:ps,ds) 
      else     (n:as,ps,ds) 

Y si bien tengo abundants_perfects_deficients, prefiero tener tres valores: abundants, perfects, y deficients todos de tipo Integral a -> [a].

Una cosa que no funciona es:

abundants,perfects,deficients :: Integral a => [a] 
(abundants,perfects,deficients) = abundants_perfects_deficients 

Debido a que este constriñe los tres para ser todos en el mismo a.

que hemos probado algo que ver ellos uno por uno, para que no se limitará mutuamente, pero eso no funcionó bien:

perfects :: Integral a => [a] 
(_,perfects,_) = abundants_perfects_deficients 

Debido a que el compilador no pudo encontrar la manera de convertir un valor de tipo forall a. Integral a => ([a],[a],[a]) para escribir (t1, forall a. Integral a => [a], t2).

Lo que parece bastante chulo.

Ahora sé que podría implementar por separado (solo perfects = filter isPerfect [1..]), o limitan a ser todos del mismo tipo ((abundants,perfects,deficients) = abundants_perfects_deficients funciona bien si abundants,perfects,deficients :: [Integer]), pero

  • me gusta usar la información compartida para construir los tres
  • Quiero simplemente no estando limitado a Integer s

ideas?


Editar: lo suficientemente fascinante esto funciona:

abundants :: Integral a => [a] 
abundants = f as 
    where as :: [Integer] 
     (as,_,_) = abundants_perfects_deficients 
     f :: Integral a => [Integer] -> [a] 
     f = map fromInteger 

pero esto no significa:

abundants_perfects_deficients' :: (Integral a,Integral p, Integral d) => ([a],[p],[d]) 
abundants_perfects_deficients' = (f as, f ps, f ds) 
    where as,ps,ds :: [Integer] 
     (as,ps,ds) = abundants_perfects_deficients 
     f :: Integral a => [Integer] -> [a] 
     f = map fromInteger 

abundants,perfects,deficients :: (Integral a) => [a] 
(abundants,perfects,deficients) = abundants_perfects_deficients' 

no tengo ni idea de por qué.

Respuesta

7

Esto se relaciona con lo que los tipos polimórficos realmente quieren decir, que es un poco más complicada que la forma en que aparecen por primera vez.

En este punto, es probable que sea más fácil para cambiar de modo de pensar y de mirar el cuantificador como una forma de abstracción lambda: Un tipo como ∀ a. [a] es, pues, una función dar un solo argumento de tipo, y devolver una lista de cosas de ese tipo.Las restricciones de clase como Integral a se pueden ver como argumentos adicionales (específicamente, el diccionario de instancia) que se proporcionan implícitamente cuando GHC encuentra los valores para usted.

Para enfatizar el punto, voy a escribir los cuantificadores como /\ a -> para imitar la sintaxis lambda, y escribir restricciones de clase como argumentos regulares.

Dicho de esta manera, el tipo de abundants_perfects_deficients es /\a -> Integral a -> ([a],[a],[a]), y su intento inicial falló esencialmente porque estaba tratando de emparejar el patrón en el resultado de una función de dos argumentos. En muchos casos, GHC mezcla automágicamente estos argumentos implícitos para hacer que todo funcione, pero aquí es absolutamente imposible: para obtener cualquier resultado de abundants_perfects_deficients primero debe aplicarlo a ambos argumentos, obteniendo un resultado monomórfico, que es entonces atado usando el patrón. Incluso cuando el patrón vincula solo un valor, el resto es _, GHC aún necesita verificar el patrón del enlace, por lo que aunque parezca que los argumentos adicionales pueden ser transferidos al único identificador vinculado, esto falla para el mismo razonar como unir a los tres a la vez.

Para enlazar tres valores polimórficos con un patrón, en su lugar necesitaría que los argumentos adicionales estuvieran en el interior, dando a abundantes_perfects_deficients un tipo como (/\a -> Integral a -> [a], /\a -> Integral a -> [a], /\a -> Integral a -> [a]). Esto requiere the ImpredicativeTypes extension, que tiene un pasado algo accidentado, y del que todavía tengo dudas.

Mucho de lo que está tropezando aquí es que GHC no es lo suficientemente inteligente como para averiguar cosas "obvias", como argumentos de tipo flotante y restricciones implícitas en base a solamente se utiliza dentro de una parte particular de una unión. Dada la cantidad de magia que tiene detrás de las escenas, esto no me molesta demasiado. :]

La solución más simple es unir las tres por separado, usando una función de selección para extraer los elementos individuales. Esto permite que el enlace de nivel superior sea polimórfico de la manera esperada, con los argumentos implícitos que recibe implícitamente pasados ​​a abundants_perfects_deficients, y la función de proyección descarta simplemente los otros tres después de una coincidencia de patrón (ahora monomórfica).

3
abundants,perfects,deficients :: Integral a => [a] 
(abundants,perfects,deficients) = abundants_perfects_deficients 

Probar:

fst3 (a,_,_) = a 
snd3 (_,a,_) = a 
thd3 (_,_,a) = a 

abundants,perfects,deficients :: Integral a => [a] 
abundants = fst3 . abundants_perfects_deficients 
perfects = snd3 . abundants_perfects_deficients 
deficients = thd3 . abundants_perfects_deficients 
+0

no funcionará: 'FST :: (a, b) -> a' (y similar para snd) tendrá que proporcionar algunos nuevos accesores de tres tuplas;) –

+0

Ops, tiene razón. Actualizado para arreglarlo. – nulvinge

+0

necesita eliminar sus (.) Operadores (abundants_perfects_deficients no es una función) – rampion

1

fromIntegral podría ser útil:

Prelude> :t fromIntegral 
fromIntegral :: (Num b, Integral a) => a -> b 
1

Probablemente un poco offtopic, pero de todos modos.

Su función factors está mal (tratar de calcular factors 28;)

Aquí es un enfoque diferente al problema:

classifieds = map (\n -> (n, compare n (sum $ factors n))) [1..] 

perfects = map fst $ filter ((== EQ) . snd) classifieds 
abundants = map fst $ filter ((== GT) . snd) classifieds 
deficients = map fst $ filter ((== LT) . snd) classifieds 
+0

whoops :) buenas ideas, también. – rampion

Cuestiones relacionadas