2012-02-17 15 views
11

łapię się często pisania kodu następującego wzoru:Jak pozwolić funkcji [a] -> [a] działać na [(a, Int)]?

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

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

Teraz chcę streszczenie tego z dala

foo = indexesOf (filter (<10)) 

bar = indexesOf sort 

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

Jak wykonać magick?

+0

... Ja naprawdę nie wiadomo, co ty próbuję to zrobić. –

+0

Mam funkcję działającą na liście, powiedz "sortuj". Teraz zamiast posortowanej listy chcę odzyskać * indeksy * elementów. Na przykład. dla '[5.0, 8.0, 7.0]' Nie chcę '[5.0. 7.0, 8.0] 'ale' [0,2,1] '. – Landei

Odpowiedz

11

Podpis typu nie działa. Musisz dać przekazanej funkcji listę krotek, co oznacza, że ​​musisz albo użyć typów wyższego rzędu, aby wymusić to polimorficzne, albo wyraźnie wspomnieć krotki w swoim sygnaturze typu.

Bez tego nie można "zajrzeć do środka" funkcji, aby zobaczyć, jak zmienia ona układ elementów listy. W rzeczywistości, biorąc pod uwagę twój typ podpisu przekazana funkcja mogłaby zrobić wszystko, co chciała na liście, włączając wstawianie elementów, których nawet tam nie było!

Oto co mam do pracy przy użyciu typów wyższej rank:

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

Zauważ, że miałem też dodać dodatkowy argument do funkcji przekazywane, aby poinformować go, jak wyodrębnić część Dba o od elementy listy, nad którą pracuje. Bez tego można by używać tylko funkcji, które nie sprawdzają elementów listy, takich jak reverse, a to nie byłoby zbyt użyteczne.

Przykład uruchomić w 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

Myślę, że odpowiadasz na pytanie w tytule. –

+0

(kontynuacja) ... ale w podanych przykładach funkcja faktycznie jest ':: (Ord a) => [a] -> [a]'. Możesz więc "zajrzeć do środka". –

+0

@RafaelCaetano: Nadal musisz mieć możliwość wyboru innego "a", albo poprzez typ wyższej rangi, albo przez wyraźne wymienienie krotek w sygnaturze typu. Możesz_przypisać podpis typu 'indexOf :: (forall b. Ord b => [b] -> [b]) -> [a] -> [Int]', ale działałoby to tylko dla przykładu 'sort' , a nie ten z 'filtrem', ponieważ funkcja może tylko porównywać elementy ze sobą. Nie można porównać z "10". – hammar

4

Dobre pytanie, ale podejrzewam, że nie istnieje taka funkcja. Zobacz Theorems For Free!. Jak mówi młotek, musisz przekazać funkcje, które jawnie przyjmują krotki.

Oto mój nieco uproszczona wersja, która nie wymaga RankNTypes (co jest, co prawda, nie jest to bardzo dobry postęp w stosunku do oryginalnego kodu):

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

Jest to poprawne, chociaż typy wyższej rangi dają ci nieco silniejsze gwarancje, ponieważ funkcja nie może zepsuć liczb całkowitych. Wszystko, co można zrobić, to zmienić kolejność, skopiować i usunąć istniejące elementy z listy. – hammar

+0

To prawda. Ponadto, jeśli mamy funkcję typu a -> Int, to musi być trywialna, czyli stała (lub niezdefiniowana). – mnish

Powiązane problemy