2010-01-11 14 views
5

Ho eseguito una funzione simile a numpy array. Converte le liste per gli array, liste di liste per le matrici 2D, eccFamiglia di tipi Haskell e argomenti fittizi

funziona così:

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,())) e non (Int, Int) perché io non conosco un modo programmatico per aumentare la lunghezza del una tupla. (domanda a parte: c'è un modo?)

La codifica di esso era scomoda e dovevo fare una "soluzione" (passando gli argomenti fittizi alle funzioni) perché funzionasse. Mi chiedo se c'è un modo migliore.

Quindi, ecco il codice, interrotta con i dettagli del brutto soluzioni alternative:

{-# 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 "dovrebbe" essere :: ListOfIndex i a -> (i, i). E allo stesso modo per acFlatten. Ciascuno è data una variabile dummy (undefined è sempre il valore dato) perché altrimenti non potrei farlo per compilare :(

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

Sopra è il manichino undefined argomento passando al lavoro. Racconta la GHC quale istanza di ListOfIndex da usare.

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

la funzione qui sotto avrebbe dovuto essere la funzione acBounds in un'istanza di ArrConv, ed è dichiarata fuori solo perché ho bisogno di usare ScopedTypeVariables e non so come posso farlo in un funzione in una definizione di istanza

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)) 
+3

"Non conosco un modo programmatico per aumentare la lunghezza di una tupla." Non penso che tu possa Questo è un perfetto esempio di una funzione il cui tipo dipende da un valore. Sarebbe facile farlo in un linguaggio tipizzato come "Agda", ma impossibile in Haskell. Forse potresti usare 'GADTs 'in qualche modo per darti un comportamento dipendente, ma in cima alla mia testa, non so come. –

+0

Forse Template Haskell può essere utile: http://www.haskell.org/bz/thdoc.htm http://www.haskell.org/haskellwiki/Template_Haskell – primodemus

+0

@primodemus: Con TH ho potuto creare istanze per 'ArrConv' per matrici di fino a 10 dimensioni, e userebbero tuple normali per gli indici, che è un miglioramento.Ma mi sento come se il limite fosse arbitrario e il codice sarebbe probabilmente molto meno leggibile. – yairchu

risposta

4

La ragione per cui gli argomenti extra a acBounds e acFlatten sono necessario è che i tipi a e i non possono essere recuperati rispettivamente da ListOfIndex i a -> (i, i) e ListOfIndex i a -> [a]. Una soluzione alternativa consiste nel combinare i due metodi in un metodo acArgs di tipo ListOfIndex i a -> ((i, i), a). Ora l'unico problema è usarlo nell'istanza di (Int, i) in un modo che impedisce al typechecker di generalizzare troppo il suo tipo causando lo stesso problema di prima (ad esempio, non possiamo semplicemente usare 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 
+0

@Reid Barton: Fantastico! Grazie :) Ho rifattorato la tua risposta un po '(spero non ti dispiaccia): invece di passare 'acArgs' a una funzione lo rendono monomorfico, ora lo usa solo in un posto. – yairchu

+0

Ah, capisco. Bello. –

0

Se si desidera mantenere acBounds e acFlatten separata, si potrebbe aggiungere un type-level tag argument ad esso, vale a dire acBounds avrebbe tipo acBounds :: Proxy a -> ListOfIndex i a -> (i, i). Ciò elimina la necessità degli argomenti undefined, dal momento che puoi semplicemente passare (Proxy :: SomeConcreteType) ad esso; e acBounds non ha alcun modo di estrarre informazioni utili a livello di valore da esso, poiché è isomorfo (in un modo non tipizzato) al tipo di unità.

Problemi correlati