Prolog List Manipulation Assistance - list

I have a prolog homework problem that I've been working on that I'm rather stuck on. The basic problem is that I have three lists, each of which has members containing a phrase and a value. I am supposed to randomly select a member of the list, print the phrase, and repeat until I've printed phrases from at least two of the lists, and the total value of the phrases printed is at least 9.
What I've done so far: I've gone through several dozen iterations of the program, and searched the SWI documentation as well as Lean Prolog Now and stack overflow. In the process I've learned that using assert() to add an item to a list will cause corruption on a subsequent run, all variables are local, that appending a single item to an empty list does not result in a list but does result in fun error messages the next recursion, and that you can't vary a variable's value.
My program successfully does the following: randomly chooses one of 3 lists. Randomly grabs an item from that list. Places the phrase and value from the selected item into different variables, and prints them.
Its main issue is that my list manipulation does not appear to be working. The line "append(Usedlist, Z, Usedlist3)," always fails in the second iteration of the code without throwing an error. Additionally, I believe that the member(Z, Usedlist) is not working as the rare occasions where I randomly get a repeat in the second iteration it does not function properly. However, I suspect that the fault is in the append not adding to the list properly.
Relevant code (condensed to 1 list for sanity reasons)
main :-
A is 0,
B is 0,
C is 0,
Num is 0,
Usedlist = ["Garbage data1", "More garbage data"],
prologwhile(Num, A, B, C, Usedlist).
prologwhile(Num, A, B, C, Usedlist) :- Num < 9 ,
F = [["time of day", 1], ["Season", 5], ["Yesterdays weather", 2], ["Month of last big storm", 2], ["Randomly generated riddle", 9], ["A captcha", 1], ['Current day', 6], ["Tomorrows date", 5]],
random_member(Rand, F),
nth0(1, Rand, Add),
writeln(Rand),
nth0(0, Rand, Z),
writeln(Usedlist),
( member(Z, Usedlist) ->
writeln("Test5"),
prologwhile(Num, A, B, C, Usedlist)
; writeln(Rand),
writeln(Add),
writeln("Test6"),
append(Usedlist, Z, Usedlist3),
writeln(Z),
C3 is 1,
Num3 is Num + Add,
writeln(Num3),
prologwhile(Num3, A, B, C3, Usedlist3)
)
Sample output
[Tomorrows date,5]
[Garbage data1,More garbage data]
[Tomorrows date,5]
5
Test6
Tomorrows date
5
[time of day,1]
[Garbage data1,More garbage data|Tomorrows date]
[time of day,1]
1
Test6
false.

Let us tackle it in stages:
1. Eliminate side-effects and simplify the program
I start with the following straight-forward changes of your program:
I eliminate all goals that perform I/O, since these only get in the way of the reasoning we would like to perform about the program.
Instead of X is C where C is a constant, and the variable is only used once in the remainder of the code, I simply use the constant C directly.
Thus, we obtain:
main :-
prologwhile(0, 0, 0, 0, ["Garbage data1", "More garbage data"]).
prologwhile(Num, A, B, C, Usedlist) :-
Num < 9 ,
F = [["time of day", 1], ["Season", 5], ["Yesterdays weather", 2], ["Month of last big storm", 2], ["Randomly generated riddle", 9], ["A captcha", 1], ['Current day', 6], ["Tomorrows date", 5]],
random_member(Rand, F),
nth0(1, Rand, Add),
nth0(0, Rand, Z),
( member(Z, Usedlist) ->
prologwhile(Num, A, B, C, Usedlist)
; append(Usedlist, Z, Usedlist3),
Num3 is Num + Add,
prologwhile(Num3, A, B, 1, Usedlist3)
).
Now, for readability, I make the following additional changes:
I factor out the phrases into their own predicate
I use pairs instead of lists to represent Phrase-Value pairs
I use pattern matching and a bit more telling variable names.
In total, we now have:
main :-
prologwhile(0, 0, 0, 0, ["Garbage data1", "More garbage data"]).
prologwhile(Num, A, B, C, Usedlist) :-
Num < 9 ,
phrases(Ps),
random_member(Phrase-Value, Ps),
( member(Phrase, Usedlist) ->
prologwhile(Num, A, B, C, Usedlist)
; append(Usedlist, Phrase, Usedlist3),
Num3 is Num + Value,
prologwhile(Num3, A, B, 1, Usedlist3)
).
phrases(["time of day"-1,
"Season"-5,
"Yesterdays weather"-2,
"Month of last big storm"-2,
"Randomly generated riddle"-9,
"A captcha"-1,
'Current day'-6,
"Tomorrows date"-5]).
Henceforth, I assume phrases/1 as defined in the above snippet, and do not carry its definition along.
2. Find the cause of the failure
Now the actual reasoning about the program starts, because we have:
?- main.
false.
To help you find the mistake, I add the following definition to the program:
$(Goal) :-
portray_clause(Goal),
Goal.
We can use this predicate as follows in prologwhile/5:
prologwhile(Num, A, B, C, Usedlist) :-
Num < 9 ,
phrases(Ps),
random_member(Phrase-Value, Ps),
( member(Phrase, Usedlist) ->
prologwhile(Num, A, B, C, Usedlist)
; $(append(Usedlist, Phrase, Usedlist3)),
Num3 is Num + Value,
prologwhile(Num3, A, B, 1, Usedlist3)
).
We now get:
?- main.
append(["Garbage data1", "More garbage data"], "A captcha", _).
append(["Garbage data1", "More garbage data"|"A captcha"], 'Current day', _).
false.
So here's the problem:
?- append(["Garbage data1", "More garbage data"|"A captcha"], 'Current day', _).
false
This goal fails, and hence the whole program fails.
You obviously intended the following:
append(Usedlist, [Phrase], Usedlist3)
i.e., you want to append a the contents of two lists, not that of a list and something else. Note that it is often a good idea to prepend elements in front of a list instead of appending them to obtain good performance, so we can write this as:
Usedlist3 = [Phrase|Usedlist]
So, the whole predicate becomes:
prologwhile(Num, A, B, C, Usedlist) :-
Num < 9 ,
phrases(Ps),
random_member(Phrase-Value, Ps),
( member(Phrase, Usedlist) ->
prologwhile(Num, A, B, C, Usedlist)
; Usedlist3 = [Phrase|Usedlist],
Num3 is Num + Value,
prologwhile(Num3, A, B, 1, Usedlist3)
).
Or shorter:
prologwhile(Num, A, B, C, Usedlist) :-
Num < 9 ,
phrases(Ps),
random_member(Phrase-Value, Ps),
( member(Phrase, Usedlist) ->
prologwhile(Num, A, B, C, Usedlist)
; Num3 is Num + Value,
prologwhile(Num3, A, B, 1, [Phrase|Usedlist])
).
However, we still have:
?- main.
false.
To see why this still fails, think about the cases in which the predicate ought to hold. After a bit of reflection, the following is a case in which it should hold:
prologwhile(Num, _, _, _, [_,_|_]) :- Num >= 9.
Thus, we add the case to our program, obtaining:
prologwhile(Num, _, _, _, [_,_|_]) :- Num >= 9.
prologwhile(Num, A, B, C, Usedlist) :-
Num < 9 ,
phrases(Ps),
random_member(Phrase-Value, Ps),
( member(Phrase, Usedlist) ->
prologwhile(Num, A, B, C, Usedlist)
; Num3 is Num + Value,
prologwhile(Num3, A, B, 1, [Phrase|Usedlist])
).
Note how I am using pattern matching on the accumulated list to detect the case that elements from at least 2 different pairs are used.
Now, we at last have:
?- main.
true .
That's pretty cool, but not that useful.
3. Report the actual solution on the toplevel
Now, it is time to ask a few deeper questions about the program. For example, what are all these arguments really doing here? Let us simply forget about A, B and C, obtaining:
main :-
prologwhile(0, ["Garbage data1", "More garbage data"]).
prologwhile(Num, [_,_|_]) :- Num >= 9.
prologwhile(Num, Usedlist) :-
Num < 9 ,
phrases(Ps),
random_member(Phrase-Value, Ps),
( member(Phrase, Usedlist) ->
prologwhile(Num, Usedlist)
; Num3 is Num + Value,
prologwhile(Num3, [Phrase|Usedlist])
).
It works just as well:
?- main.
true .
On the other hand, an important thing seems to be missing, namely the phrases we have actually used! Let us get them by introducing an argument that represents them. In such situations, a useful naming convention is a pair Ls0 and Ls, with Ls0 denoting the initial list, and Ls the final one that we want the toplevel to report.
We can also simply forget about the "garbage" elements.
Thus the whole program becomes:
solution(List) :-
prologwhile(0, [], List).
prologwhile(Num, Ls, Ls) :- Num >= 9, Ls = [_,_|_].
prologwhile(Num, Ls0, Ls) :-
Num < 9 ,
phrases(Ps),
random_member(Phrase-Value, Ps),
( member(Phrase, Ls0) ->
prologwhile(Num, Ls0, Ls)
; Num3 is Num + Value,
prologwhile(Num3, [Phrase|Ls0], Ls)
).
phrases(["time of day"-1,
"Season"-5,
"Yesterdays weather"-2,
"Month of last big storm"-2,
"Randomly generated riddle"-9,
"A captcha"-1,
'Current day'-6,
"Tomorrows date"-5]).
Sample query:
?- solution(Ls).
Ls = ["Randomly generated riddle", "Season"] ;
false.
You can also easily extend this to report the total value, either while building the list or in a simple additional step. I leave this as an exercise. Note also that prologwhileisnotaseasytoread as for example using_underscores_would_be.

Related

Order of instructions and recursion in Prolog

I just started in prolog and I have a question regarding recursion.
I basically want to count the number of time a certain value is in a list,
so I have for example:
count( a, [ a, s, d, f, g, a, s, d, a, s ], W ). W = 3.
Where I want to count the number of time "a" is in the list and store the number in "W".
The correct answer is
count( _, [], 0 ).
count( A, [B|S], X ) :- A==B, count( A, S, Y ), X is Y + 1.
count( A, [B|S], X ) :- \+ A == B, count( A, S, X ).
Howhever, I do not understand why in line 2, the is a "X is Y+1" at the end.
Shouldn't the line rather be
count( A, [A|S], X ) :- Y is X + 1, count( A, S, Y ).
In that case, we first set Y to 1, then we send it to "count" again with the recursion.
If anyone can help me I'd appreciate it very much!
Consider that when you call:
?- count(a,[a,s,d,f,g,a,s,d,a,s],W).
...then first predicate that matches is count(A,[B|S],X). So that means that it sees it like count(a,[a|S],X) where S & X are variables. X is just W from the original call so it is still a variable.
Suggesting that Y is X + 1 is then evaluated doesn't make sense as X is a variable.
The original predicate, however, does make sense.
count(A,[B|S],X) :- A==B, count(A,S,Y), X is Y + 1.
When it recurses it sends in a new variable Y into the 2nd count/3 predicate (or just passes the same variable in the 3nd predicate). And it keeps doing that until it hits the base case when it finally unifies the variable to 0. At that point it unwinds the recursion and now it can finally do X is Y + 1 (because Y is a value) and hence X is now a value.
Prolog is a fun language because it almost has a sense of time travel in the way you have to think forwards and backwards to understand how a program works.
count( A, [B|S], X ) :- A==B, count( A, S, Y ), X is Y + 1.
This means: If S contains Y occurrences of A, and A == B, then [B | S] contains one more occurrence of A.
For example, imagine we're getting here while counting the occurrences of prolog in [prolog, prolog, prolog]. [B | S] = [prolog, prolog, prolog], so S = [prolog, prolog]. The number of occurrences of prolog in S is 2, so we should have Y = 2. The number of occurrences of prolog in [B | S] is 3, so we should have X = 3. Once we know Y = 2, then X is Y + 1 computes this correct value of X.
count( A, [A|S], X ) :- Y is X + 1, count( A, S, Y ).
This would mean: If [A | S] contains X occurrences of A, then S contains one more occurrence of A. This cannot be.
Using the above example again: Clearly [A | S] = [prolog, prolog, prolog] contains 3 occurrences of prolog, so X = 3 should hold. But then Y would have to be 4, and the goal in the body would try to prove that S = [prolog, prolog] contains 4 occurrences of prolog. This can clearly not be the case.
Note that this explanation is simply about the meaning of the predicate. It does not require us to think about recursion, or the order of goals, or the exact way that the program is actually executed. When doing Prolog programming, try to be clear about the logical meaning of your programs first.

Prolog predicate that will check if a list A is a list of the prefix sums of a list D

So I'm very new to prolog and have to write a predicate that is satisfiable when an integer list D is the list of prefix sums of a list A.
sums(A, D)
So for example,
sums([4,11,1,-3,8], [4,15,16,13,21]) is satisfiable
I have written this predicate over a dozen different ways to no avail. This is what I currently have written.
sums([], []).
sums([A], [A]).
sums([A|[B|C]], [A|[E|F]]) :- TOTAL is A + B, E = TOTAL, sums([C], [F]).
This somewhat works, in that it will check that the first values of each list are equal, and also check that the second element in the list is correct in that it should be 15. I understand why it works incorrectly in this way, but I am having trouble coming up with how to write it differently, in the correct way.
I have since changed the code to,
sumrunner(L, S) :- sumrunner(L, S, 0).
sumrunner([], [], _).
sumrunner([A], [A], _).
sumrunner([A|B], [C|D], TOTAL) :- TOTAL is TOTAL + A, TOTAL = C,sumrunner(B, D, TOTAL).
However, now it just says false for all cases except for when the two lists are empty, and when the lists both contain one element and they are both equal to each other.
You should learn more about list notation: [A|[B|C]] can be written as [A,B|C] for example. It is now clearer that C is the tail of the list, and thus, is a list itself! Therefore, when you write sums([C], [F]), you are wrapping C and F into a list, even though they are already lists, which is your problem.
If we fix this and run your predicate, we get this:
?- sums([4,11,1,-3,8],Z).
Z = [4, 15, 1, -2, 8]
It is still wrong as you can see. The main problem is that, the recursive call sums in the third rule expresses that the prefix sums of the tail of a list are the tail of the prefix sums of the list, which is wrong because those prefix sums depend on the previous element!
To solve this, you need to introduce an extra argument to maintain the sum value throughout recursive calls:
:- use_module(library(clpfd)).
prefix_sums(L, D) :-
prefix_sums(L, 0, D).
prefix_sums([], _, []).
prefix_sums([H|T], S, [S1|T2]) :-
S1 #= H + S,
prefix_sums(T, S1, T2).
Using library(clpfd), we get the behaviour we expect:
?- prefix_sums([4,11,1,-3,8],Z).
Z = [4, 15, 16, 13, 21].
But also the reverse behaviour:
?- prefix_sums(Z, [4,15,16,13,21]).
Z = [4, 11, 1, -3, 8].
And also correct behaviour with even less information:
?- prefix_sums([A,B,C],Z).
Z = [A, _7964, _7970],
B+A#=_7964,
C+_7964#=_7970.
?- prefix_sums(X,Z).
X = Z, Z = [] ;
X = Z, Z = [_7122],
_7122 in inf..sup ;
X = [_7452, _7458],
Z = [_7452, _7482],
_7458+_7452#=_7482 ;
X = [_7770, _7776, _7782],
Z = [_7770, _7806, _7812],
_7776+_7770#=_7806,
_7782+_7806#=_7812 ;
…
Your code must be simplified a lot:
sums(L, S) :- sumrunner(L, S, 0).
sumrunner([], [], _).
sumrunner([A|B], [C|D], TOTAL) :- C is TOTAL + A, sumrunner(B, D, C).
?- sums([4,11,1,-3,8], [4,15,16,13,21]).
true.
?- sums([4,11,1,-3,8], [4,15,16,14,21]).
false.
The expression C is TOTAL + A both checks the requirements and update the accumulator for the recursive step.

minimum in list of lists in prolog

hello i have a list like this:
[[3,[a,b,c,d]],[2,[a,b,d]],[5,[d,e,f]]]
list of lists...
i want to find the minimum number on inner list
in this case i want to return D=2 and L=[a,b,d]
i tried this code:
minway([[N|L]],N,L).
minway([[M|L1]|L2],D,_):- M<D, minway(L2,M,L1).
minway([[M|_]|L2],D,L):- M>=D, minway(L2,D,L).
but i got error:
</2: Arguments are not sufficiently instantiated
Exception: (8) minway([[3,[a,b,c,d]],[2,[a,b,d]],[5,[d,e,f]]], _G7777, _G7778) ?
creep
for this run sentence:
minway([[3,[a,b,c,d]],[2,[a,b,d]],[5,[d,e,f]]],D,L).
the result need to be:
D=2.
L=[a,b,d].
where my problem?
and how to fix it?
tnx a lot
First, switch to a better data representation: Instead of [Key,Value], use Key-Value!
Then, define minway_/3 based on
iwhen/2,
ground/1,
keysort/2, and
member/2, like so:
minway_(Lss, N, Ls) :-
iwhen(ground(Lss), (keysort(Lss,Ess), Ess = [N-_|_], member(N-Ls, Ess))).
Sample query using SICStus Prolog 4.5.0:
| ?- minway_([3-[a,b,c,d],2-[a,b,d],5-[d,e,f],2-[x,t,y]], N, Ls).
N = 2, Ls = [a,b,d] ? ;
N = 2, Ls = [x,t,y] ? ;
no
There are a couple of fundamental issues.
One is in your problem lies in your representation of a list. Your predicates seem to assume that, for example, [3, [a,b,c]] is represented as [3 | [a,b,c]] but it is not. The list [3 | [a,b,c]] is the list with 3 as the head, and [a,b,c] as the rest of the list or the tail. In other words, [3 | [a,b,c]] is [3, a, b, c].
And, so, your base case would be:
minway([[N,L]], N, L).
The second issue is in your other predicate clauses. There's no starting point for D. In other words, it's never given a value to start with, so you get an instantiation error. You cannot compare N > D if one of the variables doesn't have a value.
When doing a minimum or maximum from scratch, a common approach is to start by assuming the first element is the candidate result, and then replace it if you find a better one on each step of the recursion. It also means you need to carry with you the last candidate at each recursive call, so that adds extra arguments:
minway([[N,L]|T], D, R) :-
minway(T, N, L, D, R).
minway([], D, R, D, R). % At the end, so D, R is the answer
minway([[N,L]|T], Dm, Rm, D, R) :-
( N < Dm
-> minway(T, N, L, D, R) % N, L are new candidates if N < Dm
; minway(T, N, Dm, Rm, D, R) % Dm, Rm are still best candidate
).
In Prolog, you can simplify this a little since Prolog has a more general term comparison operator, #<, #>, etc, which is smart about comparing more complex terms. For example, [2, [d,e,f]] #< [3, [a,b,c]] is true since 2 < 3 is true. We can then write:
minway([H|T], D, R) :-
minway(T, H, D, R).
minway([], [D, R], D, R).
minway([H|T], M, D, R) :-
( H #< M
-> minway(T, H, D, R)
; minway(T, M, D, R)
).
You can do this by using the minimum predicate. Findall can be very helpful.
min([X],X).
min([H|T],Min):-
min(T,TMin),
H>TMin,
Min is TMin.
min([H|T],Min):-
min(T,TMin),
H=<TMin,
Min is H.
minway(List,D,L):-
findall(Value,member([Value,_],List),VList),
min(VList,Min),
D=Min,
findall(EList,member([Min,EList],List),L).
?-minway([[3,[a,b,c,d]],[2,[a,b,d]],[5,[d,e,f]]],D,L).
D = 2,
L = [[a, b, d]]
Try library(aggregate):
?- aggregate_all(min(X,Y),
member([X,Y], [[3,[a,b,c,d]],
[2,[a,b,d]],
[5,[d,e,f]]]),
min(D,L)).
D = 2,
L = [a, b, d].
See also here:
Aggregation operators on backtrackable predicates
https://www.swi-prolog.org/pldoc/man?section=aggregate

Swapping a specific number in list 1 with a specific number in list 2

I have been brushing up on some Prolog recently. I kind of enjoy just coming up with random problems to try and solve and then working them out. This one is quite tough though, and I'm not one to give up on a problem that I have set out to solve.
The problem: I want to make a predicate that will have 2 predetermined lists, 2 numbers to swap, and then output the lists after the swapping is done.
Further Explanation: I made it a little harder on myself by wanting to find a specific unique number from list 1, and swapping this with a specific unique number from list 2 so that if I have 2 lists...
[7,2,7,8,5], and [1,2,3,8,7,9,8], and then give the predicate 2 numbers(Lets just say 8 and 7), then the number 8 and the number 7 will be swapped between the lists IF AND ONLY IF the number 8 is in the first list and the number 7 is in the second list. (It would disregard an 8 in the second list and a 7 in the first list).
Sample query with expected answer:
?- bothSwap([7,2,7,8,5],[1,2,3,8,7,9,8],8,7,X,Y).
X = [7,2,7,7,5], Y = [1,2,3,8,8,9,8].
I kind of got stuck at this point:
bothSwap([],L2,N1,N2,[],L2).
bothSwap(L1,[],N1,N2,L1,[]).
bothSwap([H1|T1],[H2|T2],N1,N2,X,Y) :- H1 == N1, H2 == N2, bothSwap(T1,T2,N1,N2,D1,D2), append(D1,[H2],X), append(D2,[H1],Y).
bothSwap([H1|T1],[H2|T2],N1,N2,X,Y) :- H1 == N1, H2 =\= N2, bothSwap([H1|T1],T2,N1,N2,D1,D2).
bothSwap([H1|T1],[H2|T2],N1,N2,X,Y) :- H1 =\= N1, H2 == N2, bothSwap(T1,[H2|T2],N1,N2,D1,D2).
Any bright minds out there willing to tackle this problem with me? :)
Imagine how easy this problem would be if we could just "wish" for a list to be split up at the occurrence of the desired element, like this:
?- splitsies([1,2,3,4,5,6,7,8], 4, Prefix, Suffix).
Prefix = [1, 2, 3],
Suffix = [5, 6, 7, 8] ;
Guess what? :) append/3 can do that:
% splitsies is true if X splits list into a prefix/suffix pair.
splitsies(List, X, Start, Finish) :-
append(Start, [X|Finish], List).
Now the problem seems pretty simple!
bothSwap(Left, Right, A, B, AfterLeft, AfterRight) :-
% break up the inputs
splitsies(Left, A, LPre, LPost),
splitsies(Right, B, RPre, RPost),
% glue together the outputs (note that A and B are switched)
splitsies(AfterLeft, B, LPre, LPost),
splitsies(AfterRight, A, RPre, RPost).
I wouldn't pretend that this solution is efficient… but it's so hot you better wear oven mitts when you type it in. Oh, and check this out:
?- bothSwap([7,2,7,8,5],[1,2,3,8,7,9,8], X, Y, [7,2,7,7,5], [1,2,3,8,8,9,8]).
X = 8,
Y = 7 ;
false.
Let's start, what you mean by swapping.
swap(X0,X, S0,S) :-
if_(X0 = S0, S = X, S = S0).
bothSwap0(Xs0, Ys0, X0,X, Xs,Ys) :-
maplist(swap(X0,X), Xs0,Xs),
maplist(swap(X,X0), Ys0,Ys).
if_( C_1, Then_0, Else_0) :-
call(C_1, Truth),
functor(Truth,_,0), % safety check
( Truth == true -> Then_0 ; Truth == false, Else_0 ).
=(X, Y, R) :- X == Y, !, R = true.
=(X, Y, R) :- ?=(X, Y), !, R = false. % syntactically different
=(X, Y, R) :- X \= Y, !, R = false. % semantically different
=(X, Y, R) :- R == true, !, X = Y.
=(X, X, true).
=(X, Y, false) :-
dif(X, Y).
Now you wanted a particular condition - it is not clear how to apply it. I see two interpretations:
bothSwap(Xs0, Ys0, X0,X, Xs,Ys) :-
memberd(X0, Xs0),
memberd(X, Ys0),
maplist(swap(X0,X), Xs0,Xs),
maplist(swap(X,X0), Ys0,Ys).
Which means that bothSwap/6 will fail should the two elements not occur in their respective list.
Another interpretation might be that you want that otherwise the lists remain the same. To express this (in a pure monotonic fashion):
bothSwap(Xs0, Ys0, X0,X, Xs,Ys) :-
if_( ( memberd_t(X0, Xs0), memberd_t(X, Ys0) ),
( maplist(swap(X0,X), Xs0,Xs), maplist(swap(X,X0), Ys0,Ys) ),
( Xs0 = Xs, Ys0 = Ys) ).
memberd_t(E, Xs, T) :-
list_memberd(Xs, E, T).
list_memberd([], _, false).
list_memberd([X|Xs], E, T) :-
if_(E = X, T = true, list_memberd(Xs, E, T) ).
','( A_1, B_1, T) :-
if_( A_1, call(B_1, T), T = false ).
Since Prolog is a descriptive language (that is, we describe what constitutes a solution and let Prolog work it out), If I understand your problem statement correctly, something like this ought to suffice:
both_swap(L1, L2, A, B, S1, S2 ) :- % to do the swap,
memberchk(A,L1) , % - L1 must contain an A
memberchk(B,L2) , % - L2 must contain a B
replace(L1,A,B,S1) , % - replace all As in L1 with a B
replace(L2,B,A,S2) % - replace all Bs in L2 with an A
. % Easy!
replace([],_,_,[]) . % if the list is empty, we're done.
replace([H|T],A,B,[S|Ss]) :- % otherwise...
( H = A -> S=B ; S=H ) , % - do the swap (if necessary),
replace(T,A,B,Ss) % - and recurse down
. % Also easy!
This replicates the implementation that uses splitsies/4
swap_two(A,B,C,D,E,F) :-
nth0(I1,A,C,L1),
dif(A,L1),
nth0(I2,B,D,L2),
dif(B,L2),
nth0(I1,E,D,L1),
nth0(I2,F,C,L2).

Prolog program that deletes every n-th element from a list

Could you help me solve the following?
Write a ternary predicate delete_nth that deletes every n-th element from a list.
Sample runs:
?‐ delete_nth([a,b,c,d,e,f],2,L).
L = [a, c, e] ;
false
?‐ delete_nth([a,b,c,d,e,f],1,L).
L = [] ;
false
?‐ delete_nth([a,b,c,d,e,f],0,L).
false
I tried this:
listnum([],0).
listnum([_|L],N) :-
listnum(L,N1),
N is N1+1.
delete_nth([],_,_).
delete_nth([X|L],C,L1) :-
listnum(L,S),
Num is S+1,
( C>0
-> Y is round(Num/C),Y=0
-> delete_nth(L,C,L1)
; delete_nth(L,C,[X|L1])
).
My slightly extravagant variant:
delete_nth(L, N, R) :-
N > 0, % Added to conform "?‐ delete_nth([a,b,c,d,e,f],0,L). false"
( N1 is N - 1, length(Begin, N1), append(Begin, [_|Rest], L) ->
delete_nth(Rest, N, RestNew), append(Begin, RestNew, R)
;
R = L
).
Let's use clpfd! For the sake of versatility and tons of other good reasons:
:- use_module(library(clpfd)).
We define delete_nth/3 based on if_/3 and (#>=)/3:
delete_nth(Xs,N,Ys) :-
N #> 0,
every_tmp_nth_deleted(Xs,0,N,Ys).
every_tmp_nth_deleted([] ,_ ,_,[] ). % internal auxiliary predicate
every_tmp_nth_deleted([X|Xs],N0,N,Ys0) :-
N1 is N0+1,
if_(N1 #>= N,
(N2 = 0, Ys0 = Ys ),
(N2 = N1, Ys0 = [X|Ys])),
every_tmp_nth_deleted(Xs,N2,N,Ys).
Sample query:
?- delete_nth([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],2,Ys).
Ys = [1,3,5,7,9,11,13,15] % succeeds deterministically
Ok, how about something a little more general?
?- delete_nth([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],N,Ys).
N = 1 , Ys = []
; N = 2 , Ys = [1, 3, 5, 7, 9, 11, 13, 15]
; N = 3 , Ys = [1,2, 4,5, 7,8, 10,11, 13,14 ]
; N = 4 , Ys = [1,2,3, 5,6,7, 9,10,11, 13,14,15]
; N = 5 , Ys = [1,2,3,4, 6,7,8,9, 11,12,13,14 ]
; N = 6 , Ys = [1,2,3,4,5, 7,8,9,10,11, 13,14,15]
; N = 7 , Ys = [1,2,3,4,5,6, 8,9,10,11,12,13, 15]
; N = 8 , Ys = [1,2,3,4,5,6,7, 9,10,11,12,13,14,15]
; N = 9 , Ys = [1,2,3,4,5,6,7,8, 10,11,12,13,14,15]
; N = 10 , Ys = [1,2,3,4,5,6,7,8,9, 11,12,13,14,15]
; N = 11 , Ys = [1,2,3,4,5,6,7,8,9,10, 12,13,14,15]
; N = 12 , Ys = [1,2,3,4,5,6,7,8,9,10,11, 13,14,15]
; N = 13 , Ys = [1,2,3,4,5,6,7,8,9,10,11,12, 14,15]
; N = 14 , Ys = [1,2,3,4,5,6,7,8,9,10,11,12,13, 15]
; N = 15 , Ys = [1,2,3,4,5,6,7,8,9,10,11,12,13,14 ]
; N in 16..sup, Ys = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].
Please follow aBathologist instructive answer and explanation (+1). I just post my own bet at solution since there is a problem in ditto solution for ?‐ delete_nth([a,b,c,d,e,f],0,L)..
delete_nth(L,C,R) :-
delete_nth(L,C,1,R).
delete_nth([],_,_,[]).
delete_nth([_|T],C,C,T1) :- !, delete_nth(T,C,1,T1).
delete_nth([H|T],N,C,[H|T1]) :- C<N, C1 is C+1, delete_nth(T,N,C1,T1).
yields
1 ?- delete_nth([a,b,c,d,e,f],2,L).
L = [a, c, e].
2 ?- delete_nth([a,b,c,d,e,f],1,L).
L = [].
3 ?- delete_nth([a,b,c,d,e,f],0,L).
false.
A minor (?) problem: this code is deterministic, while the samples posted apparently are not (you have to input ';' to get a false at end). Removing the cut will yield the same behaviour.
An interesting - imho - one liner variant:
delete_nth(L,C,R) :- findall(E, (nth1(I,L,E),I mod C =\= 0), R).
but the C==0 must be ruled out, to avoid
ERROR: mod/2: Arithmetic: evaluation error: `zero_divisor'
Edited, correcting the mistake pointed out by #CapelliC, where predicate would succeed on N = 0.
I can see where you're headed with your solution, but you needn't bother with so much arithmetic in this case. We can delete the Nth element by counting down from N repeatedly until the list is empty. First, a quick note about style:
If you use spaces, line breaks, and proper placement of parenthesis you can help your readers parse your code. Your last clause is much more readable in this form:
delete_nth([X|L], C, L1):-
listnum(L, S),
Num is S+1,
C>0 -> Y is round(Num/C),
Y=0 -> delete_nth(L, C, L1)
; delete_nth(L, C, [X|L1]).
Viewing your code now, I'm not sure whether you meant to write
( C>0 -> ( Y is round(Num/C),
Y=0 -> delete_nth(L, C, L1) )
; delete_nth(L, C, [X|L1])
).
or if you meant
C>0 -> Y is round(Num/C),
( Y=0 -> delete_nth(L, C, L1)
; delete_nth(L, C, [X|L1])
).
or perhaps you're missing a ; before the second conditional? In any case, I suggest another approach...
This looks like a job for auxiliary predicates!
Often, we only need a simple relationship in order to pose a query, but the computational process necessary to resolve the query and arrive at an answer calls for a more complex relation. These are cases where it is "easier said than done".
My solution to this problem works as follows: In order to delete every nth element, we start at N and count down to 1. Each time we decrement the value from N, we move an element from the original list to the list of elements we're keeping. When we arrive at 1, we discard the element from our original list, and start counting down from N again. As you can see, in order to ask the question "What is the list Kept resulting from dropping every Nth element of List?" we only need three variables. But my answer the question, also requires another variable to track the count-down from N to 1, because each time we take the head off of List, we need to ask "What is the Count?" and once we've reached 1, we need to be able to remember the original value of N.
Thus, the solution I offer relies on an auxiliary, 4-place predicate to do the computation, with a 3-place predicate as the "front end", i.e., as the predicate used for posing the question.
delete_nth(List, N, Kept) :-
N > 0, %% Will fail if N < 0.
delete_nth(List, N, N, Kept), !. %% The first N will be our our counter, the second our target value. I cut because there's only one way to generate `Kept` and we don't need alternate solutions.
delete_nth([], _, _, []). %% An empty list has nothing to delete.
delete_nth([_|Xs], 1, N, Kept) :- %% When counter reaches 1, the head is discarded.
delete_nth(Xs, N, N, Kept). %% Reset the counter to N.
delete_nth([X|Xs], Counter, N, [X|Kept]) :- %% Keep X if counter is still counting down.
NextCount is Counter - 1, %% Decrement the counter.
delete_nth(Xs, NextCount, N, Kept). %% Keep deleting elements from Xs...
Yet another approach, following up on #user3598120 initial impulse to calculate the undesirable Nth elements away and inspired by #Sergey Dymchenko playfulness. It uses exclude/3 to remove all elements at a 1-based index that is multiple of N
delete_nth(List, N, Kept) :-
N > 0,
exclude(index_multiple_of(N, List), List, Kept).
index_multiple_of(N, List, Element) :-
nth1(Index, List, Element),
0 is Index mod N.