4

New to Prolog, trying to write a predicate to give all the options that an element could be inserted in to a list at any position. Ex:

ins(a, [b,c], R). should give:

R = [a,b,c]
R = [b,a,c]
R = [b,c,a]

which it does, but then gives an error 'Out of Global stack'. Is there a way to make this more deterministic, give the results and be done? When it is run in reverse ie. ins(X, Y, [a,b,c]). It gives the expected results then says false indicating it has completed. Code:

app([],L,L).
app([H|T],L2,[H|L]) :- 
    app(T,L2,L).

ins(E, List, R) :-
    R = R1,
    app(R2, R3, R1),
    app([E], R4, R3),
    app(R2, R4, List).

Here is a link to run the code in an online compiler, SWISH (This also has an example of how I hope to use ins, but ins is the problem right now) Any Help would be appreciated!

false
  • 10,264
  • 13
  • 101
  • 209
ABane
  • 43
  • 1
  • 3

3 Answers3

4

Did you notice how this went bad? First, Prolog was very nice and dandy and showed you how smart it is, and only later on it struck you: Buy. More. RAM. Now!

Wouldn't it be better if Prolog would be up front? Before showing any answer?

Well, you can force Prolog to do exactly this. Add a false at the end of your query like so:

?- ins(a, [b,c], R), false.
   resource_error(_). % ERROR: Out of global stack

And the same you can do with your remaining program: Simply add false such that the remaining program still loops or runs out of space. I came up with the following minimal

app([],L,L) :- false.
app([H|T],L2,[H|L]) :-
    app(T,L2,L), false.

ins(E, List, R) :-
    R = R1,
    app(R2, R3, R1), false,
    app([E], R4, R3),
    app(R2, R4, List).

?- ins(a, [b,c], R), false.

That means that we have to modify something in the remaining visible part in order to get rid of that looping. In other words: As long as the visible part remains unmodified the error will persist — guaranteed!

For more on this technique to understand reasons for non-termination see

The immediate fix would be to put the first app/3-goal last.


But there is something else: You used all kinds of variables that are difficult to fathom. Maybe stick to a more uniform scheme. Also, there is no need for appending [A] using app/3. You actually need only two app/3 goals.

false
  • 10,264
  • 13
  • 101
  • 209
3

Here is a simple implementation of this predicate:

ins(X, [], [X]).
ins(X, [H|T], [X,H|T]).
ins(X, [H|T], [H|T2]) :-
    ins(X, T, T2).

It works in the directions you would expect it to:

?- ins(a, [b,c], R).
R = [a, b, c] ;
R = [b, a, c] ;
R = [b, c, a] ;
false.

?- ins(a, L, [a,b,c]).
L = [b, c] ;
false.

?- ins(X, [b,c], [a,b,c]).
X = a ;
false.

?- ins(X, L, [a,b,c]).
X = a,
L = [b, c] ;
X = b,
L = [a, c] ;
X = c,
L = [a, b] ;
false.

?- ins(a, X, Y).
X = [],
Y = [a] ;
X = [_5312|_5314],
Y = [a, _5312|_5314] ;
X = [_5312],
Y = [_5312, a] ;
X = [_5312, _5324|_5326],
Y = [_5312, a, _5324|_5326] ;
…
Fatalize
  • 3,513
  • 15
  • 25
  • 1
    @false You could indeed fuse both facts into `ins(X, L, [X|L]).`. Both versions still have problems with `ins(a, X, Y)` though (they won't enumerate every possible case). – Fatalize Nov 21 '17 at 07:46
  • Fusing both the clauses give correct solutions too. – javaEntu May 17 '20 at 21:31
0

Prolog has an interesting predicate called select :

?- select(a, Out, [b,c]).
Out = [a, b, c] ;
Out = [b, a, c] ;
Out = [b, c, a] ;
false.

You can use it with another very usefull predicate called setof :

ins(Elem, In, Lst_Out) :-
    setof(Out, select(Elem, Out, In), Lst_Out).

This gives :

?- ins(a, [b,c], Out).
Out = [[a, b, c], [b, a, c], [b, c, a]].
joel76
  • 5,565
  • 1
  • 18
  • 22