I am writing a program in prolog that should interact with the user. I have a database of bands that I like and that I dislike, and I can ask prolog about these bands. First I have to say
hello.
to prolog, and prolog answers hello and I can start asking questions like
do you like the band motorhead?
and prolog should answer
yes i like the band because it is rock.
Then I should be able to ask another question.
My initial idea to achieve this was to get the last word of the question, check if it is in one of the two list (liked-list or disliked-list), and then recursively call the function to interact with the program.
In fact my code works well, except for one annoying detail that I can't solve. Here is my problem:
?- hello.
hello!
Ask your question: do you like the band motorhead?
I like the band because it is metal.
Ask your question: I don't know that band.
Ask your question: do you like the band motorhead
I like the band because it is metal.
In fact when I add a question mark at the end of the question, prolog answers the question (no problem here), recursively call the function to ask questions, and then adds "I don't know that band" and calls once again the function to ask questions instead of calling it once, and waiting for me to type another question.
Here is my current code for interacting with prolog
hello :- write('hello!'),nl, ask.
ask :- write('Ask your question: '),
getsentence(X), last(Word, X),
process(Word).
process(stop) :- !.
process(hello) :- hello, !.
process(X) :-
(like-list(LikeList), member(X, LikeList), type(X, Style),
write('I like the band because it is '), write(Style), write('.'), nl
;
dislike-list(DisLikeList), member(X, DisLikeList),
write('I don\'t like that band.'), nl
;
write('I don\'t know that band.'), nl),
ask.
And here is my current code for parsing what the user types:
getsentence( Wordlist) :-
get0( Char),
getrest( Char, Wordlist).
getrest( 46, [] ) :- !. % End of sentence: 46 = ASCII for '.'
getrest( 63, [] ) :- !. % 63 = ASCII for '?'
getrest( 10, [] ) :- !. % 10 = ASCII for '\n'
getrest( 13, [] ) :- !. % 13 = ASCII for 'CR'
getrest( 32, Wordlist) :- !, % 32 = ASCII for blank
getsentence( Wordlist). % Skip the blank
getrest( Letter, [Word | Wordlist] ) :-
getletters( Letter, Letters, Nextchar), % Read letters of current word
name( Word, Letters),
getrest( Nextchar, Wordlist).
getletters( 46, [], 46) :- !. % End of word: 46 = full stop
getletters( 63, [], 63) :- !.
getletters( 10, [], 63) :- !.
getletters( 13, [], 63) :- !.
getletters( 32, [], 32) :- !. % End of word: 32 = blank
getletters( Let, [Let | Letters], Nextchar) :-
get0( Char),
getletters( Char, Letters, Nextchar).
last(Item, List) :- append(_, [Item], List),!.
Ok I managed to solve my problem by taking a different approach.
I hope this will be of any use to anyone.
hello :-
write_ln('Robot: Hello!'),
ask.
/* read_line_to_codes converts what the user typed to ASCII codes, and then
* with atom_codes, I reconstruct the question from the list of ASCII codes.
*/
ask :-
write('Me: '),
read_line_to_codes(user_input, Codes),
atom_codes(X, Codes),
process(X), !.
process(stop) :- !.
process(hello) :- hello.
process(X) :-
((sub_atom(X, _, _, _, 'do you like the band '),
like-list(List),
searchForLikes(X, List, Band, Style),
write('Robot: '),
write('I like '),
write(Band),
write(' because it is '),
write(Style), write_ln('.')
);
(sub_atom(X, _, _, _, 'do you like the band '),
dislike-list(List),
searchForDislikes(X, List, Band, Style),
write('Robot: '),
write('I don\'t like '),
write(Band),
write(' because it is '),
write(Style), write_ln('.')
);
(X=='what bands do you like?',
like-list(LikeList),
write_ln(LikeList)
);
(X=='what bands don\'t you like?',
dislike-list(DislikeList),
write_ln(DislikeList)
);
(sub_atom(X, _, _, _, 'do you like the band '),
write('Robot: '),
write_ln('I don\'t know that band.')
);
(write('Robot: '),
write_ln('I don\'t understand the question.'))
),
ask.
/*
* term_to_atom transforms for example "ac-dc" to "'ac-dc'" in order to look for that particular atom in the String.
* The lookup is done with the function sub_atom (it searches the Band in the string X).
*/
searchForLikes(_, [], _) :- !, fail.
searchForLikes(X, [Head|Tail], Band, Style) :-
(term_to_atom(Head, Band),
sub_atom(X, _, _, _, Band),
type(Head, Style),
!);
searchForLikes(X,Tail, Band, Style).
searchForDislikes(_, [], _) :- !, fail.
searchForDislikes(X, [Head|Tail], Band, Style) :-
(term_to_atom(Head, Band),
sub_atom(X, _, _, _, Band),
type(Head, Style),
!);
searchForDislikes(X,Tail, Band, Style).
The main difference of this code with the first one is the use of built in functions.
Related
This is the current code I have for a problem I am working on. It is supposed to read in from a file, and increment a counter, R, every time it comes across a vowel.
Currently, I have it stop when reaching a vowel, but I would like it to increment a counter, then continue processing. Once done, I want it to print R to the console. Thanks in advance!
readWord(InStream, W) :-
get0(InStream,Char),
checkChar_readRest(Char,Chars,InStream, R),
atom_codes(Code,Chars),
write(Code).
%checkChar_readRest(10,[],_) :- !. % Return
%checkChar_readRest(32,[],_) :- !. % Space
checkChar_readRest(-1,[],_,_) :- !. % End of Stream
checkChar_readRest(97,[],_,R) :- !. % a
checkChar_readRest(101,[],_,R) :- !. % e
checkChar_readRest(105,[],_,R) :- !. % i
checkChar_readRest(111,[],_,R) :- incr(R,R1), write(R1). % o
checkChar_readRest(117,[],_,R) :- !. % u
%checkChar_readRest(end_of_file,[],_,_) :- !.
checkChar_readRest(Char,[Char|Chars],InStream,R) :-
get0(InStream,NextChar),
checkChar_readRest(NextChar,Chars,InStream,R).
incr(X, X1) :- X1 is X+1.
vowel(InStream, R) :-
open(InStream, read, In),
repeat,
readWord(In, W),
close(In).
Here's my attempt (ISO predicates):
% open Src, count vowels, close stream, print to console
count_vowels_in(Src) :-
open(Src, read, Stream),
count(Stream, Total),
close(Stream),
% cutting here because our Stream is now closed, any backtracking will break things that rely on it
% you could also put this after at_end_of_stream/1 in count_/3
% not sure what best practices are here
!,
write(Total).
% just a nice wrapper to get started counting with the initial count set to 0
count(Stream, Total) :-
count_(Stream, 0, Total).
% at end of stream, Count = Total and we're done
count_(Stream, Count, Count) :-
at_end_of_stream(Stream).
% read from stream recursively, incrementing Count as needed
count_(Stream, Count0, Total) :-
\+at_end_of_stream(Stream),
get_char(Stream, Char),
char_value(Char, Value),
Count1 is Count0 + Value,
% recursively call count_, but now with our new Count1 value instead, carrying forward the results
count_(Stream, Count1, Total).
char_value(Char, 1) :-
vowel(Char).
char_value(Char, 0) :-
\+vowel(Char).
vowel(a).
vowel(e).
vowel(i).
vowel(o).
vowel(u).
The biggest difference is that I use two variables for keeping track of the count. Count (equivalent to your R) is the current count, and Total is a variable representing the final count. We unify Total with Count when we are finished counting: at the end of the stream.
In the original program posted, there were many singleton variables (variables that are never unified with anything, for example W). This is usually indicative of a bug and will generate warnings. Remember that Prolog is a logical language, it can be good to take a step back and think "what am I actually trying to do with these variables?". It can also help to break the problem down into smaller chunks instead of trying to write one predicate that does everything.
I might approach it like this:
Slurp in the entire file as a list of characters.
Traverse that list and tally the vowels it contains.
Write the tally.
Something like
findall(C, ( get0(V) , V \= -1 , char_code(C,V) ), Cs).
should suffice for slurping the text.
And then, something along these lines:
count_vowels :-
findall(C, ( get0(V) , V \= -1 , char_code(C,V) ), Cs),
count_vowels(Cs,N),
writeln( total_vowels : N )
.
count_vowels( S , N ) :- string(S), !, string_chars(S,Cs), count_vowels(Cs,N) .
count_vowels( Cs , N ) :- count_vowels(Cs,0,N) .
count_vowels( [] , N , N ) .
count_vowels( [C|Cs] , T , N ) :- tally(C,T,T1), count_vowels(Cs,T1,N).
tally( C , M , N ) :- vowel(C), !, N is M+1 .
tally( _ , N , N ) .
vowel( a ).
vowel( e ).
vowel( i ).
vowel( o ).
vowel( u ).
To learn some Prolog (I'm using GNU Prolog) and grok its parsing abilities, I am starting by writing a Lisp (or S-expression, if I'm being exact) tokenizer, which given a set of tokens like ['(', 'f', 'o', 'o', ')'] should produce ['(', 'foo', ')']. It's not working as expected, which is why I'm here! I thought my thought process shined through in my pseudocode:
tokenize([current | rest], buffer, tokens):
if current is '(' or ')',
Tokenize the rest,
And the output will be the current token buffer,
Plus the parenthesis and the rest.
if current is ' ',
Tokenize the rest with a clean buffer,
And the output will be the buffer plus the rest.
if the tail is empty,
The output will be a one-element list containing the buffer.
otherwise,
Add the current character to the buffer,
And the output will be the rest tokenized, with a bigger buffer.
I translated that to Prolog like this:
tokenize([Char | Chars], Buffer, Tokens) :-
((Char = '(' ; Char = ')') ->
tokenize(Chars, '', Tail_Tokens),
Tokens is [Buffer, Char | Tail_Tokens];
Char = ' ' ->
tokenize(Chars, '', Tail_Tokens),
Tokens is [Buffer | Tail_Tokens];
Chars = [] -> Tokens is [Buffer];
atom_concat(Buffer, Char, New_Buffer),
tokenize(Chars, New_Buffer, Tokens)).
print_tokens([]) :- write('.').
print_tokens([T | N]) :- write(T), write(', '), print_tokens(N).
main :-
% tokenize(['(', 'f', 'o', 'o', '(', 'b', 'a', 'r', ')', 'b', 'a', 'z', ')'], '', Tokens),
tokenize(['(', 'f', 'o', 'o', ')'], '', Tokens),
print_tokens(Tokens).
When running the result, below, like this: gprolog --consult-file lisp_parser.pl it just tells me no. I traced main, and it gave me the stack trace below. I do not understand why tokenize fails for the empty case. I see that the buffer is empty since it was cleared with the previous ')', but even if Tokens is empty at that point in time, wouldn't Tokens accumulate a larger result recursively? Can someone who is good with Prolog give me a few tips here?
| ?- main.
no
| ?- trace.
The debugger will first creep -- showing everything (trace)
(1 ms) yes
{trace}
| ?- main.
1 1 Call: main ?
2 2 Call: tokenize(['(',f,o,o,')'],'',_353) ?
3 3 Call: tokenize([f,o,o,')'],'',_378) ?
4 4 Call: atom_concat('',f,_403) ?
4 4 Exit: atom_concat('',f,f) ?
5 4 Call: tokenize([o,o,')'],f,_429) ?
6 5 Call: atom_concat(f,o,_454) ?
6 5 Exit: atom_concat(f,o,fo) ?
7 5 Call: tokenize([o,')'],fo,_480) ?
8 6 Call: atom_concat(fo,o,_505) ?
8 6 Exit: atom_concat(fo,o,foo) ?
9 6 Call: tokenize([')'],foo,_531) ?
10 7 Call: tokenize([],'',_556) ?
10 7 Fail: tokenize([],'',_544) ?
9 6 Fail: tokenize([')'],foo,_519) ?
7 5 Fail: tokenize([o,')'],fo,_468) ?
5 4 Fail: tokenize([o,o,')'],f,_417) ?
3 3 Fail: tokenize([f,o,o,')'],'',_366) ?
2 2 Fail: tokenize(['(',f,o,o,')'],'',_341) ?
1 1 Fail: main ?
(1 ms) no
{trace}
| ?-
How about this. I think that's what you want to do, but let's use Definite Clause Grammars (which are just horn clauses with :- replaced by --> and two elided arguments holding the input character list and remaining character list. An example DCG rule:
rule(X) --> [c], another_rule(X), {predicate(X)}.
List processing rule rule//1 says: When you find character c in the input list, then continue list processing with another_rule//1, and when that worked out, call predicate(X) as normal.
Then:
% If we encounter a separator symbol '(' or ')', we commit to the
% clause using '!' (no point trying anything else, in particular
% not the clause for "other characters", tokenize the rest of the list,
% and when we have done that decide whether 'MaybeToken', which is
% "part of the leftmost token after '(' or ')'", should be retained.
% it is dropped if it is empty. The caller is then given an empty
% "part of the leftmost token" and the list of tokens, with '(' or ')'
% prepended: "tokenize('', [ '(' | MoreTokens] ) -->"
tokenize('', [ '(' | MoreTokens] ) -->
['('],
!,
tokenize(MaybeToken,Tokens),
{drop_empty(MaybeToken,Tokens,MoreTokens)}.
tokenize('',[')'|MoreTokens]) -->
[')'],
!,
tokenize(MaybeToken,Tokens),
{drop_empty(MaybeToken,Tokens,MoreTokens)}.
% No more characters in the input list (that's what '--> []' says).
% We succeed, with an empty token list and an empty buffer fro the
% leftmost token.
tokenize('',[]) --> [].
% If we find a 'Ch' that is not '(' or ')', then tokenize
% more of the list via 'tokenize(MaybeToken,Tokens)'. On
% returns 'MaybeToken' is a piece of the leftmost token found
% in that list, so we have to stick 'Ch' onto its start.
tokenize(LargerMaybeToken,Tokens) -->
[Ch],
tokenize(MaybeToken,Tokens),
{atom_concat(Ch,MaybeToken,LargerMaybeToken)}.
% ---
% This drops an empty "MaybeToken". If "MaybeToken" is
% *not* empty, it is actually a token and prepended to the list "Tokens"
% ---
drop_empty('',Tokens,Tokens) :- !.
drop_empty(MaybeToken,Tokens,[MaybeToken|Tokens]).
% -----------------
% Call the DCG using phrase/2
% -----------------
tokenize(Text,Result) :-
phrase( tokenize(MaybeToken,Tokens), Text ),
drop_empty(MaybeToken,Tokens,Result),!.
And so:
?- tokenize([h,e,l,l,o],R).
R = [hello].
?- tokenize([h,e,l,'(',l,')',o],R).
R = [hel,(,l,),o].
?- tokenize([h,e,l,'(',l,l,')',o],R).
R = [hel,(,ll,),o].
I think in GNU Prolog, the notation `hello` generates [h,e,l,l,o] directly.
I do not understand why tokenize fails for the empty case.
The reason anything fails in Prolog is because there is no clause that makes it true. If your only clause for tokenize is of the form tokenize([Char | Chars], ...), then no call of the form tokenize([], ...) will ever be able to match this clause, and since there are no other clauses, the call will fail.
So you need to add such a clause. But first:
:- set_prolog_flag(double_quotes, chars).
This allows you to write ['(', f, o, o, ')'] as "foo".
Also, you must plan for the case where the input is completely empty, or other cases where you must maybe emit a token for the buffer, but only if it is not '' (since there should be no '' tokens littering the result).
finish_buffer(Tokens, Buffer, TokensMaybeWithBuffer) :-
( Buffer = ''
-> TokensMaybeWithBuffer = Tokens
; TokensMaybeWithBuffer = [Buffer | Tokens] ).
For example:
?- finish_buffer(MyTokens, '', TokensMaybeWithBuffer).
MyTokens = TokensMaybeWithBuffer.
?- finish_buffer(MyTokens, 'foo', TokensMaybeWithBuffer).
TokensMaybeWithBuffer = [foo|MyTokens].
Note that you can prepend the buffer to the list of tokens, even if you don't yet know what that list of tokens is! This is the power of logical variables. The rest of the code uses this technique as well.
So, the case for the empty input:
tokenize([], Buffer, Tokens) :-
finish_buffer([], Buffer, Tokens).
For example:
?- tokenize([], '', Tokens).
Tokens = [].
?- tokenize([], 'foo', Tokens).
Tokens = [foo].
And the remaining cases:
tokenize([Parenthesis | Chars], Buffer, TokensWithParenthesis) :-
( Parenthesis = '('
; Parenthesis = ')' ),
finish_buffer([Parenthesis | Tokens], Buffer, TokensWithParenthesis),
tokenize(Chars, '', Tokens).
tokenize([' ' | Chars], Buffer, TokensWithBuffer) :-
finish_buffer(Tokens, Buffer, TokensWithBuffer),
tokenize(Chars, '', Tokens).
tokenize([Char | Chars], Buffer, Tokens) :-
Char \= '(',
Char \= ')',
Char \= ' ',
atom_concat(Buffer, Char, NewBuffer),
tokenize(Chars, NewBuffer, Tokens).
Note how I used separate clauses for the separate cases. This makes the code more readable, but it does have the drawback compared to (... -> ... ; ...) that the last clause must exclude characters handled by previous clauses. Once you have your code in this shape, and you're happy that it works, you can transform it into a form using (... -> ... ; ...) if you really want to.
Examples:
?- tokenize("(foo)", '', Tokens).
Tokens = ['(', foo, ')'] ;
false.
?- tokenize(" (foo)", '', Tokens).
Tokens = ['(', foo, ')'] ;
false.
?- tokenize("(foo(bar)baz)", '', Tokens).
Tokens = ['(', foo, '(', bar, ')', baz, ')'] ;
false.
Finally, and very importantly, the is operator is meant only for evaluation of arithmetic expressions. It will throw an exception when you apply it to anything that is not arithmetic. Unification is different from the evaluation of arithmetic expression. Unification is written as =.
?- X is 2 + 2.
X = 4.
?- X = 2 + 2.
X = 2+2.
?- X is [a, b, c].
ERROR: Arithmetic: `[a,b,c]' is not a function
ERROR: In:
ERROR: [20] throw(error(type_error(evaluable,...),_3362))
ERROR: [17] arithmetic:expand_function([a,b|...],_3400,_3402) at /usr/lib/swi-prolog/library/arithmetic.pl:175
ERROR: [16] arithmetic:math_goal_expansion(_3450 is [a|...],_3446) at /usr/lib/swi-prolog/library/arithmetic.pl:147
ERROR: [14] '$expand':call_goal_expansion([system- ...],_3512 is [a|...],_3492,_3494,_3496) at /usr/lib/swi-prolog/boot/expand.pl:863
ERROR: [13] '$expand':expand_goal(_3566 is [a|...],_3552,_3554,_3556,user,[system- ...],_3562) at /usr/lib/swi-prolog/boot/expand.pl:524
ERROR: [12] setup_call_catcher_cleanup('$expand':'$set_source_module'(user,user),'$expand':expand_goal(...,_3640,_3642,_3644,user,...,_3650),_3614,'$expand':'$set_source_module'(user)) at /usr/lib/swi-prolog/boot/init.pl:443
ERROR: [8] '$expand':expand_goal(user:(_3706 is ...),_3692,user:_3714,_3696) at /usr/lib/swi-prolog/boot/expand.pl:458
ERROR: [6] setup_call_catcher_cleanup('$toplevel':'$set_source_module'(user,user),'$toplevel':expand_goal(...,...),_3742,'$toplevel':'$set_source_module'(user)) at /usr/lib/swi-prolog/boot/init.pl:443
ERROR:
ERROR: Note: some frames are missing due to last-call optimization.
ERROR: Re-run your program in debug mode (:- debug.) to get more detail.
^ Call: (14) call('$expand':'$set_source_module'(user)) ? abort
% Execution Aborted
?- X = [a, b, c].
X = [a, b, c].
I am brand new to prolog and I feel like there is a concept that I am failing to understand, which is preventing me from grasping the concept of recursion in prolog. I am trying to return S, which is the sum of the square of each digit, taken as a list from an integer that is entered by the user in a query. E.g The user enters 12345, I must return S = (1^2)+(2^2)+(3^2)+(4^2)+(5^2) = 55.
In my program below, I understand why the each segment of the calculation of S is printed multiple time as it is part of the recursive rule. However, I do not understand how I would be able to print S as the final result. I figured that I could set a variable = to the result from sos in the second rule and add it as a parameter for intToList but can't seem to figure this one out. The compiler warns that S is a singleton variable in the intToList rule.
sos([],0).
sos([H|T],S) :-
sos(T, S1),
S is (S1 + (H * H)),
write('S is: '),write(S),nl.
intToList(0,[]).
intToList(N,[H|T]) :-
N1 is floor(N/10),
H is N mod 10,
intToList(N1,T),
sos([H|T],S).
The issue with your original code is that you're trying to handle your call to sos/2 within your recursive clause for intToList/2. Break it out (and rename intToList/2 to something more meaningful):
sosDigits(Number, SoS) :-
number_digits(Number, Digits),
sos(Digits, SoS).
Here's your original sos/2 without the write, which seems to work fine:
sos([], 0).
sos([H|T], S) :-
sos(T, S1),
S is (S1 + (H * H)).
Or better, use an accumulator for tail recursion:
sos(Numbers, SoS) :-
sos(Numbers, 0, SoS).
sos([], SoS, SoS).
sos([X|Xs], A, SoS) :-
A1 is A + X*X,
sos(Xs, A1, SoS).
You can also implement sos/2 using maplist/3 and sumlist/2:
square(X, S) :- S is X * X.
sos(Numbers, SoS) :- maplist(square, Numbers, Squares), sumlist(Squares, SoS).
Your intToList/2 needs to be refactored using an accumulator to maintain correct digit order and to get rid of the call to sos/2. Renamed as explained above:
number_digits(Number, Digits) :-
number_digits(Number, [], Digits).
number_digits(Number, DigitsSoFar, [Number | DigitsSoFar]) :-
Number < 10.
number_digits(Number, DigitsSoFar, Digits) :-
Number >= 10,
NumberPrefix is Number div 10,
ThisDigit is Number mod 10,
number_digits(NumberPrefix, [ThisDigit | DigitsSoFar], Digits).
The above number_digits/2 also handles 0 correctly, so that number_digits(0, Digits) yields Digit = [0] rather than Digits = [].
You can rewrite the above implementation of number_digits/3 using the -> ; construct:
number_digits(Number, DigitsSoFar, Digits) :-
( Number < 10
-> Digits = [Number | DigitsSoFar]
; NumberPrefix is Number div 10,
ThisDigit is Number mod 10,
number_digits(NumberPrefix, [ThisDigit | DigitsSoFar], Digits)
).
Then it won't leave a choice point.
Try this:
sos([],Accumulator,Accumulator).
sos([H|T],Accumulator,Result_out) :-
Square is H * H,
Accumulator1 is Accumulator + Square,
sos(T,Accumulator1,Result_out).
int_to_list(N,R) :-
atom_chars(N,Digit_Chars),
int_to_list1(Digit_Chars,Digits),
sos(Digits,0,R).
int_to_list1([],[]).
int_to_list1([Digit_Char|Digit_Chars],[Digit|Digits]) :-
atom_number(Digit_Char,Digit),
int_to_list1(Digit_Chars,Digits).
For int_to_list I used atom_chars which is built-in e.g.
?- atom_chars(12345,R).
R = ['1', '2', '3', '4', '5'].
And then used a typical loop to convert each character to a number using atom_number e.g.
?- atom_number('2',R).
R = 2.
For sos I used an accumulator to accumulate the answer, and then once the list was empty moved the value in the accumulator to the result with
sos([],Accumulator,Accumulator).
Notice that there are to different variables for the accumulator e.g.
Accumulator1 is Accumulator + Square,
sos(T,Accumulator1,Result_out).
this is because in Prolog variables are immutable, so one can not keep assigning new values to the same variable.
Here are some example runs
?- int_to_list(1234,R).
R = 30.
?- int_to_list(12345,R).
R = 55.
?- int_to_list(123456,R).
R = 91.
If you have any questions just ask in the comments under this answer.
I'm completely new to Prolog and have a hard time understanding its unification system. My problem is as follows:
I have a Constraint integer, a Source array and a Target array (both of them are arrays of integers). I'd like to filter the elements in the Source array such that the remaining elements have a counterpart in the Target array so that the difference between the element in the Source array and the element in the Target array is exactly Constraint.
Example: Constraint = 1, Source = [1, 5, 7], Target = [2, 3, 4]. FilteredSource should be [1, 5], because abs(1 - 2) = 1 and abs(5 - 4) = 1. There's no 6 or 8 in Target so 7 is no good.
Depending on the arguments I use I either get wrong results or the query get's in an infinite loop.
So far, I have come up with this:
filterByDistanceConstraint(_Constraint, [], _Target, _).
filterByDistanceConstraint(Constraint, [SourceHead|SourceTail], Target, FilteredSource) :-
filterByDistanceConstraint(Constraint, SourceTail, Target, NewFilteredSource),
(
passesDistanceConstraint(Constraint, SourceHead, Target)
->
append(NewFilteredSource, [SourceHead], FilteredSource),
filterByDistanceConstraint(Constraint, SourceTail, Target, NewFilteredSource)
;
filterByDistanceConstraint(Constraint, SourceTail, Target, FilteredSource)
).
The passesDistanceConstraint part seems to work ok but I'll include it here for reference:
passesDistanceConstraint(_Constraint, _SourceHead, []) :-
fail.
passesDistanceConstraint(Constraint, SourceHead, [TargetHead|TargetTail]) :-
Distance is TargetHead - SourceHead,
( abs(Distance) =\= Constraint %test failed
-> passesDistanceConstraint(Constraint, SourceHead, TargetTail)
; write('Distance constraint passed for '), write(SourceHead), nl
).
I'm quite sure this is a trivial problem but I can't figure it out and I'm pulling my hair off. Thanks for any help!
This should work
filterByDistanceConstraint(_Constraint, [], _Target, []).
filterByDistanceConstraint(Constraint, [SourceHead|SourceTail], Target, FilteredSource) :-
( passesDistanceConstraint(Constraint, SourceHead, Target)
-> FilteredSource = [SourceHead|NewFilteredSource]
; FilteredSource = NewFilteredSource
),
filterByDistanceConstraint(Constraint, SourceTail, Target, NewFilteredSource).
Note the [] instead of _ in the first rule, and that there is only one recursive call in the second.
The 'filter' programming pattern is implemented in SWI-Prolog by include/3 or exclude/3, but to write in plain Prolog, since member/2 can act as an 'enumerator':
filterByDistanceConstraint(Constraint, Source, Target, FilteredSource) :-
findall(E, (member(E, Source), member(C, Target), abs(E - C) =:= Constraint), FilteredSource).
I have this code which finds double quotation marks and converts the inside of those quotation marks into a string. It manages to find the first quotation mark but fails to find the second so: "this" would be "this . How do I get it I can get this function to find the full string.
Maybe this is too obvious:
if (ch = #"\"") then SOME(String(x ^ "\""))
I do not really understand your code: you return the string just after the first occurence of the quotation mark, but this string has been built with the characters that you've found before it. Moreover, why do you return SOME(Error) instead of NONE?
You need to use a boolean variable to know when the first quotation mark has been seen and to stop when the second one is found. So I would write something like this:
fun parseString x inStr quote =
case (TextIO.input1 inStr, quote) of
(NONE, _) => NONE
| (SOME #"\"", true) => SOME x
| (SOME #"\"", false) => parseString x inStr true
| (SOME ch, true) => parseString (x ^ (String.str ch)) inStr quote
| (SOME _ , false) => parseString x inStr quote;
and initialize quote with false.