Эти утверждения содержат сведения об очках, набранных командами. Предположим, что необходимо сложить все очки и усреднить их. Сбор очков в список осуществляется при помощи встроенного предиката findall :
findall(Points,football(_,Points),Point_list)
Здесь Poits является свободной переменной для значений набранных командами очков, а Point_list - списочной переменной, элементы которой принадлежат к тому же домену, что и Points, в данном случае, к домену real.
Сама работа предиката скрыта от глаз пользователя. В нашем примере findall просматривает все утверждения с предикатом football, начиная с первого. Значение переменной Points (116), взятое из этого утверждения, присваивается голове списка Point_list. Остальные значения Points помещаются в список на последующие позиции. По завершению работы findall переменная Point_list принимает значение
[116.0,121.0,114.0,99.0,122.0]
Для подсчета среднего значения набранных очков применяется рекурсивное правило
sum_list([],0,0).
sum_list([H|T], Sum, Number) :- sum_list(T,Sum1,Number1), Sum = H + Sum1,
Number = Number1 + 1.
Оно напоминает правило sum из гл. 4.
Для получения суммы всех элементов списка Point_list это правило необходимо задать в качестве подцели
sum_list(Point_list,Sum,Number).
Сначала Турбо-Пролог сопоставляет эту подцель с вариантом правила sum_list([H|T],Sum,Number). Point_list при этом сопоставляется с [H|T], а пе-
ременные Sum и Number оказываются неопределенными. Рекурсии с первым вариантом правила продолжаются до тех пор, пока Point_list не превратиться в нулевой список; на каждой рекурсии очередной элемент списка помещается в стек. Теперь Турбо-Пролог пытается удовлетворить правило
sum_list([],0,0).
Переменным Sum и Number присваиваются нули, и таким образом правило полностью удовлетворено. Сворачивая рекурсию sum_list, ТурбоПролог последовательно, один за другим, извлекает из стека засланные туда элементы и складывает их с уже имеющимся значением суммы; при этом переменная Number каждый раз увеличивается на единицу. В итоге имеем следующие формы sum_list:
sum_list([122],122,1) sum_list([99,122],221,2) sum_list([114,99,122],335,3) sum_list([121,114,99,122],456,4) sum_list([116,121,114,99,122],572,5)
По окончанию рекурсий значениями переменных Sum и Number являются соответственно 572 и 5.
116
Совсем просто выглядит правило для нахождения среднего значения набранных очков:
Average = Sum / Number
Правило Average использует подсчитанное правилом Sum_list значения переменных Sum и Number. Результатом применения этого правила является присвоение переменной Average частного от деления Sum и Number, т. е.
114.4.
Программа "Очки" (листинг 5.7) демонстрирует использование предиката findall для сбора данных из базы данных в список.
____________________________________________________________
Листинг 5.7
/* Программа: Очки |
*/ |
|
/* Назначение: Показ использования предиката |
*/ |
|
/* |
findall для вычисления среднего значения. */ |
|
domains
name = string points = real list = points *
predicates football(name,points)
sum_list(list,points,integer) report_average_football_score
goal
report_average_football_score.
clauses
/* факты (футбольная база данных) */ football("Ohio State",116.0). foot-ball("Michigan",121.0). football("Michigan State",114.0). football("Purdue",99.0). football("UCLA",122.0). report_average_football_score:-
findall(Points,football(_,Points),Point_list), sum_list(Point_list,Sum,Number), Average = Sum / Number,
write("College Football Power Rating:"), nl,
write(" Average Points = ",Average).
sum_list([],0,0).
sum_list([H|T], Sum, Number) :- sum_list(T,Sum1,Number1), Sum = H + Sum1,
117
Number = Number1 + 1.
/***** конец программы *****/
_____________________________________________________________
Программа подсчитывает сумму очков команд и их среднее значение. Внутренняя цель программы есть
report_average_football_score
Цель представляет собой правило, содержащее подцели findall, sum_list, Average, а также предикаты, осуществляющие вывод полученных результатов в нужной форме.
Начиная свою работу программа, пытается удовлетворить подцель findall в том виде, в котором она была описана. Когда подцель удовлетворена, делается попытка удовлетворить подцель sum_list, а затем Average. Переменной Average при этом присваивается значение 114.4, которое используется затем предикатом write. Теперь все подцели удовлетворены, следовательно, удовлетворена и цель программы.
*Упражнение
5.17.Возьмите текущею таблицу чемпионата СССР по футболу, введите в базу данных "Очки" результаты лучших десяти команд. Запустите программу на счет. Каким будет средний результат десятки ?
5.7. Заключение
В настоящей главе были представлены структура и функции списков в Турбо-Прологе, а также различные операции для работы с ними. Рассмотрение работы со списками началось с операций создания и печати списков при посредстве метода деления списка на голову и хвост. Завершилось это операциями поиска нужного элемента списка, деления, присоединения и сортировки списка. Затрагивался вопрос об использовании предиката findall для компоновки списка из данных базы данных.
Читая главу, вы познакомились с приемами создания правил, позволяющих описать цель программы. Вы научились превращать сформулированные на естественном языке запросы к программе в правила ТурбоПролога.
Рекомендуется читателю не пропускать предлагаемые упражнения, которые призваны углубить понимание основных структур и техники программирования. Упражнения помогают также научиться модифицировать демонстрационные программы с целью приспособить их к своим нуждам. Семь законченных программ данной главы показывают основные методы работы со списками и отличительные черты Турбо-Пролога. Их можно использовать в качестве заготовок для ваших собственных программ.
Чтение главы является обязательным, если вы собираетесь использовать списки в ваших программах, так как представленные здесь методы находят самое широкое применение при разработке программ на Турбо-
118
Прологе. Эти методы можно применять даже в том случае, если вы не понимаете до конца механизм их работы, хотя, конечно, понимание сделает Вам честь как программисту.
119