简单字符串生成中的空间泄漏。为什么?

时间:2013-10-14 07:29:31

标签: haskell

-- generates names in the following order
-- a, b, c ... z, aa, ba, ca, ... za, ab, bb, cb ...
nextName :: String -> String
nextName [] = "a"
nextName (x:xs) = if x == 'z' then 'a' : nextName xs else succ x : xs

-- verify if the number of names generated is as expected.
countNames :: String -> String -> Int
countNames start end = loop 1 start
    where
        loop acc next =
            if next == end then
                acc
            else
                loop (acc + 1) (nextName next)

在ghci中运行countNames "a" "zzzzzz"

在我的com上运行它会占用整个内存并花费大量时间来完成。

如果有人说明空间泄漏发生的地点和原因,请表示赞赏吗?

2 个答案:

答案 0 :(得分:8)

问题是一个大的计数器thunk因为计数器acc上的循环不严格。通常的解决方案是使用seqBangPatterns来严格限制。以下是使用BangPatterns的解决方案。

{-# LANGUAGE BangPatterns #-}
-- generates names in the following order
-- a, b, c ... z, aa, ba, ca, ... za, ab, bb, cb ...
nextName :: String -> String
nextName [] = "a"
nextName (x:xs) = if x == 'z' then 'a' : nextName xs else succ x : xs

-- verify if the number of names generated is as expected.

countNames :: String -> String -> Int
countNames start end = loop 1 start
    where
        loop !acc next =
            if next == end then
                acc
            else
                loop (acc + 1) (nextName next)

答案 1 :(得分:5)

虽然使用严格的评估修复了您的问题,但我建议您重复使用标准函数来计算间隔长度:

countNames :: String -> String -> Int
countNames start end = (+) 1 . length . takeWhile (/= end) $ iterate nextName start

说明:

  • iterate生成nextName[start, nextname start, nextname (nextName start), ...];
  • 的无限列表
  • takeWhile (/= end)保留列表元素,直到达到预期值(不包括上限);
  • 然后你拿length并加1。