Проверьте, не превышает ли частота какого-либо элемента предел

Я хочу решить проблему, заключающуюся в том, что у меня есть список элементов Prolog. Если частота любого элемента больше N, возвращается false. Мои ожидания, как показано ниже.

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

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

У меня есть код для получения определенной частоты элемента. Любая идея для проблемы.

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).

person Gamsh    schedule 20.02.2016    source источник
comment
Где ты застрял? Вы пытались что-нибудь реализовать frequency/2? Путь грубой силы (неэффективный, но будет работать) состоит в том, чтобы рекурсивно пройтись по каждому элементу входного списка и проверить его частоту в исходном списке (используя ваш предикат count/3) по максимуму.   -  person lurker    schedule 20.02.2016
comment
Пожалуйста, будьте более конкретными! Что такое частота? Должен ли ?- frequency([1,2,2,3,2,4],3). преуспеть? А как насчет ?- frequency([1,2,2,3,2,2,2,4],3).   -  person repeat    schedule 20.02.2016
comment
@luker count/3 дает частоту определенного числа. Я хочу взять максимальную частоту, и она должна быть меньше 4. как ее получить?   -  person Gamsh    schedule 21.02.2016
comment
@repect мое ожидание, что любая частота больше 3 возвращает false.?- frequency([1,2,2,3,2,2,2,4],3). здесь частота 2 равна 5, а 5>3. тогда он вернет false.   -  person Gamsh    schedule 21.02.2016


Ответы (3)


Используйте clpfd!

:- use_module(library(clpfd)).

Если мы строим вспомогательный предикат list_counts/2, мы можем определить frequency/2 следующим образом:

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

Примеры запросов:

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

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

Благодаря clpfd мы можем задавать довольно общие вопросы. вопросы и получать логически обоснованные ответы!

?- 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).
person repeat    schedule 25.02.2016
comment
да. спасибо за хороший ответ. конечно, это хорошее решение. - person Gamsh; 26.02.2016

Вы можете сначала попытаться подумать о наиболее «дешевом» (с точки зрения написания) способе решения такой проблемы. Обычно я пытаюсь понять, как бы я это сделал со стандартными инструментами командной строки. Например, чтобы найти вхождение всех строк в текстовый файл с именем foo, нужно написать (в Bash):

sort foo | uniq --count

Вы можете прочитать руководства, но вот один пример запуска:

$ 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

Теперь один из способов спросить: «Есть ли какая-нибудь строка со счетом выше 3?» использовать awk следующим образом:

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

(Я уверен, что есть и более умные способы.)

С вышеуказанным файлом вы получаете:

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

Хорошо, а как это поможет вам с Прологом? Что ж, один простой способ эмулировать такой конвейер:

foo | bar | baz # etc

состоит в том, чтобы написать на Прологе такую ​​конъюнкцию:

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

Вернемся к вашей проблеме: вы можете использовать msort/2 (или, тем не менее, в используемой вами реализации вызывается предикат стабильной сортировки). Затем вам нужно подсчитать прогоны одного и того же элемента. В SWI-Prolog по крайней мере у вас есть group_pairs_by_key/2. Вы можете использовать его, например, следующим образом (вместе с другими предикатами в той же библиотеке вы можете увидеть код по той же ссылке):

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

В этот момент у вас есть количество вхождений каждого элемента в Sorted в Counts (по общему признанию, немного более подробное, чем uniq --count), и вам просто нужно проверить, не превышает ли какой-либо из них ваш предел. Чтобы сделать что-то очень похожее на вызов awk выше в Прологе:

maplist(=<(3), Counts)

Отказ от ответственности: это всего лишь один из способов решения проблемы. Я решил напечатать это, потому что это показывает, что вам редко нужно писать много кода самостоятельно, если вы знаете, какие инструменты уже доступны вам.

Редактировать

Конечно, нет необходимости использовать group_pairs_by_key/2; однако знать об этом очень полезно, поэтому я привязал реализацию. Для этой проблемы было бы достаточно выполнить стабильную сортировку, за которой следует предикат, который просто подсчитывает количество последовательных вхождений одного и того же элемента, и добивается успеха только в том случае, если все такие запуски не длиннее предела. Базовая структура предиката, который делает это, такая же, как group_pairs_by_key/2.

person Community    schedule 21.02.2016
comment
s(X): Мне нравится такой подход, но мы используем одни и те же инструменты... в конце концов, все сводится к тем инструментам, которыми вам удобно пользоваться. - person repeat; 22.02.2016
comment
У меня только SWI-Prolog (версия 7.1.17). это возможно получить ответ? - person Gamsh; 22.02.2016
comment
@Gamsh, это ответ, ты сам пробовал? - person ; 22.02.2016
comment
@repeat Еще один момент, который я не указал, заключается в том, что в этом решении нет явного выбора. Это также относится и к трубам-оболочкам: обычно на уровне трубы нет условных выражений. Сравнение Пролога с оболочкой также интересно, потому что мне нелегко было найти способ избежать if в вызове awk, но с Прологом и maplist там также нет явного условного выражения. Другими словами, нет необходимости в потоке управления, все линейно (по крайней мере, на этом уровне абстракции). - person ; 22.02.2016
comment
@repeat Возможно, вы слышали о концепции возможностей. Я пытался, но не смог найти хороших статей по теме в контексте языков и инструментов программирования и того, как они влияют на программиста, и решений, которые они могут рассмотреть. Я уверен, что что-то должно быть, я просто не знаю, что именно искать. - person ; 22.02.2016
comment
Я не понимаю, какой из них является кодом пролога. и что это foo,bar baz - person Gamsh; 22.02.2016
comment
@Gamsh Ваш вопрос очень похож на домашнее задание, и есть рекомендации. о том, как задавать вопросы и отвечать на них. Я старался следовать этим рекомендациям. Я извиняюсь, если это не домашнее задание, но тогда дайте мне кодез, это все еще не ок. - person ; 22.02.2016
comment
Да, это хорошо. но я не могу понять, что это за запрос для этого. - person Gamsh; 22.02.2016

Что с этим кодом...

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).
person Giththan    schedule 22.02.2016
comment
Зачем добавлять пустой список к чему-то: append([],[X1|T],L)? - person ; 22.02.2016
comment
возьмите первый элемент X1 и остальные T списка, поэтому добавьте пустой список. - person Giththan; 22.02.2016
comment
Почему не [X1|T] = L? Аналогично, зачем добавлять один элемент вместо [N1|N2] = N? append/3 — полезный предикат, но унификация и сопоставление с образцом еще лучше. - person ; 22.02.2016
comment
@Giththan, все в порядке. в этом случае если один раз найти число 2 и посчитать частоту.то хвостовая часть тоже найдет счет.как мы можем это устранить? - person Gamsh; 22.02.2016
comment
@Gamsh Вы можете использовать delete/3 для удаления элемента. В этом случае вы можете удалить элемент из Tail после подсчета. Это способ, которым вы можете удалить. - person Giththan; 23.02.2016
comment
@Гамш. Это (потенциально) плохой совет. Помните о семантических опасностях использования delete/3! - person repeat; 25.02.2016
comment
Как насчет того, чтобы последовать совету, который дал тебе Борис? Почему бы не отредактировать свой ответ и не улучшить его? - person repeat; 25.02.2016
comment
@повторить. Да, я рассмотрю это. - person Gamsh; 25.02.2016