使用Haskell排序函数时堆栈溢出

时间:2012-08-30 18:40:29

标签: haskell

我在Haskell中实现了一个过滤器,即我可以从命令行调用的程序如下:

$ cat inputfile.txt | myFilter > outputfile.txt

在大约80 MB的文件上运行程序时,我得到堆栈溢出 (堆栈空间溢出:当前大小为8388608字节。)。我在cygwin下使用GHC版本6.12.3。

我认为问题来自于我在程序中使用的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 MB的文件上运行此程序会导致堆栈溢出。

如果我用以下函数替换sort函数(一个简单的快速排序实现):

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]

即。我用<替换<=,堆栈溢出再次出现!

所以我不知道这里发生了什么。 我看不出我已经引入了任何无限递归。我的另一个假设是,懒惰的评估可以在这里发挥作用(<=产生比<更大的thunk吗?)。

我有一些Haskell的经验,但我不是真正的专家所以我很乐意得到一些有用的提示,因为我过去三天一直在努力理解这一点。

2 个答案:

答案 0 :(得分:23)

罪魁祸首是

instance Ord Event where
    x < y = ...

这是定义Ord实例的错误方法。 Ord实例的最小完整定义定义了compare(<=)中的一个。根据{{​​1}}和compare所有(<=)成员函数,Ord的默认定义均为compare。因此,如果您定义(<),这是您可以使用的唯一Ord成员,则所有其他成员在调用时将无限循环,因为他们调用compare,调用(<=),调用compare Data.List.sort ...

compare函数使用(<)来确定顺序,因此它会在第一次比较时循环。您的自定义快速排序仅使用{{1}},因此可以使用。

答案 1 :(得分:1)

在尝试分析代码并获得快速结果之前 - 尝试增加堆栈大小。

使用启用RTS编译程序并指定所需的堆栈大小。

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