2012-09-27 7 views
5

Mam trzy funkcje (getRow, getColumn, getBlock) z dwoma argumentami (x i y), z których każdy tworzy listę tego samego typu. Chcę napisać czwartą funkcję, która skleja ich wyjścia:Jak zmapować listę funkcji na wiele argumentów w Haskell?

outputList :: Int -> Int -> [Maybe Int] 
outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock] 

Funkcja działa, ale czy jest jakiś sposób, aby przepisać podwójną mapę (z trzech „$” s) na jednej mapie?

Odpowiedz

22
import Data.Monoid 

outputList :: Int -> Int -> [Maybe Int] 
outputList = mconcat [getRow, getColumn, getBlock] 

Zasługujesz wyjaśnienia.

Po pierwsze, wyraźnie zaznaczę, że wszystkie te funkcje mają ten sam typ.

outputList, getRow, getColumn, getBlock :: Int -> Int -> [Maybe Int] 

Zacznijmy od oryginalnej definicji.

outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock] 

Te funkcje powodują, że [Maybe Int], a lista wszystkiego jest monidoidalna. Monologicznie łączenie list jest takie samo jak łączenie list, więc możemy zastąpić concat przez mconcat.

outputList x y = mconcat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock] 

Inną rzeczą, która jest monoid jest funkcją, jeśli jego wynik jest monoid. Oznacza to, że jeżeli b jest monoidem, wówczas a -> b jest również monoidem. Monologicznie łączenie funkcji jest takie samo, jak wywoływanie funkcji o tym samym parametrze, a następnie łączenie wyników w sposób monoidalny.

Więc możemy uprościć do

outputList x = mconcat $ map ($ x) [getRow,getColumn,getBlock] 

A potem znowu do

outputList = mconcat [getRow,getColumn,getBlock] 

Skończyliśmy!


Typeclassopedia has a section about monoids, choć w tym przypadku nie jestem pewien, że to dodaje, że znacznie poza documentation for Data.Monoid.

4

Jako pierwszy krok obserwujemy, że definicja

outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock] 

może być zapisane za pomocą operatora złożenie funkcji (.) zamiast operatora aplikacji funkcja ($) następująco.

outputList x y = (concat . map ($ y) . map ($ x)) [getRow,getColumn,getBlock] 

Następny zauważamy, że map to inna nazwa dla fmap na listach i spełnia fmap prawa, dlatego w szczególności mamy map (f . g) == map f . map g. Stosujemy to prawo, aby zdefiniować wersję za pomocą pojedynczej aplikacji o nazwie map.

outputList x y = (concat . map (($ y) . ($ x))) [getRow,getColumn,getBlock] 

W końcowym etapie można wymienić kompozycji concat i map przez concatMap.

outputList x y = concatMap (($ y) . ($ x)) [getRow,getColumn,getBlock] 

Wreszcie, moim zdaniem, chociaż programiści Haskell mają tendencję do korzystania z wielu fantazyjnych operatorów, to nie jest wstyd, aby zdefiniować funkcję

outputList x y = concatMap (\f -> f x y) [getRow,getColumn,getBlock] 

ponieważ jasno wyraża, co robi funkcja. Jednak używanie abstrakcji klas typu (jak pokazano w innej odpowiedzi) może być dobrą rzeczą, ponieważ możesz zauważyć, że twój problem ma pewną abstrakcyjną strukturę i zyskuje nowe spostrzeżenia.

2

Chciałbym iść z odpowiedzią @ dave4420, ponieważ jest to najbardziej zwięzłe i wyraża dokładnie to, co masz na myśli. Jednakże, jeśli nie chcesz polegać na Data.Monoid następnie można przepisać następująco

kod oryginalny:

outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock] 

Bezpiecznik dwie mapy:

outputList x y = concat . map (($y) . ($x)) [getRow,getColumn,getBlock] 

Zamień concat . map z concatMap:

outputList x y = concatMap (($y) . ($x)) [getRow,getColumn,getBlock] 

I gotowe.

Edytuj: aaaa i to jest dokładnie to samo, co odpowiedź @Jana Christiansena. No cóż!

+0

Nawiasem mówiąc, są jakieś statyki jak długo to trwa, dopóki pytanie dotyczące Haskell odpowiedzi? Mam wrażenie, że prawie wszystkie pytania Haskella odpowiadają na 90 procent niemal natychmiast. Chociaż nie mówi to nic o jakości odpowiedzi, moim zdaniem mają one również dość wysoką jakość. –

+2

@JanChristiansen: Możesz odpowiedzieć na to za pomocą eksploratora danych wymiany stosów, jeśli chcesz. Zrobiłem bardzo przybliżone przybliżenie (filtrowanie oczywistych wartości odstających, ignorowanie samo-odpowiedzi, i c.), A typowy (tj. Mediana) czas wynosił około 20 minut do momentu opublikowania pierwszej odpowiedzi. –