2017-10-03 21 views
8

Należy uwzględnić następujący kod Haskella.Czy można pisać funkcje dopasowane do wzorca w formie wolnej od punktów?

data Keypress = Keypress Int Char 

getSeq :: Keypress -> [Char] 
getSeq (Keypress i c) = replicate i c 

Czy istnieje jakiś sposób, aby napisać getSeq w formie pointfree?

getSeq „s definicja jest tak podobny do swojego wzorca meczu, który wydaje się, że może być jakiś sposób na wykorzystanie currying lub monad, czy coś, aby uniknąć określania parametrów i i c. Jednak pointfree.io nie rozwiązuje bez punktu wyjścia dla getSeq, myślę, że ze względu na dopasowanie wzorca.

Czy to możliwe?

Odpowiedz

16

Jest to często przydatne, zwłaszcza dla rekurencyjnych typów danych z wielu konstruktorów , aby zdefiniować katamorfizm, sposób na złożenie struktury danych, gdy podano jedną funkcję dla każdego z możliwych konstruktorów.

Na przykład dla Bool catamorphism jest

bool :: a -> a -> Bool -> a 
bool x _ False = x 
bool _ y True = y 

i Either to

either :: (a -> c) -> (b -> c) -> Either a b -> c 
either f g (Left x) = f x 
either f g (Right x) = g x 

Bardziej zaawansowany catamorphism jest jeden na listach, które masz prawdopodobnie widział: foldr!

foldr :: (a -> b -> b) -> b -> [a] -> b 
foldr f init [] = init 
foldr f init (x:xs) = f x (foldr f init xs) 

Zwykle nie myśleć o tym w ten sposób (lub przynajmniej nie mam), ale foldr jest catamorphism: obsługuje wzorzec dopasowywania i rekurencyjnie dekonstrukcji listę dla ciebie, tak długo jak ty zapewnienie „ładowarki” dla wartości znajdujących się w dwóch konstruktorów [a]:

  • Jeden przypadek obsłużyć [], potrzebując żadnych argumentów na wszystkich: tylko wartość typu b
  • jednym przypadku do obsługi (x:xs). Przypadek ten przyjmuje jeden argument reprezentujący x, nagłówek listy i jeden argument reprezentujący wynik złożenia rekurencyjnie rekina, o wartości typu b.

catamorphism jest mniej ekscytujące dla typu z tylko jeden konstruktor, ale możemy łatwo określić dla swojego typu Keypress:

key :: (Int -> Char -> a) -> Keypress -> a 
key f (Keypress x c) = f x c 

W sposób catamorphism pozwala streszczenie dala wzór -matchowanie części definicji funkcji, po czym można po prostu pracować z funkcjami, które nie muszą już bezpośrednio dotykać bazowego typu danych.

Po zdefiniowaniu tej ogólnie użytecznej funkcji jeden raz, możesz użyć jej wiele razy, aby zaimplementować dowolną funkcję bez punktu, której pragnie twoje serce. W twoim przypadku możesz po prostu napisać:

getSeq :: Keypress -> [Char] 
getSeq = key replicate 
5

Nie możesz tego zrobić bez odrobiny szablonu, ale lens może wygenerować dla ciebie ten zestaw, więc prawdopodobnie jest tak blisko, jak to tylko możliwe.

Generowanie Iso generuje Iso, która jest zasadniczo dopasowaniem do pierwszej klasy dla typu danych jednego konstruktora. Dogodnie, Iso s są dwukierunkowe, więc można je view je (aby uzyskać wartości out, takie jak dopasowanie do wzorca) i review je (aby przywrócić wartość, jak zwykle przy użyciu konstruktora).

Korzystanie makePrisms i view, to można napisać getSeq w sposób pointfree:

{-# LANGUAGE TemplateHaskell #-} 
import Control.Lens 

data Keypress = Keypress Int Char 
makePrisms ''Keypress 

getSeq :: Keypress -> [Char] 
getSeq = uncurry replicate . view _Keypress 

Czy to lepiej? Brak pomysłu. Dopasowanie do wzorca w kodzie wygląda dla mnie dobrze, ale jeśli i tak używasz już i tak, ta wersja może być dla ciebie bardziej przyjemna.

12

Jak napisano, nie. Ale można ustalić, że jeśli chcesz:

data Keypress = Keypress 
    { count :: Int 
    , char :: Char } 

Następnie

getSeq p = replicate (count p) (char p) 

i które mogą być zamienione na

getSeq = replicate <$> count <*> char