简体   繁体   中英

N-Queens example program strange output

I try the code from the squeen.icl example. When I try it with BoardSize :== 11 , there is no problem. But when I change it to 12 , the output is [ . Why? How to fix that?

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 [] []

This is because you're running out of space on the heap. By default, the heap of Clean programs is set to 2M. You can change this, of course. When using clm from the command line, you can add -h 4M to its command line or to the command line of the clean program itself. If you're using the Clean IDE, you can change the heap size through Project Options, Application.

The reason that ( is still printed (which is what I get, rather than [ ), is the following. A Clean program will output as much of its output as possible, rather than waiting until the whole output is known. This means, for example, that a simple line as Start = [0..] will spam your terminal, not wait until the whole infinite list is in memory and then print it. In the case of squeen.icl , Clean sees that the result of Start will be a tuple, and therefore prints the opening brace directly. However, when trying to compute the elements of the tuple ( length solutions and hd solutions ), the heap fills up, making the program terminate.

I don't know what it looks like when you get a full heap on Windows, but on Linux(/Mac), it looks like this:

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

Note that the tuple opening brace is on the last line. So, when using a terminal it is quite easy to spot this error.

Interestingly, since length exploits tail recursion, the first element of the tuple can be computed, even with a small heap (you can try this by replacing the second element with [] ). Also the second element of the tuple can be computed on a small heap (replace the first element with 0 ).

The point is that the length is computed before the head, since it has to be printed the first. While with a normal length call parts of the list are garbage collected (after iterating over the first 100 elements, they can be discarded, allowing for smaller heap usage), the hd call makes sure that the first element of the list is not discarded. If the first element is not discarded, than neither can the second be, the third, etc. Hence, the whole list is kept in memory, while this is not actually necessary. Flipping the length and hd calls solve the issue:

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

Now, after hd has been called, there is no reason to keep the whole list in memory, so length can discard elements it has iterated over, and the heap doesn't fill up.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM