Tengo un ejercicio en el que tengo que definir un tipo para representar una lista con 0 a 5 valores. Primero pensé que podría resolver esto de forma recursiva así:
data List a = Nil | Content a (List a)
Pero no creo que este sea el enfoque correcto. ¿Puedes por favor darme un poco de pensamiento?
No responderé su ejercicio por usted; para los ejercicios, es mejor averiguar la respuesta usted mismo, pero aquí hay una pista que lo llevará a la respuesta: puede definir una lista con 0 a 2 elementos como
data List a = None | One a | Two a a
Ahora, piense en cómo puede extender esto a cinco elementos.
Bueno, una solución recursiva es ciertamente lo normal y de hecho algo bueno en Haskell, pero es un poco difícil limitar la cantidad de elementos. Entonces, para una solución simple al problema, primero considere la estúpida pero funcional dada por bradm.
Con la solución recursiva, el truco consiste en pasar una variable de "contador" por la recursión, y luego deshabilitar el consumo de más elementos cuando alcance el máximo permitido. Esto se puede hacer muy bien con un GADT:
{-# LANGUAGE GADTs, DataKinds, KindSignatures, TypeInType, StandaloneDeriving #-}import Data.Kindimport GHC.TypeLitsinfixr5:#data ListMax :: Nat -> Type -> Type where
Nil :: ListMax n a(:#):: a -> ListMax n a -> ListMax (n+1) aderivinginstance(Show a)=> Show (ListMax n a)
Entonces
*Main>0:#1:#2:#Nil :: ListMax 5 Int0:#(1:#(2:# Nil))*Main>0:#1:#2:#3:#4:#5:#6:#Nil :: ListMax 5 Int<interactive>:13:16: error:• Couldn't match type‘1’ with ‘0’
Expected type: ListMax 0 Int
Actual type: ListMax (0+1) Int• In the second argument of‘(:#)’, namely ‘5:#6:# Nil’
In the second argument of‘(:#)’, namely ‘4:#5:#6:# Nil’
In the second argument of‘(:#)’, namely ‘3:#4:#5:#6:# Nil’
muchas gracias. Debido a que es un ejercicio para principiantes, creo que es el enfoque más fácil. Pero también pensaré en tu enfoque.
mayerph
7
En aras de la exhaustividad, permítanme agregar un enfoque alternativo "feo", que sin embargo es bastante básico.
Recordemos que Maybe aes un tipo cuyos valores son de la forma Nothingo Just xpara algunos x :: a.
Por lo tanto, al reinterpretar los valores anteriores, podemos considerar Maybe acomo un "tipo de lista restringida" donde las listas pueden tener cero o un elemento.
Ahora, (a, Maybe a)simplemente agrega un elemento más, por lo que es un "tipo de lista" donde las listas pueden tener uno ( (x1, Nothing)) o dos ( (x1, Just x2)) elementos.
Por lo tanto, Maybe (a, Maybe a)es un "tipo de lista" donde las listas pueden tener elementos cero ( Nothing), uno ( Just (x1, Nothing)) o dos ( (Just (x1, Just x2)).
Ahora debería poder entender cómo proceder. Permítanme enfatizar nuevamente que esta no es una solución conveniente para usar, pero es (IMO) un buen ejercicio para entenderlo de todos modos.
Usando algunas características avanzadas de Haskell, podemos generalizar lo anterior usando una familia de tipos:
type family List (n :: Nat)(a :: Type):: Type where
List 0 a =()
List n a = Maybe (a, List (n-1) a)
Esta respuesta podría ampliarse con la familia de tipos de lista basada en Quizás de longitud máxima n .
Leftaroundabout
@leftaroundabout Hecho. Eso podría ser demasiado para un principiante, pero lo agregué de todos modos.
chi
como máximo tres as en Either () (a, Either () (a, Either () (a, Either () ())))... álgebra tipo interesante foldr (.) id (replicate 3 $ ([0] ++) . liftA2 (+) [1]) $ [0] == [0,1,2,3].
En aras de la exhaustividad, permítanme agregar un enfoque alternativo "feo", que sin embargo es bastante básico.
Recordemos que
Maybe a
es un tipo cuyos valores son de la formaNothing
oJust x
para algunosx :: a
.Por lo tanto, al reinterpretar los valores anteriores, podemos considerar
Maybe a
como un "tipo de lista restringida" donde las listas pueden tener cero o un elemento.Ahora,
(a, Maybe a)
simplemente agrega un elemento más, por lo que es un "tipo de lista" donde las listas pueden tener uno ((x1, Nothing)
) o dos ((x1, Just x2)
) elementos.Por lo tanto,
Maybe (a, Maybe a)
es un "tipo de lista" donde las listas pueden tener elementos cero (Nothing
), uno (Just (x1, Nothing)
) o dos ((Just (x1, Just x2)
).Ahora debería poder entender cómo proceder. Permítanme enfatizar nuevamente que esta no es una solución conveniente para usar, pero es (IMO) un buen ejercicio para entenderlo de todos modos.
Usando algunas características avanzadas de Haskell, podemos generalizar lo anterior usando una familia de tipos:
fuente
a
s enEither () (a, Either () (a, Either () (a, Either () ())))
... álgebra tipo interesantefoldr (.) id (replicate 3 $ ([0] ++) . liftA2 (+) [1]) $ [0] == [0,1,2,3]
.