Functional programming N-Queens示例程序奇怪输出

Functional programming N-Queens示例程序奇怪输出,functional-programming,n-queens,clean-language,Functional Programming,N Queens,Clean Language,我尝试了squen.icl示例中的代码。当我尝试使用BoardSize:==11时,没有问题。但是当我将其更改为12时,输出是[。为什么?如何修复 module squeen import StdEnv BoardSize :== 12 Queens::Int [Int] [[Int]] -> [[Int]] Queens row board boards | row>BoardSize = [board : boards] | otherwise =

我尝试了
squen.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 [] []

这是因为堆上的空间不足。默认情况下,干净程序的堆设置为2M。当然,您可以更改此设置。从命令行使用
clm
时,可以将
-h4m
添加到其命令行或干净程序本身的命令行。如果您使用的是干净IDE,则可以更改堆大小通过项目选项、应用程序确定

仍被打印(这是我得到的,而不是
[
)的原因如下。一个干净的程序将输出尽可能多的输出,而不是等到知道整个输出。这意味着,例如,一个简单的行,如
Start=[0..]
将向终端发送垃圾邮件,而不是等到整个无限列表在内存中时再打印。在
squen.icl
的情况下,Clean看到Start的结果将是一个元组,因此直接打印开始大括号。但是,在尝试计算元组的元素时(
length solutions
hd 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
call时,列表的部分被垃圾收集(在迭代前100个元素后,它们可以被丢弃,从而减少堆的使用),调用
hd
确保列表的第一个元素不会被丢弃。如果第一个元素没有被丢弃,那么第二个元素和第三个元素都不能被丢弃,等等。因此,整个列表都保存在内存中,而这实际上是不必要的。翻转
长度
hd
调用可以解决此问题:

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

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

它是一个(’,事实上。谢谢。@sama我在这个答案中发现了一个不准确的地方。这并不是严格程度分析器不能识别,只是在
hd
之前对
length
进行了评估。翻转这两个选项可以解决这个问题。我在更新的答案中对此进行了详细说明。