Переполнение стека при использовании функции сортировки Haskell

Я реализую фильтр в Haskell, то есть программу, которую я могу вызвать из командной строки следующим образом:

$ cat inputfile.txt | myFilter > outputfile.txt

При запуске программы в файле размером около 80 МБ я получаю переполнение стека (Переполнение пространства стека: текущий размер 8388608 байт.). Я использую GHC Version 6.12.3 под cygwin.

Я думаю, проблема связана с функцией sort, которую я использую в программе, но после того, как я искал проблему в течение трех дней, я не знаю, как это решить, поэтому я хотел бы, чтобы кто-то мог дать мне подсказка.

Вот основные сведения о моей программе.

Моя программа фильтрации считывает стандартный ввод в строку, разбивает ее на строки и анализирует каждую строку в записи какого-либо типа Event

data Event = ...

который является экземпляром Ord

instance Ord Event where
    x < y = ...

чтобы я мог сортировать события, используя встроенную функцию sort.

Разделение на строки и разбор событий (по одному событию на строку) выполняется функцией

p :: String -> [Event]

который внутренне использует стандартную функцию lines.

У меня также есть функция g, которая группирует события:

g :: [Event] -> [[Event]]

g использует некоторые критерии, которые здесь не актуальны; каждая группа может содержать не более 4 событий.

Я сортирую каждую группу событий (представленную как список) с помощью sort (т.е. все события внутри каждой группы сортируются) и, наконец, отформатирует все группы событий как строку с помощью функции

f :: [[Event]] -> String

Основная функция выглядит следующим образом:

main = interact (f . (map sort) . g . p)

Как сказано, запуск этой программы в файле размером около 80 МБ приводит к переполнению стека.

Если я заменил функцию сортировки следующей функцией (наивная быстрая сортировка):

mySort :: [Event] -> [Event]
mySort [] = []
mySort (e:es) = let h = [j | j <- es, j < e]
                    t = [j | j <- es, e < j]
                in
                  (mySort h) ++ [e] ++ (mySort t)


main = interact (f . (map mySort) . g . p)

У меня нет!

Если в функции mySort я заменю определение t следующим образом:

                    t = [j | j <- es, e <= j]

то есть. Я заменяю < на <=, переполнение стека снова!

Итак, я не знаю, что здесь происходит. Я не вижу, что я ввел бесконечную рекурсию. Моя другая гипотеза заключается в том, что здесь может играть роль ленивая оценка (<= производит более сильный удар, чем <?).

У меня есть некоторый опыт работы с Haskell, но я не настоящий эксперт, поэтому я был бы рад получить полезный намек, потому что я изо всех сил пытался понять это в течение последних трех дней.

Ответ 1

Преступник

instance Ord Event where
    x < y = ...

что является неправильным способом определения экземпляра Ord. Минимальное полное определение экземпляра Ord определяет один из compare или (<=). Существуют определения по умолчанию compare в терминах (<=) и всех Ord функций-членов в терминах compare. Поэтому, если вы определяете (<), что единственный член Ord, который вы можете использовать, все остальные будут бесконечно циклироваться при вызове, так как они вызывают compare, который вызывает (<=), который вызывает compare...

Функция Data.List.sort использует compare, чтобы определить порядок, поэтому он петлиет при первом сравнении. В вашей пользовательской quicksort используется (<), поэтому это работает.

Ответ 2

Прежде чем пытаться профилировать код и получить быстрый результат - попробуйте увеличить размер стека.

Скомпилируйте свою программу с включением RTS и укажите требуемый размер стека.

http://book.realworldhaskell.org/read/profiling-and-optimization.html