2012-11-17 6 views
21

mam następujące funkcje w HaskellJak mogę ponownie napisać funkcję Haskell z dwóch argumentów punkt-free style

agreeLen :: (Eq a) => [a] -> [a] -> Int 
agreeLen x y = length $ takeWhile (\(a,b) -> a == b) (zip x y) 

Próbuję nauczyć się pisać „idiomatyczne” Haskell, które wydają zamiast nawiasów preferowane jest używanie . i $, a także, w miarę możliwości, preferowanie kodu bezbłędnego. Po prostu nie mogę pozbyć się wyraźnego wymieniania x i y. Jakieś pomysły?

Myślę, że miałbym ten sam problem z wyrażeniem dowolnej funkcji dwóch argumentów.

BTW, to tylko w dążeniu do pisania dobrego kodu; nie niektóre "wykorzystują wszystko, aby uczynić go bezwzrokowym" ćwiczenia domowe.

Dzięki.


(Dodano komentarz) Dzięki za odpowiedzi. Przekonałeś mnie, że ta funkcja nie korzysta z systemu pointfree. Dałeś mi także kilka świetnych przykładów na ćwiczenie transformacji ekspresji. Jest to nadal trudne dla mnie, i wydają się być niezbędne do Haskell jako wskaźniki są na C

+2

Uważam, że hlint (http://community.haskell.org/~ndm/darcs/hlint/hlint.htm) jest bardzo cenny, pomagając mi nauczyć się innych sposobów pisania wyrażeń, w tym używania. i $. – mhwombat

Odpowiedz

41

, a także, w miarę możliwości, preferować kod bezpunktowy.

Nie "tam, gdzie to możliwe", ale "tam, gdzie poprawia czytelność (lub ma inne oczywiste zalety)".

Point-Free Your

agreeLen x y = length $ takeWhile (\(a,b) -> a == b) (zip x y) 

Pierwszym krokiem byłoby przenieść prawo ($) i zastąpić jeden masz z (.):

agreeLen x y = length . takeWhile (\(a,b) -> a == b) $ zip x y 

Teraz można go przenieść jeszcze dalej po prawej:

agreeLen x y = length . takeWhile (uncurry (==)) . zip x $ y 

i tam możesz od razu siekać od jednego argumentu

agreeLen x = length . takeWhile (uncurry (==)) . zip x 

Następnie można przepisać że jako stosowania prefiksu operatora kompozycji,

agreeLen x = (.) (length . takeWhile (uncurry (==))) (zip x) 

, którą można

f (gx)

jak

f . g $ x 

ogólnie tutaj z

f = (.) (length . takeWhile (uncurry (==))) 

i g = zip, dając

agreeLen x = ((.) (length . takeWhile (uncurry (==)))) . zip $ x 

z którego argument x jest łatwo usunąć. Następnie można przekształcić aplikację prefiksu (.) na sekcję i uzyskać

agreeLen = ((length . takeWhile (uncurry (==))) .) . zip 

jednak, że jest mniej czytelny niż oryginał, więc nie polecam to robić z wyjątkiem uprawiania przekształcenia wyrażeń w punkcie wolne styl.

+9

Naprawdę niezwykłe jest to, że facet o nazwisku Schönfinkel wynalazł tę technikę w 1920 roku. –

11

Można również użyć:

agreeLen :: (Eq a) => [a] -> [a] -> Int 
agreeLen x y = length $ takeWhile id $ zipWith (==) x y 

Idiomatic Haskell jest to, co jest łatwiejsze do odczytania, niekoniecznie to, co jest najbardziej bez punktów.

+0

Używanie zipWith wygląda o wiele ładniejszego. Ale moja wersja "łatwiejszego czytania" bardzo różni się od większości pisarzy Haskella, więc staram się przenieść moją wersję bliżej ich. – JonathanZ

+3

@ Jonathan Cóż, nawet społeczność ma różne opinie na temat stylu, ale mogę powiedzieć, że moja własna konwencja polega na używaniu '.', gdy masz łańcuchy funkcji jednego argumentu i jeśli używasz dwóch lub więcej argumentów po prostu robisz są one wyraźne. Jeśli chodzi o '$', używasz go głównie do usuwania zwisających nawiasów na końcu bloku 'do', na przykład' foo $ do ... ', lub do posprzątania nawiasów wielorzędowych. –

Powiązane problemy