switch between 1 and 0 in a list - prolog

There is an initial list always consists of '1' (e.g.: [1,1,1], [1,1,1,1]), and the initial list will be given in the question. Then there are some people want to switch the list. The first person will switch every '1' to '0'. The second person follow the first one, and he want to switch the every second number to another(if he meets '0', he switch it to '1';if he meets '1', he switch it to '0'). The third person follow the first one, and he want to switch the every third number to another. Of course, the number of people will be given in the question. Please give the result of final statement of the list.
Write a program 'switch(1,N,Initial,Final). N is the count of people.
For example :
switch(1,2,[1,1],Final). Final=[0,1].
switch(1,3,[1,1,1],Final). Final=[0,1,1].

So we've got a bunch of people who, with nothing better to do in
their lives, want to sequentially switch some numbers in a list. Someone needs to introduce them to Prolog, they could make better use of their time. But this is our initial recursion and base case:
switch(N, N, In, Out) :-
person_switch(N, 1, In, Out), !.
switch(P, N, In, Out) :-
person_switch(P, 1, In, Done),
succ(P, Q),
switch(Q, N, Done, Out).
So we can make our people do their switch sequentially via the first argument, which we increment until we reach the base case.
Next up, we'd better teach these people how to do their jobs of switching 0's and 1's.
person_switch(_, _, [], []). % Base case
person_switch(P, P, [1|In], [0|Out]) :- % switch 1 to a 0 on their turn
person_switch(P, 1, In, Out). % Recurse
person_switch(P, P, [0|In], [1|Out]) :- % switch 0 to a 1 on their turn
person_switch(P, 1, In, Out). % Recurse
person_switch(P, C, [H|In], [H|Out]) :- % don't switch, unify
C < P, % don't not switch when they should, C is a counter along the list
succ(C, D), % increment
person_switch(P, D, In, Out). % Recurse
Good luck learning Prolog.

:- [library(plunit)] .
switch(_one_,_two_,_source_,_target_) :-
switch('induce',_one_,_two_,_source_,_target_,1) .
switch('swap',false,0,0) .
switch('swap',false,1,1) .
switch('swap',true,0,1) .
switch('swap',true,1,0) .
switch('induce',_one_,_two_,_source_,_target_,_nth_) :-
_source_ = [] ,
_target_ = [] ;
_source_ = [_car_|_cdr_] ,
_target_ = [_CAR_|_CDR_] ,
_NTH_ is _nth_ + 1 ,
switch('induce',_one_,_two_,_cdr_,_CDR_,_NTH_) ,
switch('deduce',_one_,_two_,_car_,_CAR_,_nth_) .
switch('deduce',_one_,_two_,_car_,_CAR_,_nth_) :-
_one_ = _nth_ ,
switch('swap',true,_car_,_CAR_) ;
_one_ \= _nth_ ,
_two_ = _nth_ ,
switch('swap',true,_car_,_CAR_) ;
_one_ \= _nth_ ,
_two_ \= _nth_ ,
switch('swap',false,_car_,_CAR_) .
%
:- begin_tests(switch).
test(switch,[nondet,true(Final == [0,0])]) :- switch(1,2,[1,1],Final) .
test(switch,[nondet,true(Final == [0,1,0])]) :- switch(1,3,[1,1,1],Final) .
test(switch,[nondet,true(Final == [0,1,1,0])]) :- switch(1,4,[1,1,1,1],Final) .
test(switch,[nondet,true(Final == [0,1,1,1,0])]) :- switch(1,5,[1,1,1,1,1],Final) .
test(switch,[nondet,true(Final == [1,1,1,1,1])]) :- switch(1,5,[0,1,1,1,0],Final) .
:- end_tests(switch).
%
/*
$ yap -f stackoverflow_switch_list.prolog -g 'run_tests' ;
YAP 6.2.2 (i686-linux): Sat Aug 17 14:01:16 UTC 2019
% PL-Unit: switch ..... done
% All 5 tests passed
yes
?-
*/

Related

Prolog rule which replaces with 0 every negative number from a list

I need to write a rule that replaces every negative number from a list with 0. This is my code:
neg_to_0(L,R) :-
(
nth1(X,L,E),
E<0,
replace(E,0,L,L2),
neg_to_0(L2,R2)
) ;
R = L.
replace(_, _, [], []).
replace(O, R, [O|T], [R|T2]) :- replace(O, R, T, T2).
replace(O, R, [H|T], [H|T2]) :- H \= O, replace(O, R, T, T2).
I have a rule "replace" which takes the element that needs to be replaced with 0 and returns the new list, but it stops after the rule replaces the values and return the new list, so i made the function to recall the main function with the new data so it can replace the other negative values :
replace(E,0,L,L2),
neg_to_0(L2,R2)
);
R = L.
On the last iteration, when it could not detect any negative numbers, i made it so that it saves the last correct list, but i only get back a "True" instead of the correct list.
Your code seems... awfully complex.
You seem to be trying to write procedural (imperative) code. Prolog is not an imperative language: one describes "truth" and lets Prolog's "inference engine" figure it out. And, pretty much everything is recursive by nature in Prolog.
So, for your problem, we have just a few simple cases:
The empty list [], in which case, the transformed list is... the empty list.
A non-empty list. [N|Ns] breaks it up into its head (N) and its tail (Ns). If N < 0, we replace it with 0; otherwise we keep it as-is. And then we recurse down on the tail.
To replace negative numbers in a list with zero, you don't need much more than this:
%
% negatives_to_zero/2 replaces negative numbers with 0
%
negatives_to_zero( [] , [] ) . % nothing to do for the empty list
negatives_to_zero( [N|Ns] , [M|Ms] ) :- % for a non-empty list,
M is max(N,0), % get the max of N and 0,
negatives_to_zero(Ns,Ms). % and recurse down on the tail
You can easily generalize this, of course to clamp numbers or lists of numbers, and constrain them to lie within a specified range:
%--------------------------------------------------------------------------------
% clamp( N , Min, Max, R )
%
% Constrain N such that Min <= N <= Max, returning R
%
% Use -inf (negative infinity) to indicate an open lower limit
% Use +inf (infinity) or +inf (positive infinity) to indicate an open upper limit
% -------------------------------------------------------------------------------
clamp( Ns , -inf , +inf , Ns ) .
clamp( N , Min , Max , R ) :- number(N) , clamp_n(N,Min,Max,R).
clamp( Ns , Min , Max , Rs ) :- listish(Ns) , clamp_l(Ns,Min,Max,Rs).
clamp_n( N , _ , _ , R ) :- \+number(N), !, R = N.
clamp_n( N , Min , Max , R ) :- T is max(N,Min), R is min(T,Max).
clamp_l( [] , _ , _ , [] ) .
clamp_l( [X|Xs] , Min , Max , [Y|Ys] ) :- clamp_n(X,Min,Max,Y), clamp(Xs,Min,Max,Ys).
listish( T ) :- var(T), !, fail.
listish( [] ) .
listish( [_|_] ) .

Is there any way to check whether input n is less than or equal to length of list?

I am new to prolog, I wish to get a function:
drop(N, X, Y) that prints list Y which is the list X with its Nth element removed. If X does not have an Nth element then the predicate should fail.
Example:
1)drop(2,[1,2,3,4,5,6],Y) should give Y=[1,3,4,5,6].
2)drop(8,[1,2,3,4,5,6],Y) should fail.
I tried to get a function that appends an element of X to Y if it is not an Nth element and skips the element if it is an Nth element. Please see the following code:
drop(N,X,Y) :- integer(N),N>0,drop(X,1,N,Y).
drop([], _ , _ , [] ) .
drop( [X1|X] , P , N , [X1|Y] ) :- N=\=P , P1 is P+1 , drop(X,P1,N,Y) .
drop( [_|X] , P , N ,Y) :- N =:= P , P1 is P+1 , drop(X,P1,N,Y) .
The problem arises if N is greater than the length of the list, my code will print the entire list, but the function is supposed to fail in this case. I am not able to find a way to compare N with the length of the list since every function in prolog returns a binary value(according to my knowledge).
Any help will be much appreciated!
You are quite close. There are two things that you should change here:
once we have reached the correct index, we should no longer recurse on drop but just return the rest of the list; and
you should remove the drop([], _, _, []) line, since given we dropped an element, we will no longer recurse (see previous point).
Note that we can each time decrement the value for N and thus prevent using two variables. Like:
drop(N, X, Y) :-
integer(N),
drop_(N, X, Y).
drop_(1, [_|T], T).
drop_(N, [X|T], [X|T2]) :-
N > 1,
N1 is N-1,
drop_(N1, T, T2).

Recursivity | Natural numbers in list swish prolog

i have the next problem,
"return the numbers of natural numbers of an array"
ex. naturales(R,[6,-7,-4,3,2,8]).
R = 4
when a negative numbers appears return false and break my recursivity
naturales(R,[Head|Tail]):-naturales(R1,Tail), Head >= 0, R is R1+1.
naturales(0,[]).
Here is a very short solution :
naturales(In, Out) :-
aggregate(count,X^(member(X, In), X >= 0), Out).
If your predicate really needs to have only 2 arguments, one being the result, R, and the other one the given list, [H|T], you can do something like this. Note that the first predicate calls the second "naturales" with 3 arguments and then, that one starts the recursive process. The C is only a counter where you can add the number of positive elements and then copy that value to the result, in the last line of code. The first line just its just to make sure the empty list returns 0 positive elements. There is probably better ways to do this, this one is probably the most intuitive.
naturales(X, []):- X = 0.
naturales(R, [H|T]):- naturales(R, [H|T], 0).
naturales(R, [H|T], C):- (H > 0, C1 is C + 1, naturales(R1, T, C1), R = R1) ; naturales(R1, T, C), R = R1.
naturales(X, [], X).
A common prolog idiom is the use of a helper predicate with an accumulator (extra) variable. Try something like this:
natural_numbers( Xs, N ) :- natural_numbers( Xs, 0, N ).
natural_numbers( [] , N , N ) .
natural_numbers( [X|Xs] , T , N ) :-
( X > 0 -> T1 is T+1 ; T1 = T ) ,
natural_numbers( Xs, T1, N ).
As others pointed out, the recursive call cannot complete when there are negative numbers. So, you can just patch your program in this way
naturales(R,[Head|Tail]):-naturales(R1,Tail), (Head >= 0, R is R1+1 ; R=R1).
naturales(0,[]).
Now, nearly every Prolog out there (except mine :) implements (->)/2, also know as 'if-then-else'. So, the patch could also be written like
naturales(R,[Head|Tail]):-naturales(R1,Tail), (Head >= 0 -> R is R1+1 ; R=R1).
naturales(0,[]).
Given that naturales/2 is anyway not tail recursive (see #NicholasCarey answer for that), I think it has no practical relevance for you.

Prolog Ending a Recursion

countdown(0, Y).
countdown(X, Y):-
append(Y, X, Y),
Y is Y-1,
countdown(X, Y).
So for this program i am trying to make a countdown program which will take Y a number and count down from say 3 to 0 while adding each number to a list so countdown(3, Y). should produce the result Y=[3,2,1]. I can't seem the end the recursion when i run this and i was wondering if anyone could help me?
I cant seem to get this code to work any help? I seem to be getting out of global stack so I dont understand how to end the recursion.
Your original code
countdown( 0 , Y ) .
countdown( X , Y ) :-
append(Y, X, Y),
Y is Y-1,
countdown(X, Y).
has some problems:
countdown(0,Y). doesn't unify Y with anything.
Y is Y-1 is trying to unify Y with the value of Y-1. In Prolog, variables, once bound to a value, cease to be variable: they become that with which they were unified. So if Y was a numeric value, Y is Y-1 would fail. If Y were a variable, depending on your Prolog implementation, it would either fail or throw an error.
You're never working with lists. You are expecting append(Y,X,Y) to magically produce a list.
A common Prolog idiom is to build lists as you recurse along. The tail of the list is passed along on each recursion and the list itself is incomplete. A complete list is one in which the last item is the atom [], denoting the empty list. While building a list this way, the last item is always a variable and the list won't be complete until the recursion succeeds. So, the simple solution is just to build the list as you recurse down:
countdown( 0 , [] ) . % The special case.
countdown( N , [N|Ns] ) :- % The general case: to count down from N...
N > 0 , % - N must be greater than 0.
N1 is N-1 , % - decrement N
countdown(N1,Ns) % - recurse down, with the original N prepended to the [incomplete] result list.
. % Easy!
You might note that this will succeed for countdown(0,L), producing L = []. You could fix it by changing up the rules a we bit. The special (terminating) case is a little different and the general case enforces a lower bound of N > 1 instead of N > 0.
countdown( 1 , [1] ) .
countdown( N , [N|Ns] ) :-
N > 1 ,
N1 is N-1 ,
countdown(N1,Ns)
.
If you really wanted to use append/3, you could. It introduces another common Prolog idiom: the concept of a helper predicate that carries state and does all the work. It is common for the helper predicate to have the same name as the "public" predicate, with a higher arity. Something like this:
countdown(N,L) :- % to count down from N to 1...
N > 0 , % - N must first be greater than 0,
countdown(N,[],L) % - then, we just invoke the helper with its accumulator seeded as the empty list
. % Easy!
Here, countdown/2 is our "public predicate. It calls countdown/3 to do the work. The additional argument carries the required state. That helper will look like something like this:
countdown( 0 , L , L ) . % once the countdown is complete, unify the accumulator with the result list
countdown( N , T , L ) . % otherwise...
N > 0 , % - if N is greater than 0
N1 is N-1 , % - decrement N
append(T,[N],T1) , % - append N to the accumulator (note that append/3 requires lists)
countdown(N1,T1,L) % - and recurse down.
. %
You might notice that using append/3 like this means that it iterates over the accumulator on each invocation, thus giving you O(N2) performance rather than the desired O(N) performance.
One way to avoid this is to just build the list in reverse order and reverse that at the very end. This requires just a single extra pass over the list, meaning you get O(2N) performance rather than O(N2) performance. That gives you this helper:
countdown( 0 , T , L ) :- % once the countdown is complete,
reverse(T,L) % reverse the accumulator and unify it with the result list
. %
countdown( N , T , L ) :- % otherwise...
N > 0 , % - if N is greater than 0
N1 is N-1 , % - decrement N
append(T,[N],T1) , % - append N to the accumulator (note that append/3 requires lists)
countdown(N1,T1,L) % - and recurse down.
. %
There are several errors in your code:
first clause does not unify Y.
second clause uses append with first and third argument Y, which would only succeed if X=[].
in that clause you are trying to unify Y with another value which will always fail.
Y should be a list (according to your comment) in the head but you are using it to unify an integer.
You might do it this way:
countdown(X, L):-
findall(Y, between(1, X, Y), R),
reverse(R, L).
between/3 will give you every number from 1 to X (backtracking). Therefore findall/3 can collect all the numbers. This will give you ascending order so we reverse/2 it to get the descending order.
If you want to code yourself recursively:
countdown(X, [X|Z]):-
X > 1,
Y is X-1,
countdown(Y, Z).
countdown(1, [1]).
Base case (clause 2) states that number 1 yields a list with item 1.
Recursive clause (first clause) states that if X is greater than 1 then the output list should contain X appended with the result from the recursive call.

Prolog deep version predicate of adding to a list

I have to write a deep version of a predicate that adds a number to each number element in a list and I've done the non-deep version:
addnum(N,T,Y)
this gives something like:
e.g. ?-addnum(7,[n,3,1,g,2],X).
X=[n,10,8,g,9]
but I want to create a deep version of addnum now which should do this:
e.g. ?-addnumdeep(7,[n,[[3]],q,4,c(5),66],C).
X=[n,[[10]],q,11,c(5),73]
Can someone give me some advice? I have started with this:
islist([]).
islist([A|B]) :- islist(B).
addnumdeep(C,[],[]).
addnumdeep(C,[Y|Z],[G|M]):-islist(Z),addnum(C,Y,[G,M]),addnumdeep(C,Z,M).
but I don't think my logic is right. I was thinking along the lines of checking if the tail is a list then runing addnum on the head and then runnig addnumdeep on the rest of the tail which is a list?
maybe you could 'catch' the list in first place, adding as first clause
addnum(N,[T|Ts],[Y|Ys]) :- addnum(N,T,Y),addnum(N,Ts,Ys).
This is one solution. The cut is necessary, or else it would backtrack and give false solutions later on. I had tried to use the old addnum predicate, but you can't know if you have to go deeper afterwards, so it would only be feasible if you have a addnum_3levels_deep predicate and even then it would be clearer to use this solution and count the depth.
addnumdeep(N,[X|Y],[G|H]):-
!, % cut if it is a nonempty list
(number(X)->
G is N + X;
addnumdeep(N,X,G)), % recurse into head
addnumdeep(N,Y,H). % recurse into tail
addnumdeep(_,A,A).
Note that this also allows addnumdeep(7,3,3). if you want it to be addnumdeep(7.3.10), you'll have to extract the condition in the brackets:
addnumdeep(N,[X|Y],[G|H]):-
!, % cut if it is a nonempty list
addnumdeep(N,X,G),
addnumdeep(N,Y,H).
addnumdeep(N,X,Y):-
number(X),!, % cut if it is a number.
Y is N+X.
addnumdeep(_,A,A).
This solution is nicer, because it highlights the three basic cases you might encounter:
It is either a list, then recourse, or a number, for everything else, just put it into the result list's tail (this also handles the empty list case). On the other hand you'll need red cuts for this solution, so it might be frowned upon by some purists.
If you don't want red cuts, you can replace the last clause with
addnumdeep(_,A,A):- !, \+ number(A), \+ A = [_|_].
If you don't want non-lists to be allowed, you could check with is_list if it is a list first and then call the proposed predicate.
I'd start with something that tells me whether a term is list-like or not, something along these lines:
is_list_like( X ) :- var(X) , ! , fail .
is_list_like( [] ) .
is_list_like( [_|_] ) .
Then it's just adding another case to your existing predicate, something like this:
add_num( _ , [] , [] ) . % empty list? all done!
add_num( N , [X|Xs] , [Y|Ys] ) :- % otherwise...
number(X) , % - X is numeric?
Y is X + N , % - increment X and add to result list
add_num( N , Xs , Ys ) % - recurse down
. %
add_num( N , [X|Xs] , [Y|Ys] ) :- % otherwise...
is_list_like( X ) , % - X seems to be a list?
! ,
add_num( N , X , Y ) , % - recurse down on the sublist
add_num( N , Xs , Ys ) % - then recurse down on the remainder
. %
add_num( N , [X|XS] , [Y|Ys] ) :- % otherwise (X is unbound, non-numeric and non-listlike
X = Y , % - add to result list
add_num( N , Xs , Ys ) % - recurse down
. %

Resources