2

So I have a problem that should be easy to solve, come to this point where I know the flow of this program I'm working on.

There is code before but this piece of code I paste here is called with this:

pairs_nodes_arcs(C, C, SalidaCreacionEnum, EnumArcos).

What it should do: Travel through second argument till it's empty, find if it meets every condition, if it does, give the ouput list that got formed (EnumArcos should then hold the answer) because of the conditions as an "answer" (that I have the intention of sending "up" through backtracking to the rest of the code, since it's one of the answers of the real question).

Now, if fails, it should (and does), remove the head of the third argument, which is a list of lists, and "re-start" the second argument (I also do this succesfully because I always hold a copy to this first argument, which is the same than the second originally was).

So as I said, it should return, in it's 4th argument, the answer list. And it does, when I use trace I see the correct answer there (finally!!) but it gets lost because when it does backtracking, that answer list get's empty, and I end up returning empty.

I just read here http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse9 that an base query is mandatory. But I can't figure it out (in strict sense, my code does not get into an infinite loop, and even if it's by dumb luck, it's not the problem). The problem is that when backtracking, I loose the answer.

pairs_nodes_arcs(_, _, [],_).
pairs_nodes_arcs(_, [], _,_).
pairs_nodes_arcs([], _, _, _).
pairs_nodes_arcs([A-B | T],[C-D | Tail0], [H|NODES], LISTARCS) :-
    member(enum(NODE_ID_C, C), H),
    member(enum(NODE_ID_D, D), H),
    REMAINDER is abs(NODE_ID_C-NODE_ID_D),
    arcs_are_repeated([A-B  | T], [C-D | Tail0], [H|NODES], REMAINDER,LISTARCS).

arcs_are_repeated([A-B | T], [_ | Tail0], [H|NODES], REMAINDER, ListArcsInput):-
    %maplist(dif(enum(REMAINDER,_)), ListArcsInput),
    myMapList(enum(REMAINDER,_),ListArcsInput),
    pairs_nodes_arcs([A-B | T], Tail0, [H|NODES], [enum(REMAINDER, A-B) | ListArcsInput], ).

arcs_are_repeated([A-B | T], [_], [H|NODES],_,_):-
    pairs_nodes_arcs([A-B | T], [A-B | T], NODES, []).


myMapList(_, []).
myMapList(enum(NUM1,_), [enum(NUM2,_)|InputList]):-
    dif(NUM1,NUM2),
    myMapList(enum(NUM1,_), InputList).

I also have the trace, I paste only the specific part where I feel like I "loose the answer (I manually separate the 4th argument to give it emphasis, it's all part of the same parenthesis):

pairs_nodes_arcs([a-b, b-c], [], [[enum(1, c), enum(3, b), enum(2, a)], [enum(2, c), enum(1, b), enum(3, a)], [enum(2, c), enum(3, b), enum(1, a)], [enum(3, c), enum(1, b), enum(2, a)], [enum(3, c), enum(2, b), enum(1, a)]], 


[enum(2, a-b), enum(1, a-b)])


 Exit:pairs_nodes_arcs([a-b, b-c], [], [[enum(1, c), enum(3, b), enum(2, a)], [enum(2, c), enum(1, b), enum(3, a)], [enum(2, c), enum(3, b), enum(1, a)], [enum(3, c), enum(1, b), enum(2, a)], [enum(3, c), enum(2, b), enum(1, a)]], [enum(2, a-b), enum(1, a-b)])
 Exit:arcs_are_repeated([a-b, b-c], [b-c], [[enum(1, c), enum(3, b), enum(2, a)], [enum(2, c), enum(1, b), enum(3, a)], [enum(2, c), enum(3, b), enum(1, a)], [enum(3, c), enum(1, b), enum(2, a)], [enum(3, c), enum(2, b), enum(1, a)]], 2, 


[enum(1, a-b)])


 Exit:pairs_nodes_arcs([a-b, b-c], [b-c], [[enum(1, c), enum(3, b), enum(2, a)], [enum(2, c), enum(1, b), enum(3, a)], [enum(2, c), enum(3, b), enum(1, a)], [enum(3, c), enum(1, b), enum(2, a)], [enum(3, c), enum(2, b), enum(1, a)]], 


[enum(1, a-b)])

edit1 Ok so my way of fixing this so far seems to be to pass an empty list, which I operate, and a variable that I always keep for myself. Then when the base case of SUCCEED succeeds, I unify the operated list that is the solution with the variable. I do that at least 3 times in the code and need it 1 more time. Not 100% it's a nice way of doing it though, I end up with a number of arguments but... I mean that there seems to be problems with this approach but it's one, at least.

keont
  • 653
  • 1
  • 9
  • 26
  • Regarding your **edit1**: That's definitely one way to do it. A nicer way is to simply *further instantiate* a partial list, so that it consists of all elements when the predicate succeeds. For example, you start with `Ls0`, and the predicate says: `Ls0 = [First|Rest]`, and then reasons about `Rest`. A very nice way to describe lists efficiently: [tag:dcg]. – mat May 31 '16 at 09:18
  • That was what I was trying to do, when it succeeds to hold every element. But I must have been doing something wrong because it didn't bind properly not matter what (in other, simpler pieces of code I've been able to do this, I think, it's a matter of coding poorly, I guess). And I might try reading again about DCG, but every time I tried before I understood very little at all. Maybe now that I have a tiny bit more experience... – keont May 31 '16 at 09:21
  • 1
    One of your clauses probably was *too general*. I would go with the "instantiate further" approach or describing lists with DCGs. – mat May 31 '16 at 09:22
  • Alright, I will check that, it should be easier now that I have working code. I need to check everything several times so that no dumb mistakes were made, or that I'm missing something. – keont May 31 '16 at 09:29

1 Answers1

4

You answer is not about backtracking (backtracking happens on failure), but about success: You expect your program to succeed with a specific binding, but it doesn't.

To debug this, think declaratively: Write down a goal that ought to succeed, but fails.

In your example, the query:

?- arcs_are_repeated([a-b, b-c], [b-c], [[enum(1, c), enum(3, b), enum(2,a)],
    [enum(2, c), enum(1, b), enum(3, a)], [enum(2, c), enum(3, b), enum(1, a)],
    [enum(3, c), enum(1, b), enum(2, a)], [enum(3, c), enum(2, b), enum(1, a)]], 
    2, [enum(1, a-b)]).

may be such an example.

Then, make the goal more general, in such a way that it still fails. For example, does the following still fail:

?- arcs_are_repeated([a-b, b-c], [b-c], [[enum(1, c), enum(3, b), enum(2,a)],
    [enum(2, c), enum(1, b), enum(3, a)], [enum(2, c), enum(3, b), enum(1, a)], _], 2, _).

if so, generalize it further. For example, what about:

?- arcs_are_repeated([a-b, b-c], _, _, _, _).

If this fails, you have narrowed down a case you have not yet taken into account, no matter what the arguments are.

This is how you debug declaratively: Think about what should succeed, but fails, and think about what should fail, but succeeds. In the former case, your program is too specific, and in the latter, it is too general. In your case, it is too specific, and so you either need to:

  • add a clause or
  • remove a constraints in the existing clauses

to make the program more general.

mat
  • 40,498
  • 3
  • 51
  • 78
  • Currently checking your answer to understand it (btw still processing parts of the previous one, with the code, it's hard but I feel improving on this as a whole). I just wanted to say thank you and that sometimes I do not know how to answer myself why do I go to university. You succeed where my teachers, by lack of interest alone (on properly explaining concepts), fail. So thank you. – keont May 31 '16 at 07:17
  • 1
    The methods I am applying depend on techniques that were not widely and freely available only a few years ago. For example, CLP(FD) constraints were available in fewer (and more expensive) systems. `dif/2` was not as widely available. It will take years and decades until what is now available spreads into text books and education of instructors, and from there onto the next generation of Prolog programmers. For example, what I am outlining above cannot be done in general if you still use low-level arithmetic predicates or nonmonotonic constructs like `!/0`. – mat May 31 '16 at 07:23
  • That's incredible, really. For someone like me who has always studied with the internet (at uni at least), for some knowledgement to not be available for the most part... On the other hand, I now realize I need to be careful. The solution has to run in a certain program (ciao...) and I will only find out when I try it (currently programming online, that's how bad the program is). – keont May 31 '16 at 07:32
  • 1
    Chances are high that your program *will* run in Ciao once it is correct, with at most small changes. – mat May 31 '16 at 07:34
  • Great!! I'm relieved to hear that! Now, for the questions you "made", first one does not fail, at all, it just, as you said, succeed but does not BIND. The next two also succeed but binding wrongly (I can see how, even if they are variables, I loose them, so there's that). I'm now trying to think of a proper binding, I thought about, when it succeds, in the base case pairs_nodes_arcs(_, [], _,_). to add a parameter that binds with the 4th current one (I was already trying that, not working). Would that count as adding a clause? Or I'm looking at the wrong place – keont May 31 '16 at 07:40
  • Ok so bad news @mat , ciao tells me dif does not exist. In other languages I would take the library and add it or whatever, I've seen prolog does have similar things, but I feel just in case it would be better to add the definition inside my own program, just because I do not know what tests are going to be used. So, is there a place where I can look at dif implementation?? Or is it somewhat protected code of swi or something :S – keont May 31 '16 at 09:17
  • 1
    Use the safe approximation [`iso_dif/2`](http://stackoverflow.com/a/20238931/1613573). Also, you can still debug your code in a Prolog system that provides this important constraint, and learn how to think more declaratively. Later, you can use impure predicates, bearing in mind that they destroy relational properties of your code and severly limit the generality to specific directions and modes of use. – mat May 31 '16 at 09:21
  • 1
    That absolutly did it!! So glad, I'm really thankful. I now have at least ONE case that works as intended, in it's intended program. Such a relief. I now intend to look for cases, look how long can the list be (last time I checked, anything bigger than 9 nodes makes the program to run out of memory), and to go back and read everything you told me to do. At the very least, see that #= and properly naming rules. Probably missing something but I will re read all of it. Thanks again – keont May 31 '16 at 09:27