1
votes

I am trying to find phrase in a file using Prolog and if its finded just returing true. The problem is that i dont have idea how to use this phrase_from_file/2. I made something like that.

search(Smthing):-   
    phrase_from_file(Smthing,'b.txt');
    false.

executing: search('myphrase').

b.txt file:

(fs1111myphrase111asd)
 aaaamyphrase

Can anyone show me how to implement it?

2
As an aside, you don't need query -> true ; false. The query will already succeed or fail, so the -> here is nearly redundant producing the same result, except it will prevent backtracking to query for more than one solution. Backtracking probably doesn't apply for phrase_from_file/2 so it is serving no purpose.lurker
ok already changed it.Andrzej Ustupski
You don't need the ; false either, for the same reason. At that point, the phrase_from_file call has already failed. Why do you need to call false?lurker
Of interest: Prolog DCG PrimerGuy Coder

2 Answers

2
votes

The major attraction of library(pio) is that it simply applies a DCG to a file.

So, first, you need to have a basic understanding of DCGs in Prolog. See for more information.

Next, focus on how you would normally describe the task with a DCG. You want to describe a list that contains [m,y,p,h,r,a,s,e], or—speaking in codes—[109,121,etc.], as a sublist.

A useful nonterminal for describing anything at all is:

... --> [] | [_], ... .

And so we can describe a list with the given property as:

contains_myphrase -->
        ...,
        "myphrase",
        ... .

The beauty of this is that we can already test it interactively:

?- length(Ls, _), phrase(contains_myphrase, Ls).
Ls = [109, 121, 112, 104, 114, 97, 115, 101] ;
Ls = [109, 121, 112, 104, 114, 97, 115, 101, _8260] ;
Ls = [_8212, 109, 121, 112, 104, 114, 97, 115, 101] ;
Ls = [109, 121, 112, 104, 114, 97, 115, 101, _8260|...] ;
Ls = [_8212, 109, 121, 112, 104, 114, 97, 115, 101|...] ;
Ls = [_8212, _8218, 109, 121, 112, 104, 114, 97, 115|...] .

And then we can also apply the same DCG to a file:

?- phrase_from_file(contains_myphrase, 'b.txt').
true .

It is straight-forward to generalize this so that you can specify what you are looking for as an argument.

However, I recommend to first do this without using phrase_from_file/2. Simply work on the DCG, and study the different encodings. In particular, you should find out the difference between:

  • atoms
  • lists of codes
  • lists of characters.

Make the following work first:

?- phrase(contains(MyPhrase), Ls).

This should hold iff Ls contains MyPhrase as a sublist, where MyPhrase could for example be specified as a list of codes. I say "could" because you should pick an encoding that is most convenient and natural for your use case. From personal experience, I can tell you that using lists of characters is by far more convenient than using lists of codes. A Prolog flag you may find relevant in this context is called double_quotes.

Once you have this solved with a DCG, it is straight-forward to use phrase_from_file/2 on it.

1
votes

To use the various phrase predicates, you will have to understand Prolog's DCG (definite clause grammar) system for defining grammars. Here is one tutorial: http://www.pathwayslms.com/swipltuts/dcg/, and there should be DCG chapters in every other reasonable Prolog text. Note also that phrase_from_file/2 is an SWI-Prolog extension that to my knowledge is not present on other Prolog systems.

So you need to write a grammar that matches an arbitary sequence of input items, they your pattern, then an arbitrary rest. All this is complicated by the fact that SWI-Prolog knows several kinds of string-like things: strings, atoms, lists of codes, and lists of chars.

Anyway, here is an example I adapted very slightly from the SWI-Prolog docs at http://www.swi-prolog.org/pldoc/man?section=pio:

:- use_module(library(dcg/basics)).

file_contains(File, Pattern) :-
        phrase_from_file(match(Pattern), File).

match(Pattern) -->
        string(_),
        string(Pattern),
        string(_).

You can run this as follows:

?- file_contains('b.txt', `myphrase`).
true ;
true ;
false.

This is not very informative, but the two true results mean that your pattern occurred twice in the input file. The final false means that there were no further occurences after these two.

Note that your phrase must be enclosed in backtick characters (`) to make SWI-Prolog interpret it as a list of character codes (the underlying representation of file contents by phrase_from_file/2).