![]() |
Representing
Non-Determinism in Qi: depth-first search |
EntropyFails (http://programmingkungfuqi.blogspot.com/2006/04/qi-programming-basics-where-guards-and.html) chose
an nice example - depth-first search - to illustrate the handling
of non-determinism in Qi. For people new to Qi you can also find the relevant chapter
on non-determinism in my online book in http://www.lambdassociates.org/webbook/chap7.htm.
Perhaps readers would find some interest in a typed higher-order
version of depth first search which is domain-neutral (i.e. not
constrained to any particular domain).
The Basics
What does depth-first search
need? A basic version needs
a. An initial start State.
b. A Successors function that maps any State to a list of
Successor States.
c. A Goal? function that tests a state to see if it is a goal
state.
To this bread and butter version I add one extra ingredient.
d. A Fail? function that tests a State and sees if it should
trigger backtracking.
Using these ingredients, we can build a higher-order typed
depth-first search function. First the code without comments;
then the comments.
Code
(define depth
{A --> (A --> [A]) --> (A --> boolean) --> (A
--> boolean) --> boolean}
State Successors Goal? Fail? -> (depth-help [State] Successors
Goal? Fail?))
(define depth-help
{[A] --> (A --> [A]) --> (A --> boolean) --> (A
--> boolean) --> boolean}
[State | _] _ Goal? _ -> true where (Goal? State)
[State | _] _ _ Fail? -> false where (Fail? State)
[State | _] Successors Goal? Fail? <- (fail-if (/. X (= X
false)) (depth-help (Successors State) Successors Goal? Fail?))
[_ | States] Successors Goal? Fail? -> (depth-help States
Successors Goal? Fail?)
_ _ _ _ -> false)
Comments
depth takes a initial start state; a successor function that maps
a state to a list of successor states, a function that tests a
state to see if it is a goal state, and a test for failure. Lets
look at depth-help line by line. The first two lines are just
name and type; the next line is:
1. [State |
_] _ Goal? _ -> true where (Goal? State)
means 'return true if the State is a goal state'
2. [State |
_] _ _ Fail? -> false where (Fail? State)
means 'return false if the State is a failure state'
3. [State |
_] Successors Goal? Fail?
<- (fail-if (/. X (= X false)) (depth-help (Successors State)
Successors Goal? Fail?))
means 'recurse using the State at the head of the list and fail
under the condition that the function returns 'false'. Notice the
use of <- rather than ->. This is important.
EntropyFails explains how it works in his post quite well. So
I'll quote him here.
"..... Qi provides us with a special form called
"fail-if" that does exactly that. It takes in a
function and a value. It tests to see if the value causes the
function to return true. If it does, it starts backtracking. If
the test returns false, however, it stops backtracking. It
basically means "backtrack if true"."
4. [_ |
States] Successors Goal? Fail? -> (depth-help States
Successors Goal? Fail?)
means 'If control has got to this point then, the first State has
failed so have a look at the others.'
5. _ _ _ _
-> false
means that we've gone through all the successor states and found
nothing - so return 'false'.
Trial
Lets try it. Here is a problem
From 4 can I make 27 by adding 3, 4 and 5 in any order?
This can be phrased as a search
problem. The start state is 4, the goal function is (/. X (= X
27)) (that is, is X 27?, remember Qi uses /. for a lambda). Failure occurs
if we exceed 27 (that is, (/. X (> X 27)) returns true when
applied) and the successors function adds the current number to
3,4 and 5 and forms a list of the results (that is,
(/. X [(+ X 3) (+ X 4) (+ X 5)])).
(8+) (depth
4 (/. X [(+ X 3) (+ X 4) (+ X 5)]) (/. X (= X 27)) (/. X (> X
27)))
true : boolean
Yes this can be done!
From 4 can I make 27 by adding 3?
(9+) (depth
4 (/. X [(+ X 3)]) (/. X (= X 27)) (/. X (> X 27)))
false : boolean
No it cannot.
Returning a Path
Well, true and false are not
very informative really. So lets add an extra input and change
the types of the functions. First I switch off the type checker
and wipe the board clean.
(10+) (tc -)
false : boolean
(11-) (destroy depth)
depth
(12-) (destroy depth-help)
depth-help
(13-) (tc +)
true
This removes the functions and their types from the environment.
I'm back in to the type checker again.
(You can overwrite types in Qi, but it's not type-secure and the top
level tells you off before complying. So lets be good and avoid
the telling off.)
We add an extra argument Path in
depth-help that maintains the path to our solution. Every time we
apply the Successors function to a State in travelling down a
branch we push
the State onto the Path. Our
failure object is the empty Path ([])
which is what we get if there is no path. Otherwise if we find a
solution, we return the Path (in
reverse order because we want the goal State last).
Here is the new program - only 1 line longer than the last one.
(define
depth
{A --> (A --> [A]) --> (A --> boolean) --> (A
--> boolean) --> [A]}
State Successors Goal? Fail? -> (depth-help [State] Successors
Goal? Fail? []))
(define depth-help
{[A] --> (A --> [A]) --> (A --> boolean) --> (A
--> boolean) --> [A] --> [A]}
[State | _] _ Goal? _ Path -> (reverse [State | Path]) where
(Goal? State)
[State | _] _ _ Fail? _ -> [] where (Fail? State)
[State | _] Successors Goal? Fail? Path <- (fail-if empty?
(depth-help (Successors State) Successors Goal? Fail? [State |
Path]))
[_ | States] Successors Goal? Fail? Path -> (depth-help States
Successors Goal? Fail? Path)
_ _ _ _ _ -> [])
Give me a path from 4 to 27 by adding 3, 4 or 5.
(14+) (depth
4 (/. X [(+ X 3) (+ X 4) (+ X 5)]) (/. X (= X 27)) (/. X (> X
27)))
[4 7 10 13 16 19 22 27] : (list number)
Give me a path from 4 to 27 by adding 3 only.
(15+) (depth
4 (/. X [(+ X 3)]) (/. X (= X 27)) (/. X (> X 27)))
[] : (list number)
The verdict is there isn't one.
This is a polymorphic program, so you can experiment with this
program in different domains than the numeric example given here.
This post is taken from the thread at the Qilang news group. You can post your discussion
here.
Have fun.
Mark
Copyright (c) 2006, Mark Tarver
dr.mtarver@ukonline.co.uk