Glen Able wrote:
> I've been fiddling with my 'su doku' search program. I now pass a function
> to the search which does whatever it wants when a solution is found, and
> tells the search to either continue or stop.
>
> So, to find a single solution and abort, I'd use:
>
> (defun find-single-solution (grid)
> (fill-grid grid
> (lambda (grid)
> (print-grid grid)
> nil)))
>
> Instead, to exhaustively count all solutions, I'd use:
>
> (let ((solutions 0))
> (defun count-solutions (grid)
> (setf solutions 0)
> (fill-grid grid
> (lambda (grid)
> (incf solutions)
> t))
> solutions))
>
> (Problem: the 'grid' argument is never used in the anonymous function, which
> causes a warning. Best way round this?)
(lambda (grid)
(declare (ignore grid))
....
If you might want a debugging line in there at some point, you can say
ignorable, in which case if you throw in a print statement you will not
get an error.
>
> (Question: is there a better way of handing the count?)
>
> The recursive part is now:
>
> (defun fill-grid (grid on-found)
> (let ((free-pos (first-free grid)))
> (if free-pos
> (let ((choices (remove-used
> grid
> free-pos
> (list 1 2 3 4 5 6 7 8 9))))
> (if choices
> (dolist (fill-val choices)
> (fill-grid (next-grid grid free-pos fill-val)
> on-found))))
> (funcall on-found grid))))
>
> So, if there's space to fill in the grid, it carries on searching, and if
> there's no space it funcalls the callback with the filled grid.
> Now, my big problem is that I need to actually use the result from the
> funcall to decide whether to continue. So I need to be able to 'break' out
> of the dolist loop if a child call to 'fill-grid' decides it has finished.
> Can anyone help me achieve this? I've been trying to do it with 'break' but
break is guaranteed by the spec to put you in a backtrace. (AllegroCL
gets this wrong, btw.)
The simplest way out is:
(setf answer (catch :bingo! <etc>))
and then anyone called can execute (throw :bingo! grid) to return the
answer.
Or you could structure your code so that everyone returns as soon as
they see that the search is over. You probably need to leverage Lisp's
ability to return two values. eg, when the solution is identified, do:
(values grid (funcall on-found grid))
Then in the dolist where you are repeatedly trying moves:
(dolist ()
...
(multiple-value-bind (solution keep-going)
(unless keep-going
(return-from fill-grid (values solution nil)))
Or just code up DO or LOOP to watch for the termination notice.
kenny
"Fred Gilham" <······@snapdragon.csl.sri.com> wrote in message
···················@snapdragon.csl.sri.com...
>
> Kenny Tilton wrote:
> > The simplest way out is:
> >
> > (setf answer (catch :bingo! <etc>))
> >
>
> To do what you want, just change find-single-solution to
>
> (defun find-single-solution (grid)
> (catch 'found-solution
> (fill-grid grid
> (lambda (grid)
> (print-grid grid)
> (throw 'found-solution nil)
> nil))))
>
Yikes, so you can nonchalantly jump all the way back up the stack and out of
'fill-grid' in one step.
I've gone for Kenny's other suggestion, using return-from, which I wasn't
aware of, and which I've had to struggle to find in any of my ebooks - why
so obscure? So 'fill-grid' does a return-from with value t if one of its
child calls returns t, or if the 'on-found' call says to stop - very simple.
Btw I don't need multiple values because solutions are handled by passing
them to the callback function.
So now I've been able to efficiently implement these 3 variants, with just
the anonymous function's return value controlling the search.
(defun find-single-solution (grid)
(defun count-solutions (grid)
(defun has-unique-solution (grid)
Thanks guys!
G.A.
Glen Able wrote:
> Yikes, so you can nonchalantly jump all the way back up the stack
> and out of 'fill-grid' in one step.
That's nothing. You should see what Scheme's
call-with-current-continuation lets you do. Imagine being able to
jump right back in.... :-)
--
Fred Gilham ······@csl.sri.com
...every candid Reader will easily understand my Discourse to be
intended only in Defence of *nominal* Christianity, the other having
been for some time wholly laid aside by general Consent, as utterly
inconsistent with all our present Schemes of Wealth and Power.
--- Jonathan Swift
In article <···········@newsg1.svr.pol.co.uk>,
Glen Able <·········@gmail.com> wrote:
> > To do what you want, just change find-single-solution to
> >
> > (defun find-single-solution (grid)
> > (catch 'found-solution
> > (fill-grid grid
> > (lambda (grid)
> > (print-grid grid)
> > (throw 'found-solution nil)
> > nil))))
> >
>
> Yikes, so you can nonchalantly jump all the way back up the stack and
> out of 'fill-grid' in one step.
>
> I've gone for Kenny's other suggestion, using return-from, which I
> wasn't aware of, and which I've had to struggle to find in any of my
> ebooks - why so obscure?
Just to impress upon you that CL's GO and RETURN constructs are not to
be trifled with ( :-) ), RETURN-FROM can "nonchalantly jump all the way
back up the stack" as well:
(defun find-single-solution (grid)
(block finding-solution
(fill-grid grid
(lambda (grid)
(print-grid grid)
(return-from finding-solution nil)))))
See, CL's tags and blocks have dynamic extent too, but unlike TRY and
CATCH have lexical scope. This (IMHO) makes this version a little
cleaner because you don't need dynamic scope to express this here.
This can be an insanely powerful flow control mechanism. In fact, if
you look at your implementation's expansions for HANDLER-CASE and
RESTART-CASE, it probably uses GO to unwind the stack.
Also, for a little mental excercise, you can implement a working TRY and
CATCH with TAGBODY and GO. :)
-bcd
--
*** Brian Downing <bdowning at lavos dot net>