So basically I am trying to simulate some C code in Prolog.
It is easy to simulate while loop in Prolog Here is the case:
C code:
int a = 1;
From the Logtalk standard library:
:- meta_predicate(whiledo(0, 0)).
whiledo(Condition, Action) :-
( call(Condition) ->
\+ \+ call(Action),
whiledo(Condition, Action)
; true
).
For other traditional loop constructs see: https://github.com/LogtalkDotOrg/logtalk3/blob/master/library/loopp.lgt https://github.com/LogtalkDotOrg/logtalk3/blob/master/library/loop.lgt
In order to implement while-loops in Prolog, I wrote a small interpreter for an imperative language.
Using this interpreter, a while-loop can be written like this:
:- initialization(main).
:- set_prolog_flag('double_quotes','chars').
main :-
imperative_statements((
A=1,
N=0,
while(A<50,(
A=A+2,
N=N-1
))
D = increment_var(1)
)),
writeln(A),
writeln(N),
writeln(D).
increment_var(A,B) :-
imperative_statements((
B=A,
while(B<50,(
B=B*2
))
)).
print(A,true) :-
writeln(A).
imperative_statements(A) :-
term_variables(A,Names),
length(Names,L),
length(Input,L),
length(Output,L),
set_var(A,Names,Input,Output),
Names=Output.
set_var(A,Names,Input) :-
length(Input,L),
length(Output,L),
set_var(A,Names,Input,Output),
Names=Output.
set_var((A,B),Var_names,Input,Output) :-
set_var(A,Var_names,Input,Output1),
set_var(B,Var_names,Output1,Output).
set_var(Name=Value,Var_names,Input,Output) :-
get_var(Value,[Var_names,Input],Value1),
set_var_(Name=Value1,Var_names,Input,Output).
set_var_(_,[],[],[]).
set_var_(Name=Value1,[Name1|Name2],[Var1|Var2],[Output1|Output2]) :-
(Name==Name1,Output1=Value1;
Name \== Name1,Output1=Var1),
set_var_(Name=Value1,Name2,Var2,Output2).
set_var(while(A,B),Names,Vars,Result) :-
get_var(A,[Names,Vars],A1),
(((A1==true)->(set_var(B,Names,Vars,Result1),set_var(while(A,B),Names,Result1,Result)));
A1==false,Vars=Result).
get_var(Name,[[Name1],[Var1]],Output) :-
var(Name),Name==Name1,
Output=Var1.
get_var(Name,[[Name1|Name2],[Var1|Var2]],Output) :-
var(Name),(Name==Name1,get_var(Name,[[Name1],[Var1]],Output);
Name \== Name1,get_var(Name,[Name2,Var2],Output)).
get_var([],_,[]).
get_var([Name1|Name2],Vars,[Name3|Name4]) :-
get_var(Name1,Vars,Name3),
get_var(Name2,Vars,Name4).
get_var(Name,_,Name) :-
number(Name);atom(Name).
get_var(A+B,Vars,Output) :-
get_var(A,Vars,A1),
get_var(B,Vars,B1),
Output is A1+B1.
get_var(A-B,Vars,Output) :-
get_var(A,Vars,A1),
get_var(B,Vars,B1),
Output is A1-B1.
get_var(A*B,Vars,Output) :-
get_var(A,Vars,A1),
get_var(B,Vars,B1),
Output is A1*B1.
get_var(A/B,Vars,Output) :-
get_var(A,Vars,A1),
get_var(B,Vars,B1),
Output is A1/B1.
get_var(A**B,Vars,Output) :-
get_var(A,Vars,A1),
get_var(B,Vars,B1),
Output is A1**B1.
get_var((A,B),Vars,Result) :-
get_var([A,B],Vars,[A1,B1]),
(A1,B1,Result=true;([A1,B1]=[true,false];[A1,B1]=[false,true]),Result=false).
get_var((A;B),Vars,Result) :-
(get_var(A,Vars,A1),call(A1);
get_var(B,Vars,B1),call(B1)) -> (Result = true);
(get_var(A,Vars,A1),A1=false,Result=false).
get_var(A<B,Vars,Result) :-
get_var(B>A,Vars,Result).
get_var(A>B,Vars,Result) :-
%comparison of variables
get_var([A,B],Vars,[A1,B1]),
(A1>B1,Result=true;A1<B1,Result=false).
get_var(A==B,Vars,Result) :-
%comparison of variables
get_var([A,B],Vars,[A1,B1]),
(A1==B1,Result=true;A1\=B1,Result=false).
get_var(Input,Vars,Output1) :-
(\+number(Input)),
Input =.. [Name|Params],
\+member(Name,['=',==,'->',not,'[|]',',',';',+,-,*,/,**,^,writeln]),
length(Params,Params_length),
Params_length > 0,
get_var(Params,Vars,Params1),
append([Name|Params1],[Output1],Input0),
Input1 =.. Input0,
call(Input1).
The most obvious way to do an infinite loop in Prolog is with repeat/0
, and it looks like this:
while(1)
do_something();
becomes
repeat,
do_something.
The real problem then becomes that there's no obvious analog to goto
or break
in Prolog. So I would be inclined to look for a pattern like this:
while(1) {
if (test)
break;
do_something();
}
and convert it to Prolog like this:
loop_entry :-
test,
do_something,
loop_entry.
You will of course need to add your local variables as parameters to loop_entry/0
and implement test
, but this way when test
fails the loop will end naturally.
Following your example with N and A leads to this kind of thing:
loop_entry(N, A) :-
N > 0,
succ(N0, N),
succ(A, A1),
loop_entry(N0, A1).
The "test" in this case is simply N > 0
. If it isn't true, the predicate will simply fail and you can go on with life the Prolog way.
Edit #2. If you want the results (N and A) then add additional parameters for the values you want to return and add one more clause:
loop_entry(N, A, ResultN, ResultA) :-
N > 0, !,
succ(N0, N),
succ(A, A1),
loop_entry(N0, A1, ResultN, ResultA).
loop_entry(N, A, N, A).
You can either add a cut after the condition or put the inverse condition in this new clause.
Not very different from your prolog_while predicate:
prolog_while(N, A) :-
( N==0 ->
true
;
N1 is N -1,
A1 is A + 1,
prolog_while(N1, A1)
).
But most likely you want the final value of A be available to the caller of this predicate, so you have to return it via an additional argument:
prolog_while(N, A, AFinal) :-
( N==0 ->
AFinal = A
;
N1 is N -1,
A1 is A + 1,
prolog_while(N1, A1, AFinal)
).