【问题标题】:Filtering infinite list causing memory overload in Haskell/GHCI在 Haskell/GHCI 中过滤导致内存过载的无限列表
【发布时间】:2015-03-10 12:04:02
【问题描述】:

我正在尝试编写一些 Haskell 代码,它会吐出一堆有效的数独谜题。这是我到目前为止的代码:

import Data.List (nub, permutations, transpose)

-- Recursively build list of possible permutations of a certain length, allowing duplicates
genPermutations list length
    | length <= 0 = [[]]
    | length == 1 = [[a] | a <- list]
    | otherwise = [[a]++b | a <- list, b <- genPermutations list $ length - 1]

-- Generate as flat list of length 9, then format
squares = [[take 3 a,take 3 $ drop 3 a, drop 6 a] | a <- permutations [1..9]]
sudokus = [[take 3 a,take 3 $ drop 3 a, drop 6 a] | a <- genPermutations squares 9]

-- Takes a sudoku as a 4d array, return True/Flase based on rules of sudoku
-- Does not check for duplicates within a square because generated sudokus shouldn't have any
checkSudukoValid x = (foldr (==) True $ map screenLineForDuplicates x) && (foldr (==) True $ map screenLineForDuplicates $ transposeSudoku x)
    where transposeSudoku x = transpose(map (\x -> map transpose x )  x)
          screenLineForDuplicates [[],[],[]] = True
          screenLineForDuplicates [a:al,b:bl,c:cl] = check && screenLineForDuplicates [al,bl,cl]
              where check = (length line)  == (length $ nub line)
                    line = concat [a,b,c]


-- Known good sudoku for testing
knownGood = [[[[9,8,3],[6,1,4],[5,2,7]],[[6,5,7],[2,8,9],[4,3,1]],[[2,4,1],[5,7,3],[9,6,8]]],[[[8,6,5],[4,3,1],[7,9,2]],[[3,2,4],[7,9,8],[1,6,5]],[[7,1,9],[6,5,2],[3,8,4]]],[[[2,7,8],[3,5,9],[1,4,6]],[[5,1,3],[8,4,6],[9,7,2]],[[4,9,6],[1,2,7],[8,3,5]]]]

此代码的重要部分是它生成一个可能有效的数独谜题列表以及一个方法,如果单个谜题是有效的。根据我的理解,我应该能够过滤所述列表以获得一些有效的数独: head $ filter checkSudukoValid sudokus

当我运行它时,GHCI 会终止我的进程,这似乎是因为内存问题。我不明白为什么我会遇到内存问题。 haskell 不应该一次懒惰地过滤列表中的项目吗?为什么这会比 filter checkSudukoValid $ take 5 sudokus 占用更多的内存

关于 Haskell 如何处理会导致这种情况的无限列表,我缺少什么?是否有一个标准的解决方案可以让这个更懒惰,让我不会遇到内存问题?

【问题讨论】:

  • 你能告诉我们你收到的错误吗?
  • 可能是 checkSudokuValid 需要很长时间。对head $ filter checkSudokusValid $ take n sudokus 进行一些实验,在其中增加n,直到找到内存不足的位置。看看你是否可以在sudokus 中找到导致checkSudokusValid 内存不足的元素。
  • 我怀疑这与它有很大关系,但是foldr (==) True 是一个非常奇怪的写法。也许你的意思是and,也就是foldr (&amp;&amp;) True,或者你的意思是(\xs -&gt; and $ zipWith (==) xs (tail xs))
  • @MrGlass,不,折叠(==) 不会做你认为它会做的事情。例如foldr (==) True [a,b] = a == (b == True) = (a &amp;&amp; b) || (not a &amp;&amp; not b) = a == b,然后是foldr (==) True [a,b,c] = a == (b == c) = (a &amp;&amp; b==c) || (not a &amp;&amp; b/=c),你已经有了一些非常奇怪的东西。尝试对foldr (==) True 的实际作用提出一个简单的解释实际上可能会相当有趣,但它肯定不是你在野外可能看到的。
  • @dfeuer (False ==)not(True ==)id。所以foldr (==) True 告诉参数列表中Falses 是偶数还是奇数。

标签: haskell infinite


【解决方案1】:

问题肯定出在生成代码上,而不是检查代码上(尽管两者都有很多东西需要改变)。具体来说,您的 genPermutations 实现似乎使用了越来越多的 RAM。我还没弄清楚为什么会这样,但如果你使用

genPermutations xs n = map (take n) $ permutations xs

内存使用下降到恒定。但是,这不会真正使您的程序正常工作,至少有两个原因。

一个原因是你的有效性测试是错误的;正如我之前提到的,折叠(==) 不会像你想的那样。你的意思是

checkSudukoValid x = (all screenLineForDuplicates x) && (all 
screenLineForDuplicates $ transposeSudoku x)
    where transposeSudoku x = transpose(map (\x -> map transpose x )  x)
          screenLineForDuplicates [[],[],[]] = True
          screenLineForDuplicates [a:al,b:bl,c:cl] = check && screenLineForDuplicates [al,bl,cl]
              where check = (length line)  == (length $ nub line)
                    line = concat [a,b,c]

这是非常低效的,但我认为它可能是正确的。

另一个更严重的原因:使用我的genPermutations 或你的版本,在你遇到第一个有效的谜题之前,你会得到大量的无效谜题。我没有足够的耐心等待它。

【讨论】:

  • genPermutations 的代码会产生很多重复项。此外,排列不允许在排列中具有相同值的多个条目(例如,对于列表 [1..3] 的排列,我希望包含 [1,1,1,1,1]
  • @MrGlass,啊,这很重要,我可以解决它,但你的问题仍然存在。
  • 我一直都知道时间是个问题。这就是为什么我试图获得 1 只是为了证明我的概念,并且对被杀死的过程感到震惊。我很高兴让我的机器在这个过程中运行几天以获得结果。
【解决方案2】:

尝试使用:

genPermutations list length = sequence (replicate length list)

【讨论】:

  • 这似乎跑得快了一点,但还是死了。
猜你喜欢
  • 1970-01-01
  • 2016-05-22
  • 2016-10-18
  • 1970-01-01
  • 2014-11-27
  • 2012-10-05
  • 2013-11-08
  • 1970-01-01
相关资源
最近更新 更多