Prolog- repeat input until - prolog

I want to create a predicate that only accepts a specific input from the user and it will keep asking for the right input if the user gives the wrong input.
I've created this, but its not completed because it does not ask for the new input if its wrong:
askchar(X):- write('give char'),nl, get_char(X), test(X).
test(X):- X=a, write('ok'). %accepts a
test(X):- X='1', write('ok'). %accepts 1
test(X):- write('wrong input. try again'),nl.

in systems lacking a decent tail recursive optimization, processing side effects can be conveniently done with a failure driven loop
1 ?- [user].
|: askchar(C) :- repeat, get(C), (C = 0'a ; C = 0'1, ! ; fail).
% user://1 compiled 0.07 sec, 2 clauses
true.
2 ?- askchar(X).
|: 5
|: a
X = 97 .

Here is what I got:
askChar(Char) :- get_char(Char), test(Char), write('This is the right char, thank you.'), !.
askChar(Char) :- write('That is wrong char!'), askChar(Char).
test(s).
It asks again and again until the char s is typed in.

Related

Evaluating functors inside a functor in Prolog

I follow the book Problem solving with Prolog by John Stobo. I've studied the Chapter 1 (Programming with Facts) and Chapter 2 (Programming with Rules) Now I am at Chapter 3: Recursion in Rules and I'm practicing the program given in the Section 3.1. I've elobarated the program a bit (without changing the main structure) and added my own functor (or function or rule ?) named is_rank_lower/2 but it doesn't work as expected.
When I enter (or ask Prolog)
is_rank_lower(ryan, jondoe).
the output is
false.
the expected output: true.
Because ryan is a private and jondoe is a corporal and private is lower in rank than corporal.
The explanations are in the code.
Question #1: How to make my own functor is_lower_rank work as expected?
Question #2: This question might be related to the book because when I write down the program exactly as it is, it works slightly wrongly and that might be causing my own functor to function wrongly, too. Just a guess.
When I enter:
lower_rank(private, corporal).
Prolog returns with true and waits at it, I have to put a dot after the true and click enter only then does it return to the ?- prompt.
The expected output is:
return with true. then return to the ?- prompt
The author seems to talk about this problem. In page 57 he writes "lower_rank would not terminae if the goal ought to fail" I've applied all the instructions but the functor still doesn't work. How to make it work?
My prolog version swi-prolog 7.2.0
% John Stobo, problem solving with Prolog, March.1989
% FACTS:
next_degree(private, corporal).
next_degree(corporal, sergeant).
next_degree(sergeant, lieutenant).
next_degree(lieutenant, captain).
next_degree(captain, major).
next_degree(major, "lieutenant colonel").
next_degree("lieutenant colonel", colonel).
next_degree(colonel, "brigadier general").
next_degree("brigadier general", "major general").
next_degree("major general","lieutenant general").
next_degree("lieutenant general", general).
soldier(ryan, private).
soldier(jondoe, corporal).
sooldier(smartson, captain).
% RULES:
lower_rank(R1, R2) :-
next_degree(R1, R2).
lower_rank(R1, R2) :- % this works but if
next_degree(R1, R3), % the result is "true"
lower_rank(R3, R2). % it doesn't end properly
% only if the user types a dot, it ends properly
is_rank_lower(A1,A2) :-
lower_rank(soldier(A1,X), soldier(A2,X)).
% doesn't work because the functors are inserted as
% 'soldier(ryan, _G1471), soldier(jondoe, _G1471))
% not as private, corporal, i.e. they are not evaluated
That next_degree/2 seems like a bizarre method - is the book suggesting it as sensible, or as an example of what not to do?
There are decent books at https://swi-prolog.discourse.group/t/useful-prolog-references/1089
This works:
% First argument is an atom, hence single quotes in swi-prolog
rank_order(private, 1).
rank_order(corporal, 2).
rank_order(sergeant, 3).
rank_order(lieutenant, 4).
rank_order(captain, 5).
rank_order(major, 6).
rank_order('lieutenant colonel', 7).
rank_order(colonel, 8).
rank_order('brigadier general', 9).
rank_order('major general', 10).
rank_order('lieutenant general', 11).
soldier(ryan, private).
soldier(jondoe, corporal).
% Not mis-spelled as "sooldier"
soldier(smartson, captain).
rank_lower(RankLower, RankUpper) :-
rank_order(RankLower, RankLowerOrder),
rank_order(RankUpper, RankUpperOrder),
RankLowerOrder < RankUpperOrder.
soldier_rank_lower(SoldierLower, SoldierUpper) :-
soldier(SoldierLower, RankLower),
soldier(SoldierUpper, RankUpper),
rank_lower(RankLower, RankUpper).
Results in swi-prolog:
?- rank_lower(private, corporal).
true.
?- soldier_rank_lower(ryan, jondoe).
true.
?- soldier_rank_lower(L, U).
L = ryan,
U = jondoe ;
L = ryan,
U = smartson ;
L = jondoe,
U = smartson ;
false.
2nd attempt:
rank_next(private, corporal).
rank_next(corporal, sergeant).
rank_next(sergeant, lieutenant).
rank_next(lieutenant, captain).
rank_next(captain, major).
rank_next(major, 'lieutenant colonel').
rank_next('lieutenant colonel', colonel).
rank_next(colonel, 'brigadier general').
rank_next('brigadier general', 'major general').
rank_next('major general', 'lieutenant general').
rank_next('lieutenant general', general).
soldier(ryan, private).
soldier(jondoe, corporal).
soldier(smartson, captain).
rank_lower(RankLower, RankUpper) :-
rank_next(RankLower, RankLower1),
% Increase lower to eventually meet with upper
( RankLower1 = RankUpper ;
rank_lower(RankLower1, RankUpper)
).
soldier_rank_lower(SoldierLower, SoldierUpper) :-
soldier(SoldierLower, RankLower),
soldier(SoldierUpper, RankUpper),
% Won't have multiple answers
once(rank_lower(RankLower, RankUpper)).
This makes the following deterministic:
?- soldier_rank_lower(ryan, jondoe).
true.
... whilst keeping the generality of rank_lower/2, i.e.:
?- findall(L-U, rank_lower(L, U), Pairs), length(Pairs, Len).
Pairs = [private-corporal,private-sergeant,private-lieutenant, ...
Len = 66.

How can I inspect WAM code in SICStus Prolog

In the context of hacking clpz on sicstus-prolog I want to glimpse at the warren-abstract-machine code generated by SICStus Prolog.
As an example, let's dissect the following predicate!
is_list([]).
is_list([_|Es]) :- is_list(Es).
Here's what I'm doing now:
Split the 2 clauses of is_list/1 into 2 separate predicates and prepend 2 dummy clauses:
is_list__clause1(dummy1). % dummy clause
is_list__clause1([]).
is_list__clause2(dummy2). % dummy clause
is_list__clause2([_|Es]) :- is_list(Es).
(Ab-)use the SICStus prolog-toplevel like so:
| ?- is_list__clause1(X).
X = dummy1 ? t
…
0x7eff37281300: GET_NIL_X0
0x7eff37281304: PROCEED
0x7eff37281308: END_OF_CLAUSEQ user:is_list__clause1/1
…
| ?- is_list__clause2(X).
X = dummy2 ? t
…
0x7eff37281150: GET_LIST_X0
0x7eff37281154: U2_VOID_XVAR 1,x(0)
0x7eff37281160: EXECUTEQ user:is_list/1
0x7eff37281170: END_OF_CLAUSEQ user:is_list__clause2/1
…
This output—albeit somewhat cryptic—is giving me a feel of what's going on at the WAM level. I like!
But there has got to a simpler way... please help!
There is a simpler way: the undocumented, unsupported, library(disassembler).
You can use this for programmatically getting information about first-argument indexing in addition to the WAM instructions. See the source for more information.
| ?- use_module(library(disassembler)).
% ...
yes
| ?- [user].
% compiling user...
| foo([],Ys,Ys). foo([X|Xs],Ys,[X|Zs]) :- foo(Xs,Ys,Zs). end_of_file.
% compiled user in module user, 89 msec 599696 bytes
yes
| ?- disassemble(foo/3).
% Predicate: user:foo/3 (user)
% Varcase: [4343940512-4343958960,4346212208-4343221120]
% Lstcase: [4346212212]
% Switch: [[]=4343940516], default: failcode
% clause indexed [var,number,atom,structure] (user)
% 4343940512: 'GET_NIL_X0'
% 4343940516: 'GET_X_VALUE_PROCEED'(x(1),x(2))
% 4343940528: 'END_OF_CLAUSEQ'(user:foo/3)
% clause indexed [var,list] (user)
% 4346212208: 'GET_LIST_X0'
% 4346212212: 'U2_XVAR_XVAR'(x(3,0),x(0,0))
% 4346212224: 'GET_LIST'(x(2))
% 4346212232: 'U2_XVAL_XVAR'(x(3),x(2,0))
% 4346212244: 'EXECUTE'(user:foo/3)
% 4346212256: 'END_OF_CLAUSEQ'(user:foo/3)
yes
| ?-

Skip/pass non-standard prolog code

I'm developing under SWI-Prolog, but my target is Erlog (https://github.com/rvirding/erlog). I need a way to use non-standard Prolog syntax.
Is there a way to write prolog code that will be disregarded by the SWI-compiler i.e. make it invisible.
Here is example how does it look like :
do_stuff(G,Amt) :- ecall(erlog_demo:efunc('Elixir.Blah':stuff({G,Amt})).
I was thinking if there is a way for SWI to skip that and I have another declaration that does nothing.
do_stuff(_,_).
One option probably is to comment it and then use parser to remove the comment before running in Erlog, but this seem cumbersome.
Any other ideas.
======
is_dialect(swi) :- catch(current_prolog_flag(dialect, swi), _, fail).
:- if(is_dialect(swi)).
do_stuff(_,_).
:- else.
do_stuff(G,Amt) :- ecall(erlog_demo:efunc('Elixir.Blah':stuff({G,Amt})).
:- endif.
Syntax error: Operator expected
I use this idiom to keep code running in different implementations
:- if(swi).
gen_hash_lin_probe(Key, HashTable, Value) :-
arg(_, HashTable, E),
nonvar(E),
E = Key-Value.
:- elif(yap).
gen_hash_lin_probe(Key, HashTable, Value) :-
HashTable =.. [htlp|Args],
nth1(_, Args, E),
nonvar(E),
E = Key-Value.
:- endif.
where predicates swi/0 or yap/0 are imported from this module(prolog_impl)
:- module(prolog_impl, [swi/0, yap/0, prolog_impl/1]).
swi :- prolog_impl(swi).
yap :- prolog_impl(yap).
prolog_impl(K) :-
F =.. [K,_,_,_,_],
current_prolog_flag(version_data, F).
One closing parenthesis is missing in the "else" branch.
do_stuff(G,Amt) :- ecall(erlog_demo:efunc('Elixir.Blah':stuff({G,Amt})). % BAD
do_stuff(G,Amt) :- ecall(erlog_demo:efunc('Elixir.Blah':stuff({G,Amt}))). % OK!
Simply count the number of opening parentheses. When . (period, full-step) is reached the difference count of opening vs closing must amount to exactly zero.
Hope that helps!
This answer is based, in part, on
#Capelli's answer and your comments on his candidate solution.
We propose a different way. Follow the dots step-by-step...
For a start, we take the following simplified and shortened snippet.
:- if(true). p(3).
:- elif(false). p(4+3).
:- endif.
In above sample we can see that the "else" branch is never taken, ever.
So... could we, in principle, put arbitrary text and binary data there?
:- if(true). p(3).
:- elif(false). p (4);;[ p())
:- endif.
нет! Upon (re-)loading, we get: ⚠ Syntax error: Operator expected
TIL that all sections between elif and endif must be valid Prolog text.
Let's try something different which was inspired by that phrases you used, in particular: "[...] use non-standard Prolog syntax [...] code that will be disregarded [...] make it invisible [...]"
To me, the phrase "non-standard syntax" indicates that new operator(s) might be introduced (or old ones redefined):
:- op(500,xfx,=>).
:- if(true). p(2).
:- elif(false). p(3 => 3).
:- endif.
Consider the following variation:
:- if(true). p(2).
:- elif(false). :- op(500,xfx,=>). % is moving the `op/3` here ok?
p(3 => 3).
:- endif.
нет! Upon (re-)loading, we get: ⚠ Unterminated conditional compilation from [...]
There is another way! We can proceed by inserting a special-purpose end_of_file fact to tell the Prolog text reader to disregard everything after end_of_file.
We use it like this:
% snip_at_end.pl
xxx1.
xxx2.
end_of_file.
xxx3.
:- op (500, xfx,eat). % broken syntax (pt.1)
1 ]][[ v % broken syntax (pt.2)
Simple test using SICStus Prolog:
$ sicstus
SICStus 4.3.2 (x86_64-linux-glibc2.12): Fri May 8 01:05:09 PDT 2015
[... License information ...]
| ?- compile(snip_at_end).
% compiling /home/stefan/prolog/snip_at_end.pl...
% compiled /home/stefan/prolog/snip_at_end.pl in module user, 40 msec 400672 bytes
yes
| ?- xxx1.
yes
| ?- xxx2.
yes
| ?- xxx3.
! Existence error in user:xxx3/0
! procedure user:xxx3/0 does not exist
! goal: user:xxx3
Hope this helps! I'm curious / interested if my answer fits your problem:)

Prolog: cut predicate and semicolon together to obtain TRUE

I'm new to Prolog and I'm facing some exercises, this one for example ask me to write a predicate histogram/1 to take a list as argument and print an horizontal histogram with the occurrences of numbers 0..9 in the list. For example:
?- histogram([1,3,4,1,1,4,7]).
0
1 ***
2
3 *
4 **
5
6
7 *
8
9
true.
My program is the following, and works as requested, but I've a doubt: as you can see I used cut predicate and semicolon in that way (first line), only to obtain TRUE after the histogram (just to fit the example provided, I don't really care about it!), if you omit this you'll get always FALSE as N<10 will fail...
My question is: is this a proper way to use the cut predicate? If not, how could I improve my program (any other suggestion will be appreciated)?
histogram(X):-
valid(X),
( print(0,X)
; !
).
print(N,X):-
write(N) , tab(2), count(N,X) , nl,
N1 is N+1,
N1 < 10 ,
print(N1,X).
count(_,[]).
count(E,[E|C]):-
write('*') ,
count(E,C).
count(E,[Y|C]):-
E\=Y ,
count(E,C).
valid([]).
valid([X|T]):-
integer(X),
X>=0,
X<10,
valid(T).
maybe
print(N,X):- N < 10, write(N), tab(2), count(N,X), nl, N1 is N+1, print(N1,X).
print(10,_).
and the whole program can be made much shorter using SWI-Prolog library, for instance
histogram(X) :-
forall(between(0,9,N), (findall(*,member(N,X),H), format('~d ~s~n', [N, H]))).

How to get two values from the user by using SWI prolog

Im working with SWI prolog to ask the user to insert two different values according to the following codes:
base:-
write('\n Please enter the base and exponent or 0 and 0 to stop the program:'),
read (X),
read(Y),
bas(X,Y).
bas(0,0):- !.
bas(X,Y):-
f is X*Y,
write('The power of '),write(x),
write(' raised to '),write(y),
write(' is '),write(f),
base.
but after i do the consult the first line will display (insert The number) but when I insert the values it show for me error.
So what is the mistake and why the program cant read my different values
here a slight reformulation
base :-
writeln('enter base and exponent (terminate with .) or 0 and 0 to stop the program'),
read(X),
read(Y),
bas(X,Y).
bas(0,0):- !.
bas(X,Y):-
P is X ^ Y,
format('The power of ~w raised to ~w is ~w~n', [X,Y,P]),
base.
to me, such interaction seems a bit weird:
?- base.
enter base and exponent (terminate with .) or 0 and 0 to stop the program
|: 4.
|: 2.
The power of 4 raised to 2 is 16
enter base and exponent (terminate with .) or 0 and 0 to stop the program
|: 0.
|: 0.
true.

Resources