0

I have an assignment that requires a function which returns (if it exists) an element that shows up on more than half of the list, else it returns no. So I have made a function to find the length of a list but now I need a method to

find the most recursive element and the times it is found inside the list

Any ideas? I am new to Prolog and I am asking for my assignment's sake so if someone is about to help me just give me hints or enlighten me, I don't want any actual code to copy paste.

lurker
  • 56,987
  • 9
  • 69
  • 103
White_Sirilo
  • 264
  • 1
  • 11
  • It's a possible duplicate of [this](https://stackoverflow.com/questions/14691479/how-to-find-the-mode-of-a-list-in-prolog). – Julián Esteban Salomón Torres May 20 '18 at 17:36
  • If it requires a function then you're out of luck. The Prolog language doesn't have functions. Youight want to check out [99 Prolog Problems](http://www.ic.unicamp.br/~meidanis/courses/mc336/2009s2/prolog/problemas/) which has simple problems and answers, particularly the list processing problems. That will show you how to write a predicate that recursively process lists. – lurker May 20 '18 at 18:15
  • 3
    Could we please stop beating people up in comments for saying "function"? Yes, it's a good idea to point out the correct terminology as part of an *answer*, but a snarky comment with nothing else but a complaint about a word isn't useful to anyone. – Isabelle Newbie May 20 '18 at 21:09
  • 1
    @IsabelleNewbie moreover, function is a special case of relation. – Will Ness May 21 '18 at 08:06

3 Answers3

2

library(aggregate) is worth to learn...

?- L=[1,2,3,3,3,1,2,3],aggregate(max(C,E),aggregate(count,member(E,L),C),R).
L = [1, 2, 3, 3, 3, 1, 2, 3],
R = max(4, 3).

edit: accounting for the requirement about the occurrences wrt list length:

?- L=[3,2,6,2,4],length(L,Len),HalfLen is Len/2, aggregate(max(C,E),(aggregate(count,member(E,L),C),C>HalfLen),R).
false.
CapelliC
  • 59,646
  • 5
  • 47
  • 90
0

There is a way (well, infinite ways) to solve this with prolog builtins, such as sort/2, findall/3, member/2, include/3 and length/2:

?- List=[3,2,6,2,2,1,4,2,2,2,2,4,1,2,3,2,2,4,2,3,2],
    sort(List, Uniq),                 % sort removing duplicate elements
    findall([Freq, X], (
        member(X, Uniq),              % for each unique element X
        include(=(X), List, XX),      %
        length(XX, Freq)              % count how many times appears in List
    ), Freqs),
    sort(Freqs, SFreqs),              % sort (by frequency)
    last(SFreqs, [Freq, MostCommon]), % last pair is the most common
    length(List, ListLen),
    Freq > ListLen/2.                 % is frequency greater than half list length?

List = [3, 2, 6, 2, 2, 1, 4, 2, 2|...],

Uniq = [1, 2, 3, 4, 6],

Freqs = [[2, 1], [12, 2], [3, 3], [3, 4], [1, 6]],

SFreqs = [[1, 6], [2, 1], [3, 3], [3, 4], [12, 2]],

Freq = 12,

MostCommon = 2,

ListLen = 21.

Depending on what constraints the instructor did put for solving the excercise, you may need to implement one or more of these builtins by yourself.

Another way, more efficient, would be to use msort/2 and then create a run-length encoding of the sorted list, then sort it again, and pick the element representing the most commonly occurring element. Right now I can't figure out how to do run-length encoding without defining auxiliary recursive predicates, so here it is:

count_repeated([Elem|Xs], Elem, Count, Ys) :-
    count_repeated(Xs, Elem, Count1, Ys), Count is Count1+1.
count_repeated([AnotherElem|Ys], Elem, 0, [AnotherElem|Ys]) :-
    Elem \= AnotherElem.
count_repeated([], _, 0, []).

rle([X|Xs], [[C,X]|Ys]) :-
    count_repeated([X|Xs], X, C, Zs),
    rle(Zs, Ys).
rle([], []).

then, getting the most common element can be done with:

?- List=[3,2,6,2,2,1,4,2,2,2,2,4,1,2,3,2,2,4,2,3,2],
    msort(List, SList),
    rle(SList, RLE),
    sort(RLE, SRLE),
    last(SRLE, [Freq, MostCommon]),
    length(List, ListLen),
    Freq > ListLen/2.

List = [3, 2, 6, 2, 2, 1, 4, 2, 2|...],

SList = [1, 1, 2, 2, 2, 2, 2, 2, 2|...],

RLE = [[2, 1], [12, 2], [3, 3], [3, 4], [1, 6]],

SRLE = [[1, 6], [2, 1], [3, 3], [3, 4], [12, 2]],

Freq = 12,

MostCommon = 2,

ListLen = 21.

fferri
  • 18,285
  • 5
  • 46
  • 95
  • since you have the frequencies for all the elements, you could pick the most frequent element - without sorting, - then sum up the frequencies of all the rest, and compare the counts. this can even be done in *one* traversal. :) – Will Ness May 21 '18 at 09:03
0

foldl may be your friend :

:- use_module(library(lambda)).
count_repeat(L, R, N) :-
    foldl(\X^Y^Z^(select(V-X, Y, Y1)-> V1 is V+1, Z = [V1-X|Y1]; Z = [1-X|Y]), L, [], L1),
    sort(L1,L2),
    last(L2, N-R).

Result :

?- count_repeat([3,2,6,2,4,1,4,2,5,3,2,4,1,2,3,1,2,4,5,3,2], R, N).

R = 2,

N = 7.

Community
  • 1
  • 1
joel76
  • 5,565
  • 1
  • 18
  • 22