2012-03-01 8 views
6

Zawsze pisałam lista produkujących funkcji rekurencyjnych w tym formacie:Haskell Lista Łączenie vs (głowa tail) format

recursiveFunc :: [a] -> [b] 
recursiveFunc (x:xs) = [change x] ++ resursiveFunc xs where 
    change :: a -> b 
    change x = ... 

Zdaję sobie sprawę dowolnej funkcji jak wyżej może być napisany w przypadku a -> b a następnie po prostu map ed przez zestaw [a], ale proszę wziąć tę sytuację rozwodnioną jako przykład.

HLint sugeruje zastąpienie [change x] ++ recursiveFunc xs z change x : recursiveFunc xs.

Czy ta sugestia jest czysto estetyczna, czy ma jakiś wpływ na to, w jaki sposób Haskell wykonuje tę funkcję?

+4

Cóż, z pierwszym argumentem pojedynczego elementu, '++' * będzie * po prostu 'cons' raz, a następnie zakończy działanie, ale jest to bardzo okrężny sposób na dodanie pojedynczej wartości. – delnan

+2

Twoja wersja ma tę zaletę, że jeśli w jakimś momencie zdecydujesz się zmodyfikować swoją funkcję w taki sposób, że do listy zostanie dodany więcej niż jeden element, będzie mniej do zmiany. –

+0

Propozycje hlint nie zmieniają wyniku wyrażenia. –

Odpowiedz

9

Podczas korzystania z [change x] ++ recursiveFunc xs tworzysz zbędną listę jednoelementową, która jest następnie rozbierana przez funkcję ++. Korzystanie z : nie ma miejsca.

Plus ++ to funkcja, która następnie użyje konstruktora :. Podczas bezpośredniego korzystania z : nie ma potrzeby wywoływania funkcji.

Używanie : jest bardziej efektywne w teorii (kompilator może jednak zoptymalizować te różnice).

4

Podstawową zaletą jest to, że change x : blah jest jaśniejsze - (:) jest dokładnie tym, co próbujesz zrobić (dodać jeden element do listy). Po co wywoływać dwie funkcje, gdy się to stanie?

Sugerowany sposób jest również nieznacznie bardziej efektywny - na swój sposób tworzy listę 1-elementową, a następnie wyrzuca ją i zastępuje innym linkiem do listy. W przeciwnym razie tworzy się tylko jeden link do listy. To powiedziawszy, różnica jest na tyle mała, że ​​nie ma znaczenia w 99% przypadków.

4

Linia

recursiveFunc (x:xs) = [change x] ++ resursiveFunc xs 

jest nieco mylące, ponieważ nie jest w normalform. Oznacza to, że ++ można zastąpić bezpośrednio z jednym prawej stronie jego definicja:

(++) (a:as) bs = a:((++) as bs) 
(++) [] bs = bs 

-- => 

recursiveFunc (x:xs) = (change x):((++) [] resursiveFunc xs) 

-- => 

recursiveFunc (x:xs) = (change x):(resursiveFunc xs) 

Haskell kompilator automatycznie stosuje taką transformację.

Ale ponieważ jest to tak banalne, utrudnia odczytanie kodu. Jeden patrzy na to i pyta "czekaj ... co?"