Question

J'ai fait une fonction similaire à la array de numpy. Il convertit les listes de tableaux, listes de listes de tableaux 2d, etc.

Il fonctionne comme ceci:

ghci> arrFromNestedLists ["hello", "world"] :: Array (Int, (Int, ())) Char
array ((0,(0,())),(1,(4,()))) [((0,(0,())),'h'),((0,(1,())),'e'),((0,(2,())),'l'),((0,(3,())),'l'),((0,(4,())),'o'),((1,(0,())),'w'),((1,(1,())),'o'),((1,(2,())),'r'),((1,(3,())),'l'),((1,(4,())),'d')]

(Int, (Int, ())) et non (Int, Int) parce que je ne sais pas d'une façon programatic d'augmenter la longueur d'un tuple. (Question de côté: est-il une telle façon)

Le codage de c'était maladroit et je devais faire une « solution » (passant autour des arguments factices aux fonctions) pour que cela fonctionne. Je me demande s'il y a une meilleure façon.

Alors, voici le code, interrompu par les détails des solutions de contournement laid:

{-# LANGUAGE FlexibleInstances, ScopedTypeVariables, TypeFamilies #-}

type family ListOfIndex i a
type instance ListOfIndex () a = a
type instance ListOfIndex (Int, i) a = [ListOfIndex i a]

class Ix i => ArrConv i where
  acBounds :: a -> ListOfIndex i a -> (i, i)
  acFlatten :: i -> ListOfIndex i a -> [a]

acBounds "devrait" être :: ListOfIndex i a -> (i, i). Et de même pour acFlatten. Chacun reçoit une variable factice (undefined est toujours la valeur donnée), car sinon je ne pouvais pas le compiler: (

arrFromNestedLists :: forall i a. ArrConv i => ListOfIndex i a -> Array i a
arrFromNestedLists lst =
  listArray
  (acBounds (undefined :: a) lst)
  (acFlatten (undefined :: i) lst)

Au-dessus est l'argument undefined factice qui passe au travail. Il raconte l'GHC quelle instance de ListOfIndex à utiliser.

instance ArrConv () where
  acBounds _ = const ((), ())
  acFlatten _ = (: [])

La fonction ci-dessous aurait dû être la fonction acBounds dans une instance de ArrConv, et est déclarée en dehors seulement parce que je dois utiliser ScopedTypeVariables et je ne sais pas comment je peux le faire en fonction dans une définition d'exemple ..

acSucBounds
  :: forall a i. ArrConv i
  => a -> [ListOfIndex i a] -> ((Int, i), (Int, i))
acSucBounds _ lst =
  ((0, inStart), (length lst - 1, inEnd))
  where
    (inStart, inEnd) = acBounds (undefined :: a) (head lst)

instance ArrConv i => ArrConv (Int, i) where
  acBounds = acSucBounds
  acFlatten _ = concatMap (acFlatten (undefined :: i))
Était-ce utile?

La solution

La raison pour laquelle les arguments supplémentaires à acBounds et acFlatten sont nécessaires est que les types a et i ne peuvent pas être récupérés à partir ListOfIndex i a -> (i, i) et ListOfIndex i a -> [a] respectivement. Une solution consiste à combiner les deux méthodes dans une méthode de type acArgs de ListOfIndex i a -> ((i, i), a). Maintenant, le seul problème est de l'utiliser dans l'instance de (Int, i) d'une manière qui empêche la typechecker de son type trop généralisant causant beaucoup le même problème que précédemment (par exemple, nous ne pouvons pas utiliser simplement fst . acArgs).

{-# LANGUAGE TypeFamilies, FlexibleInstances #-}

import Data.Array

type family ListOfIndex i a
type instance ListOfIndex () a = a
type instance ListOfIndex (Int, i) a = [ListOfIndex i a]

class Ix i => ArrConv i where
  acArgs :: ListOfIndex i a -> ((i, i), [a])

instance ArrConv () where
  acArgs x = (((), ()), [x])

instance ArrConv i => ArrConv (Int, i) where
  acArgs lst =
    (((0, inStart), (length lst - 1, inEnd)), args >>= snd)
    where
      args = map acArgs lst
      (inStart, inEnd) = fst (head args)

arrFromNestedLists :: ArrConv i => ListOfIndex i a -> Array i a
arrFromNestedLists = uncurry listArray . acArgs

Autres conseils

Si vous voulez garder acBounds et acFlatten séparé, vous pouvez ajouter un argument tag niveau de type à elle, à savoir acBounds aurait le type acBounds :: Proxy a -> ListOfIndex i a -> (i, i). Cela supprime la nécessité pour les arguments de undefined, puisque vous pouvez juste passer (Proxy :: SomeConcreteType) à elle; et acBounds n'a aucun moyen d'extraction des informations de valeur de niveau utile, puisqu'il est isomorphe (d'une manière non typée) pour le type d'unité.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top