Related
A question from someone who knows Prolog for a week=)
I am writing some prolog command infix(Inf,List)
to check if one list is, as our prof formulated, "an infix" of another list. The order matters+ it shouldn't be right at the beginning or the end of the List.
That means for example:
If we have Inf=[1,2] and List=[1,2,3,4] then is is false.
If we have Inf=[3,4] and List=[1,2,3,4] then is is also false.
If we have Inf=[2,3] and List=[1,2,3,4] then it is true.
If it is Inf=[3,2] and List=[1,2,3,4] then is is false.
If it is Inf=[2,4] and List=[1,2,3,4,5] then is is false.
I wrote some rules already and with them I seem to manage to solve the problem of order and not counting the first element of a List.
infix(Inf,List):- length(Inf,L1),length(List,L2), L1>L2, !, fail.
infix(Inf,List):- length(Inf,L1),length(List,L2), L1=L2, !, fail.
infix(Inf,List):- length(Inf,L1),length(List,L2), L1<L2, delete_first(Inf,List).
delete_first(Inf,[_L|List]):- sublist(Inf,List).
sublist([El|Sub],[El|List]):- checksublist(Sub,List).
sublist([S|Sub],[L|List]):- sublist([S|Sub],List).
checksublist([], L).
checksublist([El|Sub],[El|List]):- checksublist(Sub,List).
However, I can't formulate it in a way, so that the last element is not counted =(. According to my intuitive logic the conditionchecksublist([], L).should be smth like checksublist([], [_|[]]). But it doesn't work this way- I get false for everything.
Does anybody know how to get rid of the last element in this situation? Thanks in advance!
Grammars to the rescue! But I would not call this infix. It's a certain subsequence, actually even a certain substring.
infix(Inf, List) :-
Inf = [_|_],
phrase(([_], ..., seq(Inf), [_], ...), List).
% The following are frequently predefined
... --> [] | [_], ... .
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
(Edit) Since you insist that both the prefix and the postfix are non-empty, you probably want this to hold for the infix too. Thus Inf = [_|_] added.
This is the result using Scryer's top level:
?- infix(Inf,"abcdef").
Inf = "b"
; Inf = "bc"
; Inf = "bcd"
; Inf = "bcde"
; Inf = "c"
; Inf = "cd"
; Inf = "cde"
; Inf = "d"
; Inf = "de"
; Inf = "e"
; false.
See this how to print lists of characters as double quoted chars in other systems.
This is too imperative.
You can let Prolog solve it with append/2. It's search-assisted programming, let's use it.
infix(Inf,List) :- append([Prefix,Inf,Suffix],List),Prefix\=[],Suffix\=[].
This is completely declarative, in the sense that
it is formulated as constraint that a solution must fulfill (very mathematical).
Test it using the Unit Test framework:
:- begin_tests(infix).
test(one,[fail]) :- infix([1,2],[1,2,3,4]).
test(two,[fail]) :- infix([3,4],[1,2,3,4]).
test(three) :- infix([2,3],[1,2,3,4]).
test(four,[fail]) :- infix([3,2],[1,2,3,4]).
test(five,[fail]) :- infix([2,4],[1,2,3,4,5]).
:- end_tests(infix).
rt:-run_tests(infix).
Then
?- rt.
% PL-Unit: infix ..
Warning: user://1:12:
PL-Unit: Test three: Test succeeded with choicepoint
.. done
% All 5 tests passed
true.
Sadly, Prolog does not do deep reasoning and theorem proving but employs brute force: it tries possible solutions until one passes or there are no more. Weel, it's sufficient for this case.
For example:
?- append([Prefix,[3,4],Suffix],[1,2,3,4,5,3,4,6]).
Prefix = [1, 2],
Suffix = [5, 3, 4, 6] ;
Prefix = [1, 2, 3, 4, 5],
Suffix = [6] ;
false.
I would like to get this result:
?- numberMatrixLines(1,[[1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1]],X).
X = [machinePenalty(1,[1,1,1,1,1,1,1,1]),
machinePenalty(2 [1,1,1,1,1,1,1,1]),
machinePenalty(3,[1,1,1,1,1,1,1,1])]
I try the following code:
numberMatrixLines(X,[],ResultMatrix):-
writeln('should end here'),
reverse(ResultMatrix,ResultMatrixTemp),
initWith(ResultMatrixTemp,ResultMatrix),
!.
numberMatrixLines(X,[H|T],ResultMatrix):-
append_in_front(machinePenalty(X,H),ResultMatrix,ResultMatrixTemp),
writeln(ResultMatrixTemp),
incr(X,X1),
numberMatrixLines(X1,T,ResultMatrixTemp).
incr(X, X1) :-
X1 is X+1.
append_in_front(X,[],[X]).
append_in_front(X,L1,[X|L1]).
The result is correct when numberMatrixLines(X,[],ResultMatrix) is reached. HOWEVER, the predicate won't stop there and return X , as it's supposed to.
What can be done to make it stop in that line?
A straight-forward solution would be (I moved the input list to the first argument to take advantage of Prolog first-argument indexing to avoid spurious choice-points and the need of cuts):
% number_matrix_lines(+list, +integer, -list)
number_matrix_lines([], _, []).
number_matrix_lines([Line| Lines], I, [machine_penalty(I,Line)| NumberLines]) :-
J is I + 1,
number_matrix_lines(Lines, J, NumberLines).
Sample call:
| ?- number_matrix_lines([[1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1]], 1, NumberLines).
NumberLines = [machine_penalty(1,[1,1,1,1,1,1,1,1]), machine_penalty(2,[1,1,1,1,1,1,1,1]), machine_penalty(3,[1,1,1,1,1,1,1,1])]
yes
P.S. Note that Prolog coding guidelines advise using underscores in predicate names instead of CamelCase.
I am doing a prolog practice which is taken from this.
What I want to do now is to change the input and output way of the program.
I need to execute the program by typing this in the console:
goldbach(100, L).
just for example, and I need to press [;] to show next result when previous one is printed on the screen.
L = [3, 97];
L = [11, 89];
L = ....
However, what I want to make is like below:
Input a number:100.
L = [3, 97].
L = [11, 89].
.....
That is the program will print "Input a number:" first and read your input, then automatically print out all possible result.
I have read sections about read() and write, but I get fail when I add these:
read_gold :-
write('Input a number:'),
read(X),
write(goldbach(X, L)).
How can I fix my code to make the program to achieve the input and output that I want? Thanks for answering.
Something like this will do literally what you're asking for, although it's not normally how one uses Prolog queries and solutions.
read_gold :-
write('Input a number:'),
read(X),
show_results(goldbach(X)).
show_results(Query) :-
call(Query, L),
write('L = '), write(L), write('.'), nl,
fail.
show_results(_).
A cleaner way to collect all of the solutions in one go is to list them using findall/3:
read_gold(Solutions) :-
write('Input a number:'),
read(X),
findall(L, goldbach(X, L), Solutions).
Or, without explicitly prompting:
read_gold(X, Solutions) :-
findall(L, goldbach(X, L), Solutions).
And query it as, for example:
?- read_gold(100, Solutions).
Solutions = [[3, 97], [11,89], ...]
I'm Following Prolog Tutorial 2.1.
Program
adjacent(1, 2).
adjacent(1, 3).
adjacent(1, 4).
main:-
adjacent(1, R),
write(R).
prints 2.
But it supposes to print a list of possible values according to the tutorial:
?- adjacent(1,2).
yes
?- adjacent(1,3).
no
?- adjacent(1,R).
R = 2 ;
R = 3 ;
R = 4 ;
no
I try again in repl only to get the same result:
?- adjacent(1, R).
R = 2 .
How could I get/print a list of possible values of a variable?
In swipl, library(apply) is - by default - autoloaded, so you can write
main:-
forall(adjacent(1, R), (write(R),nl)).
note: Action is a conjuction, just for to illustrate the proper syntax required. For any practical purpose, main :- forall(adjacent(1, R), writeln(R)). could be better.
You need a failure-loop:
adjacent(1, 2).
adjacent(1, 3).
adjacent(1, 4).
main :-
adjacent(1, R),
write(R), nl,
fail.
main.
This is a basic programming technique in Prolog. fail/0 will force backtracking, so next adjacent/2 solution is explored (and so on). Second clause to main/0 prevents the loop itself from failure.
after Prolog prints R = 2;, you can press "r", "n", [TAB] or [SPACE] to show the next results.
I don't know how that works with write(R). but that is not in the code from the tutorial so I think that's supposed to be the trick
This is rather a technical question I think, I am trying to write a program that will find me all sub-sets of size K of the integers 1,2,...,N.
In here I've asked about a sub-set function that I'm using. The fixed version is:
subs(0,[],X).
subs(N,[A|R1],[A|R2]):-
N>0,
N1 is N-1,
subs(N1,R1,R2).
subs(N,[A|R1],[B|R2]):-
N>0,
subs(N,[A|R1],R2).
Later I wrote two functions to help me find the last element in a set and the sub-set of all element except the last (because [A|Rest] means A is the first and Rest is from number 2 to last, but I'd like the opposite - having the last elements and all the elements from the first to the one before the last). The functions are:
lastOf(A,[A]).
lastOf(A,[B|R]):-
lastOf(A,R).
subLast([],[X]).
subLast([A|R1],[A|R2]):-
subLast(R1,R2).
Now I wrote a function that creates a list of the first N natural numbers:
setOf(0,[]).
setOf(N,Nums):-
lastOf(N,Nums),
N>0, N1 is N-1,
subLast(NeoNums,Nums),
setOf(N1, NeoNums).
To combine all the above I have:
choose(K,N,X):-
setOf(N,Y),
subs(K,X,Y).
Running it, for example on 2 and 4, I get:
?-choose(2,4,X).
X = [1, 2] ;
X = [1, 3] ;
X = [1, 4] ;
X = [2, 3] ;
X = [2, 4] ;
X = [3, 4] ;
abort
% Execution Aborted
14 ?- ERROR: Stream user_input:6:143 Syntax error: Unexpected end of clause
These are all the correct outputs, but the problem is that after every time I press enter for a (possible) next answer, I get the next one, apart from the last, in which I have to forcefully abort, as it seems like the programs gets stuck in an infinite loop of some sort.
Can anyone assist?
I'm using SWI-Prolog.
If you're using SWI-Prolog, you can also use clpfd! Here's a clpfd variant of choose/3:
:- use_module(library(clpfd)).
choose(K,N,Zs) :-
length(Zs,K),
Zs ins 1..N,
chain(Zs,#<),
labeling([],Zs).
That's it! And here's the query you gave in the question:
?- choose(2,4,Zs).
Zs = [1,2] ;
Zs = [1,3] ;
Zs = [1,4] ;
Zs = [2,3] ;
Zs = [2,4] ;
Zs = [3,4]. % the goal `choose(2,4,Zs)` terminates
The setOf is the problem here. More specifically - lastOf, which is generating an infinite number of possible lists ending with N. Anyway, setOf can be implemented much easier and in much more readable way (and which is terminating):
setOf(0, []).
setOf(N, [N|T]) :-
N > 0,
N1 is N-1,
setOf(N1, T).
This is if you don't care about the reverse order of the numbers. Otherwise by introducing a helper predicate:
setOf(N, X) :- range(1, N, X).
% range(LowerBound, UpperBound, ResultList)
range(L, L, [L]).
range(L, U, [L|T]) :-
L < U,
L1 is L + 1,
range(L1, U, T).