Pregunta

Hice una función similar a la de array numpy. Convierte listas para arrays, listas de listas a arrays 2d, etc.

Funciona de esta manera:

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, ())) y no (Int, Int) porque no sé de una manera programática para aumentar la longitud de una tupla. (Pregunta lado: ¿hay tal forma)

La codificación de que era torpe y tuve que hacer una "solución" (que pasa alrededor de los argumentos ficticios de funciones) para que funcione. Me pregunto si hay una manera mejor.

Así que aquí está el código, interrumpido con los detalles de las soluciones feas:

{-# 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 "debería" ser :: ListOfIndex i a -> (i, i). Y lo mismo para acFlatten. Cada uno recibe una variable ficticia (undefined es siempre el valor dado) porque de lo contrario no podría conseguir que se compile: (

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

El anterior es el argumento undefined ficticio que pasa en el trabajo. Cuenta la GHC qué instancia de ListOfIndex a utilizar.

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

La función de abajo debería haber sido la función acBounds en una instancia de ArrConv, y está declarado fuera sólo porque necesito usar ScopedTypeVariables y no sé cómo puedo hacerlo de una función en una definición de ejemplo ..

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))
¿Fue útil?

Solución

La razón de que los argumentos adicionales a acBounds y acFlatten son necesarios es que los tipos a y i no se pueden recuperar de ListOfIndex i a -> (i, i) y ListOfIndex i a -> [a] respectivamente. Una solución consiste en combinar los dos métodos en uno acArgs método de tipo ListOfIndex i a -> ((i, i), a). Ahora bien, el único problema es utilizarlo en el caso de (Int, i) de una manera que impide que el typechecker de generalizar su tipo demasiado causando el mismo problema que antes (por ejemplo, no podemos simplemente utilizar 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

Otros consejos

Si desea mantener acBounds y acFlatten separada, se puede añadir un tipo de nivel de argumento etiqueta nofollow a ella, es decir acBounds tendría tipo acBounds :: Proxy a -> ListOfIndex i a -> (i, i). Esto elimina la necesidad de que los argumentos undefined, ya que sólo puede pasar (Proxy :: SomeConcreteType) a ella; y acBounds no tiene ninguna manera de extraer cualquier información a nivel de valor útil de ella, ya que es isomorfo (de una manera sin tipo) para el tipo de unidad.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top