2010-11-21 14 views
6

można przekonwertowaćPunkt wolna forma kontra stylu

-- tupleUnfold :: forall a. ((forall b. a -> b)) -> a -> ((b)) 
tupleUnfold :: Int -> ExpQ 
tupleUnfold n = do 
    xs <- forM [1 .. n] (const . newName $ "x") 
    y <- newName "y" 
    let y' = varE y 
     g (ps', es') x = (varP x : ps', appE (varE x) y' : es') 
     (ps, es) = foldl' g ([], []) xs 
    lamE [tupP ps, varP y] (tupE es) 

do pointfree styl przy zachowaniu jasności (wiem programu „pointfree”, ale wolałby, aby nie zaciemniać kodu nawet więcej)?

Tak czy inaczej, jakie zmiany można wprowadzić, aby poprawić styl funkcji, lub w inny sposób czynią ją bardziej zrozumiałą? Funkcja jest przeznaczona do użycia jak poniżej.

$(tupleUnfold 3) ((+ 1), (+ 2), (+ 3)) 2 
-- (3, 4, 5) 

Jakie są lepsze konwencje nazewnictwa (zobacz zmienne ps, ps ', es i es)?

Odpowiedz

5

Oto, co mam. Wymaga Control.Arrow (&&&) i Control.Applicative (<$>).

Nie można w nim skrócić znacznie, nie czyniąc go całkowicie niezrozumiałym.

EDIT Chociaż nie wskazują darmo, tutaj jest najwyraźniejszy mogłem zrobić. Potrzebuje Data.Function (on)

tupleUnfold :: Int -> ExpQ 
tupleUnfold n = do 
    y <- newName "y" 
    xs <- replicateM n (newName "x") 
    let exps = tupE $ zipWith appVars xs (repeat y) 
     pats = tupP $ map varP xs 
    lamE [pats, varP y] exps 
    where 
    appVars = appE `on` varE 
+0

Podoba mi się użycie "replicateM" - nie wiem, jak bardzo tęskniłem za tym – ScootyPuff

+0

Zaznaczam to jako poprawną odpowiedź nieco arbitralnie. Chociaż ten drugi jest bezsporny na najwyższym poziomie, ten wydaje się lepiej reprezentować przepływ i transformację - choć nieco trudniej go odczytać. – ScootyPuff

+0

Jednym z powodów spasowania (zamiast mapy) było uniknięcie wielokrotnych przejazdów. Wiem też, że GHC jest raczej dobry w przepuszczaniu list. Czy to zwykle znacząca różnica? Biorąc pod uwagę, że obie formy są stosunkowo łatwe do napisania, co powinno być (z reguły korzystne)? – ScootyPuff

0

Osobiście myślę, że to już całkiem jasne, ale jak o tym:

tupleUnfold :: Int -> ExpQ 
tupleUnfold = mapM (const . newName $ "x") . enumFromTo 1 >=> \xs -> do 
    y <- newName "y" 
    let y' = varE y 
     g (ps', es') x = (varP x : ps', appE (varE x) y' : es') 
     f ps = lamE [tupP ps, varP y] . tupE 
    uncurry f $ foldl' g ([],[]) xs 

Kleisli skład operator >=> (od Control.Monad) jest przydatna do tworzenia pointfree funkcje monadycznego.

+0

miałem wersji, która była w pointfree najwyższego poziomu (z użyciem> =>), ale ostatecznie musiałem go rozbić. Dobrze wiedzieć, że jest względnie przejrzysty - większość z tego, co mnie w Haskell mówi, to to, na ile sposobów można coś przepisać. – ScootyPuff

+0

Jak ważne są uwolnienia wiązań, jeśli chodzi o wydajność? To głównie dlatego, że mam "- nie byłem pewien, czy wyrażenie zostanie przyciągnięte do zewnętrznego zakresu. – ScootyPuff

+0

Tak, bez obliczeń "varE y'" nie będą udostępniane. Ale te obliczenia prawdopodobnie po prostu przekazują się do konstruktora, więc nie ma to znaczenia (i nie martw się o dwa połączenia z konstruktorem a jednym, to po prostu pozwala, aby twój język cię posiadał - nie dostaniesz szybszego kodu, ty " Po prostu dostanę brzydki kod). Prime w foldl 'jest bezużyteczne, ponieważ jest to normalna (leniwa) krotka. Użyj ścisłej krotki 'dane Tuple a b = Tuple! A! B'. Ale ponieważ jest to kod podobny do strumienia, użyj zamiast niego 'foldr' - używa on asymptotycznie mniej pamięci. Zasadniczo oznacza to "unzip". map' jak w mojej odpowiedzi. – luqui

1

trochę bardziej niezrozumiałe (spróbuj czytać od prawej do lewej):

tupleUnfold n = do 
    y <- newName "y" 
    uncurry lamE . ((:[varP y]) . tupP *** tupE) . unzip . 
    map (varP &&& (`appE` varE y) . varE) <$> replicateM n (newName "x") 

EDIT:
mieszankę strzałkami i złożenie funkcji do przetwarzania

tupleUnfold n = do 
    y <- newName "y" 
    uncurry lamE . ((tupP >>> (:[varP y])) *** tupE) . unzip . 
    map (varP &&& (varE >>> (`appE` varE y))) <$> replicateM n (newName "x") 

i za pomocą głównie strzałek (funkcja przetwarzania odczytu od lewej do prawej t)

tupleUnfold n = do 
    y <- newName "y" 
    (map (varP &&& (varE >>> (`appE` varE y))) >>> unzip >>> 
    ((tupP >>> (:[varP y])) *** tupE) >>> uncurry lamE) <$> replicateM n (newName "x") 

zauważyć, że funkcja strzałka (>>>) odpowiada klapki (.)