- What is the shortest cycle you could have in your fact database? Would
circular(a, a).
be cycle [a]
? Knowing what the shortest cycle is might help you find (one of) the finishing condition(s) for your predicate.
- To be able to find a list, your predicate needs to build it. Your
in_cycle/2
never mentions any lists. You need to use the [Head | Tail]
construct somewhere in there to be able to add elements to a list.
- You already know something about what your cyclical list looks like. The first and last element are the same, and they are the same as the symbol you're trying to find the cycle for. Use that information.
- To be able to tell when you completed a cycle, you need to remember which symbol you started with. To be able to do that with recursion, you need to keep state. So you're going to need an additional predicate where you keep that state. E.g. something involving a predicate like
in_cycle(Current_symbol, Cycle, Start_symbol)
. You can then call that from your in_cycle/2
.
Let's have a look at your try:
in_cycle(C, Cycle) :-
circular(C, Cycle1),
in_cycle(Cycle1, Cycle).
You can use the trace
command at the prompt to see what's happening: trace, in_cycle(a, X).
Press Space to step through your program. Press h for help, and a to exit. Use notrace.
to get out of trace mode again.
As you step through this, you'll find that your predicate is nicely looping through the cycle, but at no point does the X
ever become a list. That's bad.
Let's try and make this build a list. As I mentioned in point (3), you already know something about the list. The first element of the list is the same as the first argument to in_cycle
. Even more, the second element of the list is the same as the element you'll find with circular/2
. So we know a cycle has at least two elements. How about this?
in_cycle(First, [First, Next|Cycle]) :-
circular(First, Next),
in_cycle(Next, [Next|Cycle]).
If you trace this now, you'll see something is happening with X
, but still not actually anything useful. Cycle
remains a mystery and we're just looping through the facts forever. You need some end condition here. Let's try a simple one:
in_cycle(First, [First]).
in_cycle(First, [First, Next|Cycle]) :-
circular(First, Next),
in_cycle(Next, [Next|Cycle]).
Whoa! in_cycle(a, X)
suddenly gives results! All possible lists using circular
connections starting with a
, it seems. That's not exactly what we want, but maybe we're getting closer?
One problem with this is that in_cycle(Next, [Next|Cycle])
is not actually correct!
If you do in_cycle(a, X)
, you already know that X
should become [a, b, d, a]
, so filling those values into in_cycle(First, [First, Next|Cycle])
, you get:
First = a
Next = b
Cycle = [d, a]
When you get to in_cycle(Next, [Next|Cycle])
, that means it's in_cycle(b, [b, d, a])
. But [b, d, a]
is not a cycle! You need to be able to distinguish these two situations somehow. One way of doing that is to call a separate predicate like I mentioned in (4) to keep track of what your starting element was.