N-Queens示例程序奇怪的输出

时间:2016-03-24 03:42:48

标签: functional-programming n-queens clean-language

我尝试了squeen.icl示例中的代码。当我用BoardSize :== 11尝试时,没有问题。但是当我将其更改为12时,输出为[。为什么?如何解决?

module squeen
import StdEnv

BoardSize :== 12

Queens::Int [Int] [[Int]] -> [[Int]]
Queens row board boards
    | row>BoardSize =  [board : boards]
    | otherwise     =  TryCols BoardSize row board boards

TryCols::Int Int [Int] [[Int]] -> [[Int]]
TryCols 0 row board boards =  boards
TryCols col row board boards
    | Save col 1 board  =   TryCols (col-1) row board queens
    | otherwise         =   TryCols (col-1) row board boards
where   queens  = Queens (row+1) [col : board] boards

Save::!Int !Int [Int] -> Bool
Save  c1 rdiff [] =  True
Save  c1 rdiff [c2:cols]
    | cdiff==0 || cdiff==rdiff || cdiff==0-rdiff    =   False
    | otherwise                                     =   Save c1 (rdiff+1) cols
where   cdiff   = c1 - c2


Start::(Int,[Int])
Start   =   (length solutions, hd solutions)
where   solutions   = Queens 1 [] []

1 个答案:

答案 0 :(得分:2)

这是因为堆上的空间不足。默认情况下,Clean程序的堆设置为2M。当然,你可以改变它。从命令行使用clm时,可以将-h 4M添加到其命令行或clean程序本身的命令行。如果您使用的是Clean IDE,则可以通过“项目选项”,“应用程序”更改堆大小。

仍然打印(的原因(我得到的,而不是[)如下。 Clean程序将输出尽可能多的输出,而不是等到整个输出都知道。这意味着,例如,一个简单的行Start = [0..]将垃圾邮件终端,而不是等到整个无限列表在内存中然后打印它。在squeen.icl的情况下,Clean看到Start的结果将是一个元组,因此直接打印左大括号。但是,在尝试计算元组(length solutionshd solutions)的元素时,堆会填满,从而使程序终止。

我不知道在Windows上获得完整堆时的样子,但在Linux(/ Mac)上,它看起来像这样:

$ clm squeen -o squeen && ./squeen -h 2M
Linking squeen
Heap full.
Execution: 0.13  Garbage collection: 0.03  Total: 0.16
($

请注意,元组左括号位于最后一行。因此,使用终端时很容易发现此错误。

有趣的是,由于length利用尾递归,因此即使使用小堆也可以计算元组的第一个元素(您可以通过用[]替换第二个元素来尝试这一点)。此外,元组的第二个元素可以在一个小堆上计算(用0替换第一个元素。)

关键是长度是在头部之前计算的,因为它必须首先打印。使用正常的length调用时,列表中的部分是垃圾收集的(在迭代前100个元素之后,它们可以被丢弃,允许更小的堆使用),hd调用确保第一个列表的元素被丢弃。如果第一个元素没有被丢弃,那么第二个元素就不会被丢弃,第三个元素也不会被丢弃。因此,整个列表都保存在内存中,而这实际上并不是必需的。翻转lengthhd来电可以解决问题:

Start :: ([Int], Int)
Start = (hd solutions, length solutions)
where solutions = Queens 1 [] []

现在,在调用hd之后,没有理由将整个列表保留在内存中,因此length可以丢弃它迭代过的元素,并且堆不会填满。