3

I want to solve a problem that is I have a Prolog list of elements. If the any of the element frequency is greater than N then false is return. My expectation like below.

?- frequency([1,2,2,2,5],3).
true.

?- frequency([1,2,2,2,2,5],3).
false.

I have a code for get particular element frequency. Any idea for the problem.

count(_, [], 0) :-
   !.
count(X, [X|T], N) :-
   count(X, T, N2),
   N is N2 + 1.
count(X, [Y|T], N) :-
   X \= Y,
   count(X, T, N).
Gamsh
  • 545
  • 4
  • 21
  • 1
    Where are you stuck? Have you tried anything to implement `frequency/2`? The brute force way (not efficient, but would work) is to walk through each element of the input list recursively, and check it's frequency in the original list (using your `count/3` predicate) against the maximum. – lurker Feb 20 '16 at 17:49
  • 1
    Please be more specific! What's a "frequency"? Should `?- frequency([1,2,2,3,2,4],3).` succeed? What about `?- frequency([1,2,2,3,2,2,2,4],3).` – repeat Feb 20 '16 at 20:55
  • @luker `count/3` gives a particular number's frequency.I want take maximum frequency and its should be less than 4.how to get it? – Gamsh Feb 21 '16 at 02:59
  • @repect my expectation is any of the frequency greater than 3 return false.`?- frequency([1,2,2,3,2,2,2,4],3).` here 2's frequency is 5. and 5>3. then it will return false. – Gamsh Feb 21 '16 at 03:03

3 Answers3

4

Use !

:- use_module(library(clpfd)).

If we build on auxiliary predicate list_counts/2, we can define frequency/2 like this:

frequency(Es, M) :-
   list_counts(Es, Xss),
   maplist(arg(2), Xss, Zs),
   maplist(#>=(M), Zs).

Sample queries:

?- frequency([1,2,2,2,5], 3).
true.

?- frequency([1,2,2,2,2,5], 3).
false.

Thanks to we can ask quite general queries—and get logically sound answers, too!

?- frequency([A,B,C], 2).
       A=B ,           dif(B,C)
;                A=C , dif(B,C)
;            dif(A,C),     B=C
;  dif(A,B), dif(A,C), dif(B,C).
Community
  • 1
  • 1
repeat
  • 18,496
  • 4
  • 54
  • 166
3

You can first try to think about the most "cheap" (in terms of writing) way you could attack such a problem. I usually try to figure out how I would do it with standard command line tools. For example, to find the occurrences of all lines in a text file named foo, one would write (in Bash):

sort foo | uniq --count

You can read the manuals, but here is one example run:

$ cat foo
a
b
b
b
c
d
d
a
b
d
c
$ sort foo | uniq --count
      2 a
      4 b
      2 c
      3 d

Now, one way to ask "is there any line that has a count above 3?" is to use awk like this:

sort foo | uniq --count | awk '{ if ($1 > 3) exit(1) }'

(I am sure there are cleverer ways, too.)

With the above file, you get:

$ sort foo | uniq --count | awk '{ if ($1 > 3) exit(1) }'
$ echo $?
1
$ sort foo | uniq --count | awk '{ if ($1 > 4) exit(1) }'
$ echo $?
0

Alright, so how does that help you with Prolog? Well, one easy way to emulate a pipeline like this:

foo | bar | baz # etc

is to write in Prolog a conjunction like this:

foo(In, X0), bar(X0, X1), baz(X1, X2) % etc

Back to your problem: you can use msort/2 (or however a stable sort predicate is called in the implementation you are using). Then, you need to count runs of the same element. In SWI-Prolog at least you have group_pairs_by_key/2. You could use it for example as follows (together with the other predicates in the same library, you can see the code at the same link):

pairs_keys_values(Pairs, Sorted, Sorted),
group_pairs_by_key(Pairs, Grouped),
pairs_values(Grouped, Runs),
maplist(length, Runs, Counts)

At that point, you have the number of occurrences of each element in Sorted in Counts (a bit more verbose than uniq --count, admittedly), and you just need to check if any of these is above your limit. To do something very similar to the awk invocation above in Prolog:

maplist(=<(3), Counts)

Disclaimer: this is just one way to approach the problem. I decided to type it out because it shows that you rarely need to write much code yourself if you know what tools are already available to you.

Edit

It is of course unnecessary to use group_pairs_by_key/2; however, it is very useful to know about it, which is why I linked the implementation. For this problem, it would be enough to do a stable sort, followed by a predicate that simply counts the number of consecutive occurrences of the same element, and only succeed if all such runs are not longer than the limit. The basic structure of a predicate that does that is the same as group_pairs_by_key/2.

  • s(X): I like the approach, but we're using the same tools... in the end it all comes down to the tools you are comfortable using. – repeat Feb 21 '16 at 22:05
  • 1
    I have only SWI-Prolog (Version 7.1.17). is this possible to get the answer? – Gamsh Feb 22 '16 at 02:24
  • @Gamsh this is the answer, did you try it out yourself? –  Feb 22 '16 at 05:27
  • @repeat Another point that I didn't spell out is that there are no explicit choices in this solution. This is also the case with shell pipes: usually, there are no conditionals on the level of the pipe. The Prolog to shell comparison is also interesting, because I didn't easily find a way to avoid the `if` in the awk invocation, but with Prolog and `maplist`, there is not explicit conditional there, either. In other words, no control flow necessary, everything is linear (at this abstraction level at least). –  Feb 22 '16 at 06:27
  • 1
    @repeat You might have heard of the concept of [affordances](https://en.wikipedia.org/wiki/Affordance#As_perceived_action_possibilities). I tried and failed to find good papers on the topic in the context of programming languages and tools and how they influence the programmer and the solutions they are likely to consider. I am sure there must be something, I just don't know what exactly to look for. –  Feb 22 '16 at 06:34
  • I am not understand which one is prolog code.and what is that `foo`,`bar` `baz` – Gamsh Feb 22 '16 at 12:01
  • 1
    @Gamsh Your question looks very much like homework, and there are [guidelines](http://meta.stackexchange.com/questions/10811/how-do-i-ask-and-answer-homework-questions) on how to ask and answer those. I tried to follow these guidelines. I apologize if this is _not_ homework, but then, "give me the codez" is still not ok. –  Feb 22 '16 at 12:20
  • yes, it is ok. but i can't understand what is the query for this. – Gamsh Feb 22 '16 at 17:09
1

What is about this code...

frequency(L,N):-getall(L,L1), max_member(A,L1),A=<N.

getall([],[]).
getall(L,N):-append([],[X1|T],L),count(X1,L,N1),getall(T,N2),append([N1],N2,N).
Giththan
  • 58
  • 6
  • 1
    Why append an empty list to something: `append([],[X1|T],L)`? –  Feb 22 '16 at 12:40
  • take for the first element `X1` and the rest `T` of the list so append an empty list. – Giththan Feb 22 '16 at 14:05
  • 2
    Why not `[X1|T] = L`? Similarly, why append a single element instead of `[N1|N2] = N`? `append/3` is a useful predicate, but unification and pattern matching are even nicer. –  Feb 22 '16 at 14:57
  • 1
    @Giththan it is ok. in this case if once find the number `2` and count the frequency.then the tail part also find the count.how can we eliminate this? – Gamsh Feb 22 '16 at 17:12
  • @Gamsh You can use `delete/3` for delete the element.In this case you can delete the element from Tail after the count.This is the way you can eliminate. – Giththan Feb 23 '16 at 13:48
  • @Gamsh. That is (potentially) bad advice. Be aware of the semantic perils of utilizing `delete/3`! – repeat Feb 25 '16 at 13:45
  • How about actually following the advice that Boris gave you? Why not edit your answer and improve it? – repeat Feb 25 '16 at 13:46
  • 1
    @repeat. Yes I will Consider it. – Gamsh Feb 25 '16 at 14:07