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.