Typ zur Darstellung einer Liste mit 0 bis 5 Werten

14

Ich habe eine Übung, in der ich einen Typ für die Darstellung einer Liste mit 0 bis 5 Werten definieren muss. Zuerst dachte ich, ich könnte das rekursiv so lösen:

data List a = Nil | Content a (List a)

Aber ich denke nicht, dass dies der richtige Ansatz ist. Können Sie mir bitte einen Denkanstoß geben?

Mayerph
quelle

Antworten:

12

Ich werde Ihre Übung nicht für Sie beantworten - für Übungen ist es besser, die Antwort selbst herauszufinden -, aber hier ist ein Hinweis, der Sie zur Antwort führen sollte: Sie können eine Liste mit 0 bis 2 Elementen als definieren

data List a = None | One a | Two a a

Überlegen Sie nun, wie Sie dies auf fünf Elemente erweitern können.

bradrn
quelle
10

Nun, eine rekursive Lösung ist sicherlich die normale und in der Tat nette Sache in Haskell, aber es ist ein bisschen schwierig, die Anzahl der Elemente dann zu begrenzen. Betrachten Sie für eine einfache Lösung des Problems zunächst die dumme, aber funktionierende von Bradm.

Bei der rekursiven Lösung besteht der Trick darin, eine "Zähler" -Variable über die Rekursion zu übergeben und dann die Berücksichtigung weiterer Elemente zu deaktivieren, wenn Sie das maximal zulässige Maß erreichen. Dies kann gut mit einem GADT gemacht werden:

{-# LANGUAGE GADTs, DataKinds, KindSignatures, TypeInType, StandaloneDeriving #-}

import Data.Kind
import GHC.TypeLits

infixr 5 :#
data ListMax :: Nat -> Type -> Type where
  Nil :: ListMax n a
  (:#) :: a -> ListMax n a -> ListMax (n+1) a

deriving instance (Show a) => Show (ListMax n a)

Dann

*Main> 0:#1:#2:#Nil :: ListMax 5 Int
0 :# (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
links herum
quelle
Vielen Dank. Da es sich um eine Anfängerübung handelt, denke ich, dass dies der einfachere Ansatz ist. Aber ich werde auch über Ihren Ansatz nachdenken.
Mayerph
7

Lassen Sie mich der Vollständigkeit halber einen "hässlichen" alternativen Ansatz hinzufügen, der jedoch eher grundlegend ist.

Denken Sie daran, dass dies Maybe aein Typ ist, dessen Werte von der Form Nothingoder Just xfür einige sind x :: a.

Daher können wir durch Neuinterpretation der obigen Werte Maybe aeinen "eingeschränkten Listentyp" betrachten, bei dem Listen entweder null oder ein Element haben können.

Fügen Sie jetzt (a, Maybe a)einfach ein weiteres Element hinzu, sodass es sich um einen "Listentyp" handelt, bei dem Listen ein ( (x1, Nothing)) oder zwei ( (x1, Just x2)) Elemente enthalten können.

Daher Maybe (a, Maybe a)handelt es sich um einen "Listentyp", bei dem Listen null ( Nothing), eins ( Just (x1, Nothing)) oder zwei ( (Just (x1, Just x2)) Elemente enthalten können.

Sie sollten jetzt verstehen können, wie Sie vorgehen müssen. Lassen Sie mich noch einmal betonen, dass dies keine bequeme Lösung ist, aber es ist (IMO) eine schöne Übung, sie trotzdem zu verstehen.


Mit einigen erweiterten Funktionen von Haskell können wir das Obige mithilfe einer Typfamilie verallgemeinern:

type family List (n :: Nat) (a :: Type) :: Type where
    List 0 a = ()
    List n a = Maybe (a, List (n-1) a)
Chi
quelle
Diese Antwort könnte um die Typfamilie der Vielleicht-basierten Liste der maximalen Länge n erweitert werden .
Links um den
@leftaroundabout Fertig. Das mag für einen Anfänger etwas zu viel sein, aber ich habe es trotzdem hinzugefügt.
Chi
höchstens drei as in Either () (a, Either () (a, Either () (a, Either () ())))... interessanter Typalgebra , foldr (.) id (replicate 3 $ ([0] ++) . liftA2 (+) [1]) $ [0] == [0,1,2,3].
Will Ness