2012-02-17 6 views
11

mi ritrovo spesso la scrittura di codice seguendo il modello:Come far funzionare una funzione [a] -> [a] su [(a, Int)]?

foo xs = map snd $ filter ((< 10).fst) $ zip xs [0..] 

bar ys = map snd $ sortBy (compare `on` fst) $ zip ys [0..] 

Ora voglio abstract questa via

foo = indexesOf (filter (<10)) 

bar = indexesOf sort 

indexesOf :: ([a] -> [a]) -> [a] -> [Int] 
indexesOf f xs = map snd $ magick $ zip xs [0..] where 
    magick = undefined 

come eseguire il magick?

+0

... sono davvero non è chiaro quello che si stai cercando di fare. –

+0

Ho una funzione che lavora su una lista, dico 'sort'. Ora invece della lista ordinata voglio recuperare gli * indici * degli elementi. Per esempio. per '[5.0, 8.0, 7.0]' Non voglio '[5.0. 7.0, 8.0] 'ma' [0,2,1] '. – Landei

risposta

11

La tua firma del tipo non funzionerà. Devi essere in grado di fornire alla funzione passata un elenco di tuple, il che significa che devi utilizzare tipi di rango più elevato per forzare la polimorfizzazione, o menzionare esplicitamente le tuple nella tua firma del tipo.

Senza questo, non è possibile "guardare dentro" la funzione per vedere come riordina gli elementi della lista. Infatti, data la tua firma del tipo, la funzione passata poteva fare tutto ciò che voleva nella lista, incluso inserire elementi che non erano nemmeno lì per cominciare!

Ecco quello che ho avuto modo di lavorare con i tipi più alto rango:

{-# LANGUAGE RankNTypes #-} 

import Data.List (sortBy) 
import Data.Ord (comparing) 

indexesOf :: (forall b. (b -> a) -> [b] -> [b]) -> [a] -> [Int] 
indexesOf f xs = map snd $ f fst $ zip xs [0..] 

foo :: (Ord a, Num a) => [a] -> [Int] 
foo = indexesOf (filter . ((< 10) .)) 

bar :: Ord a => [a] -> [Int] 
bar = indexesOf (sortBy . comparing) 

Nota che ho dovuto anche aggiungere un argomento in più per la funzione passata per dirgli come estrarre la parte che preoccupa dal elementi della lista su cui sta lavorando. Senza questo, si sarà in grado di utilizzare solo le funzioni che non controllano gli elementi dell'elenco, come ad esempio reverse e che non sarebbe molto utile.

Esempio eseguito in GHCi:

> let xs = [42, 0, 7, 3, 12, 17, 99, 36, 8] 
> foo xs 
[1,2,3,8] 
> bar xs 
[1,3,2,8,4,5,7,0,6] 
> indexesOf (const reverse) xs 
[8,7,6,5,4,3,2,1,0] 
+0

Penso che tu stia rispondendo alla domanda nel titolo. –

+0

(continua) ... ma negli esempi forniti la funzione è in realtà ':: (Ord a) => [a] -> [a]'. Quindi puoi "guardare dentro". –

+0

@RafaelCaetano: Devi ancora essere in grado di selezionare un diverso 'a', tramite un tipo di rango più alto o menzionando esplicitamente le tuple nella firma del tipo. Puoi _could_ rendere la firma del tipo 'indexOf :: (forall b. Ord b => [b] -> [b]) -> [a] -> [Int]', tuttavia ciò funzionerebbe solo per l'esempio 'sort' e non quello con 'filter', poiché la funzione sarebbe solo in grado di confrontare gli elementi l'uno con l'altro. Non sarebbe in grado di confrontare contro '10'. – hammar

4

Grande questione, ma ho il sospetto che non esiste tale funzione. Vedi Theorems For Free!. Come dice il martello, è necessario passare le funzioni che accettano le tuple in modo esplicito.

Ecco la mia versione leggermente semplificata che non richiede RankNTypes (che è, certamente, non è un buon miglioramento rispetto al codice originale):

import Data.List 
import Data.Ord 

indexesOf :: ([(a,Int)] -> [(a,Int)]) -> [a] -> [Int] 
indexesOf f xs = map snd $ f $ zip xs [0..] 

foo :: (Ord a,Num a) => [a] -> [Int] 
foo = indexesOf $ filter $ (< 10) . fst 

bar :: Ord a => [a] -> [Int] 
bar = indexesOf $ sortBy $ comparing fst 
+1

Questo è corretto, anche se i tipi di rango più alto ti danno garanzie leggermente più forti in quanto la funzione non può rovinare gli interi. Tutto ciò che può fare è riordinare, copiare e rimuovere gli elementi esistenti nell'elenco. – hammar

+0

È vero. Inoltre, quando uno ha una funzione di tipo a -> Int, deve essere banale, cioè una costante (o non definita). – mnish

Problemi correlati