2012-02-17 18 views
11

me encuentro a menudo la escritura de código siguiendo el patrón:¿Cómo operar una función [a] -> [a] en [(a, Int)]?

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

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

Ahora quiero el extracto esta distancia

foo = indexesOf (filter (<10)) 

bar = indexesOf sort 

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

cómo realizar la magick?

+0

... realmente no estoy claro lo que Estoy tratando de hacer. –

+0

Tengo una función que trabaja en una lista, digamos 'ordenar'. Ahora, en lugar de la lista ordenada, quiero recuperar los * índices * de los elementos. P.ej. para '[5.0, 8.0, 7.0]' No quiero '[5.0. 7.0, 8.0] 'pero' [0,2,1] '. – Landei

Respuesta

11

Su tipo de firma no funcionará. Debes poder darle a la función pasada una lista de tuplas, lo que significa que debes usar tipos de rango más alto para forzar que sea polimórfica o mencionar explícitamente tuplas en tu firma de tipo.

Sin esto, no puede "mirar dentro" de la función para ver cómo reorganiza los elementos de la lista. De hecho, dada su firma de tipo, la función aprobada podría hacer cualquier cosa que desee a la lista, ¡incluyendo insertar elementos que ni siquiera estaban allí para empezar!

Aquí es lo que tengo que trabajar utilizando los tipos de rango superior:

{-# 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) 

Tenga en cuenta que también he tenido que añadir un argumento adicional para la función pasada para decirle cómo extraer la parte que se preocupa de la elementos de la lista en la que está trabajando. Sin esto, solo podría usar funciones que no inspeccionan los elementos de la lista, como reverse, y eso no sería muy útil.

Ejemplo correr en 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

Creo que estás respondiendo la pregunta en el título. –

+0

(continuando) ... pero en los ejemplos dados, la función es en realidad ':: (Ord a) => [a] -> [a]'. Entonces puedes "mirar dentro". –

+0

@RafaelCaetano: Aún necesita poder elegir una 'a' diferente, ya sea a través de un tipo de rango superior o mencionando explícitamente tuplas en la firma de tipo. Usted podría hacer la firma de tipo 'indexOf :: (forall b. Ord b => [b] -> [b]) -> [a] -> [Int]', sin embargo eso solo funcionaría para el ejemplo 'sort' , y no el que tiene 'filter', ya que la función solo podría comparar elementos entre sí. No sería capaz de comparar contra '10'. – hammar

4

Muy buena pregunta, pero sospecho que no existe tal función. Ver Theorems For Free!. Como dice hammer, necesitas pasar funciones que toman tuplas explícitamente.

Aquí está mi versión ligeramente simplificada que no requiere RankNTypes (que es, sin duda, no una muy buena mejora sobre el código original):

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

Esto es correcto, aunque los tipos de rango superior le dan garantías ligeramente más fuertes ya que la función no puede interferir con los enteros. Todo lo que puede hacer es reordenar, copiar y eliminar los elementos existentes en la lista. – hammar

+0

Eso es verdad. Además, cuando uno tiene una función de tipo a -> Int, debe ser trivial, es decir, una constante (o indefinida). – mnish