Mam pewne doświadczenie w pisaniu małych narzędzi w Haskell i uważam, że korzystanie z nich jest bardzo intuicyjne, szczególnie w przypadku pisania filtrów (używania interact
), które przetwarzają standardowe wejście i przesyłają je do standardowego wyjścia.
Ostatnio próbowałem użyć jednego takiego filtru w pliku, który był około 10 razy większy niż zwykle i dostałem Stack space overflow
błąd.
Po przeczytaniu (np. Tutaj i tutaj ) zidentyfikowałem dwie wskazówki, aby zaoszczędzić miejsce na stosie (doświadczeni Haskellerzy, poprawcie mnie, jeśli napiszę coś, co jest nieprawidłowe):
- Unikaj rekurencyjnych wywołań funkcji, które nie są rekurencyjne (dotyczy to wszystkich języków funkcjonalnych, które obsługują optymalizację wywołania ogona).
- Wprowadź,
seq
aby wymusić wczesną ocenę podwyrażeń, aby wyrażenia nie rosły zbyt duże, zanim zostaną zmniejszone (jest to specyficzne dla Haskell lub przynajmniej języków używających leniwej oceny).
Po wprowadzeniu pięciu lub sześciu seq
wywołań w moim kodzie moje narzędzie ponownie działa płynnie (także w przypadku większych danych). Uważam jednak, że oryginalny kod był nieco bardziej czytelny.
Ponieważ nie jestem doświadczonym programistą Haskell, chciałem zapytać, czy wprowadzenie seq
w ten sposób jest powszechną praktyką i jak często można to zobaczyć seq
w kodzie produkcyjnym Haskell. Czy są jakieś techniki, które pozwalają uniknąć seq
zbyt częstego używania i nadal zajmują mało miejsca na stosie?
Odpowiedzi:
Niestety zdarzają się przypadki, gdy trzeba użyć
seq
, aby uzyskać wydajny / dobrze działający program do dużych danych. W wielu przypadkach nie można tego zrobić w kodzie produkcyjnym. Więcej informacji można znaleźć w rozdziale Real World Haskell, Rozdział 25. Profilowanie i optymalizacja .Istnieją jednak możliwości uniknięcia
seq
bezpośredniego używania . Może to uczynić kod czystszym i bardziej niezawodnym. Jakieś pomysły:interact
. Leniwe IO ma problemy z zarządzaniem zasobami (nie tylko pamięcią), a iteraty są zaprojektowane tak, aby to rozwiązać. (Sugeruję unikanie leniwego We / Wy niezależnie od tego, jak duże są twoje dane - zobacz Problem z leniwymi We / Wy .)seq
bezpośrednio używać (lub projektować własne) kombinatory, takie jak foldl ' lub foldr' lub ścisłe wersje bibliotek (takich jak Data.Map.Strict lub Control.Monad.State.Strict ), które są przeznaczone do ścisłych obliczeń.seq
ścisłym dopasowaniem wzorca. W niektórych przypadkach przydatne może być również zadeklarowanie ścisłych pól konstruktora .rseq
) lub pełnej NF (rdeepseq
). Istnieje wiele użytecznych metod pracy z kolekcjami, łączenia strategii itp.źródło
ByteString
.