У меня есть некоторый опыт написания небольших инструментов на Haskell, и я нахожу его очень интуитивно понятным, особенно для написания фильтров (использующих interact
), которые обрабатывают их стандартный ввод и передают его на стандартный вывод.
Недавно я попытался использовать один такой фильтр для файла, который был примерно в 10 раз больше обычного, и я получил Stack space overflow
ошибку.
После некоторого чтения (например, здесь и здесь ) я определил два руководства по экономии места в стеке (опытные Хаскелеры, пожалуйста, исправьте меня, если я напишу что-то не то):
- Избегайте рекурсивных вызовов функций, которые не являются хвостово-рекурсивными (это справедливо для всех функциональных языков, которые поддерживают оптимизацию хвостовых вызовов).
- Введение
seq
для принудительной ранней оценки подвыражений, чтобы выражения не становились слишком большими, прежде чем их уменьшать (это характерно для Haskell или, по крайней мере, для языков, использующих ленивую оценку).
После введения пяти или шести seq
вызовов в моем коде мой инструмент снова работает гладко (также для больших данных). Тем не менее, я считаю, что оригинальный код был немного более читабельным.
Так как я не опытный программист на Haskell, я хотел спросить, является ли введение seq
таким способом обычной практикой, и как часто это можно увидеть seq
в рабочем коде Haskell. Или есть какие-то методы, которые позволяют избегать seq
слишком частого использования и при этом все еще использовать мало стекового пространства?
Ответы:
К сожалению, есть случаи, когда приходится использовать
seq
эффективную / хорошо работающую программу для больших данных. Поэтому во многих случаях вы не можете обойтись без этого в производственном коде. Вы можете найти больше информации в Real World Haskell, Глава 25. Профилирование и оптимизация .Тем не менее, есть возможности, как избежать использования
seq
напрямую. Это может сделать код чище и надежнее. Некоторые идеи:interact
. Известно, что у Lazy IO есть проблемы с управлением ресурсами (не только памятью), и итераторы созданы именно для того, чтобы преодолеть это. (Я бы посоветовал избегать ленивого ввода-вывода вообще, независимо от того, насколько велики ваши данные - см . Проблему с ленивым вводом-выводом .)seq
непосредственного использования (или разработки собственных) комбинаторов, таких как foldl ' или foldr' или строгих версий библиотек (таких как Data.Map.Strict или Control.Monad.State.Strict ), которые предназначены для строгих вычислений.seq
на строгое сопоставление с образцом. Объявление строгих полей конструктора также может быть полезно в некоторых случаях.rseq
) или full NF (rdeepseq
). Существует множество полезных методов для работы с коллекциями, комбинирования стратегий и т. Д.источник
ByteString
.