Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Шумихин / Шумихин / Лабораторная работа №2 - Таран Е. Ю. (1)

.docx
Скачиваний:
12
Добавлен:
20.05.2015
Размер:
325.69 Кб
Скачать

Одесский национальный университет им. И.И.Мечникова

Министерство науки и образования Украины

Лабораторная работа №2

по курсу: «Специализированые

языки программирования»

выполнил студент III курса

гр. МОКС, Таран Евгений Юрьевич

Одесса 2012

Вариант №2

Цель работы: приобретение навыков работы со списками в

Пролог-программах.

  1. Вводится список целых. Последний элемент списка 0.

1. Найти все номера элементов списка, равные максимальному

2. Удалить из введенного списка все вхождения наибольшего

элемента

3. Вычислить среднее арифметическое полученного списка

4. Вычислить медиану полученного списка

5. Разбить первоначальный список на два. В первый включить

положительные элементы полученного списка, во второй - все

остальные.

Медианой списка будем считать следующий элемент – после того как мы упорядочили этот список, медианой будет тот элемент, который в случае

А). нечетный длины списка – стоит по средине. ([1,2,3] M = 2 ([2,1,3] M = 2) т. к. медиану ищем по упорядоченному списку).

Б).четной длины списка – полусумма элементов которые стоят «посредине» списка. ([1, 2, 2, 1,] -> [1,1,2,2] -> M = (1+2)/2].

Код программы:

findmax([H|T],N):-find(T,H,MAX),N=MAX.

find([],MAX,RES):-RES = MAX.

find([H|T],MAX,RES):-MAX>H,find(T,MAX,RES).

find([H|T],MAX,RES):-MAX=<H, MAX1 = H, find(T,MAX1,RES).

readlist([X|Y]):- read(X),X =\= 0,readlist(Y),!.

readlist([0]):-!.

goal:- readlist(X),write('Entered list - '),write(X),

write('Max element positions = '),findmax(X,MAX),writemaxnum(X,1,MAX),

delmax(MAX,X,X2),write('\nList without max elements - '),write(X2),

srednarifm(X2,0,0,RES),write('\nSrednee arifmeticheskoe poslednego spiska - '),write(RES),

write('\nMediana nachalnogo spiska = '),mediana(X),

twolists(X,PL,OL),write('\nPositive list - '),write(PL),write('\nOther element list - '),write(OL).

writemaxnum([],_,_).

writemaxnum([MAX|T],MAXNUM,MAX):- write(MAXNUM),write(' '), MAXNUM1 is MAXNUM+1, writemaxnum(T,MAXNUM1,MAX).

writemaxnum([_|T],MAXNUM,MAX):- MAXNUM1 is MAXNUM+1, writemaxnum(T,MAXNUM1,MAX).

delmax(_,[],[]).

delmax(X,[X|T],L1):-delmax(X,T,L1).

delmax(X,[Y|T],[Y|T1]):- X =\= Y, delmax(X,T,T1).

srednarifm([],0,0,0):-!.

srednarifm([],SUM,NUM,RES):- RES is SUM / NUM,!.

srednarifm([H|L],SUM,NUM,RES):- SUM1 is SUM + H, NUM1 is NUM + 1, srednarifm(L,SUM1,NUM1,RES).

mediana(L):-mysort(L,S),mylength(S,N),calcmediana(S,N,M),write(M).

mysort(L,S):-swap(L,M),!,mysort(M,S).

mysort(L,L):-!.

swap([X,Y|R],[Y,X|R]):-X>Y.

swap([X|R],[X|R1]):-swap(R,R1).

mylength([],0).

mylength([_|L],N):-mylength(L,N1), N is N1+1.

calcmediana(L,N,R):- N mod 2 =:= 1, Num = (div(N,2) + 1), getelem(L,0,Num,R1), R = R1,!.

calcmediana(L,N,R):- N mod 2 =:= 0, Num is N div 2 ,getelem(L,0,Num,R1), getelem(L,0,Num+1,R2), R is (R1+R2)/2.

getelem([H|_],N,N2,RES):- N2 =:= N+1, RES = H.

getelem([_|L],CurrN,N,RES):- CurrN1 is CurrN + 1, getelem(L,CurrN1,N,RES).

add([H|T],El,[H|Res]):- add(T,El,Res).

add([],El,[El]).

twolists(L,PL,OL):-twolist(L,[],[],PL,OL).

twolist([],PL,OL,PL,OL).

twolist([H|L],PosList,OtherList,ResPosList,ResOtherList):-H > 0, add(PosList,H,Poslist1),

twolist(L,Poslist1,OtherList, ResPosList, ResOtherList).

twolist([H|L],PosList,OtherList,ResPosList,ResOtherList):-H =< 0, add(OtherList,H,OtherList1),

twolist(L, PosList, OtherList1, ResPosList, ResOtherList).

Рассмотрим используемые функции по порядку.

1.findmax – предикат который возвращает максимальный элемент, используя find.

find – предикат который от предиката findmax принимает голову списка, в котором надо найти максимальный элемент, и потом последовательно сравнивая с каждым элементом, выбирает максимальный.

readlist – предикат который считывает элементы введенные с клавиатуры до тех пор пока не будет введен ноль.

writemaxnum – предикат который выводит номера максимальных элементов. Он получает на вход максимальный элемент и начальный номер – единицу. Сравнивая голову списка с максимальным элементом, который найден предикатом findmax, он выводит их в случае равенства, иначе переходит к анализу хвоста списка, и увеличивает номер на единицу.

2.delmax – удаляет указанный элемент из указанного списка. Аналогичен предикату del_all, только назван немного по-другому в связи с контекстом.

3.srednarifm – проходит по всему списку, накапливая сумму, и количество элементов, и в конце возвращая сумму, деленную на количество элементов. В случае пустого списка возвращает ноль.

4.mediana – предикат который вычисляет медиану. Он использует следующие предикаты.

mysort – предикат сортировки пузырьком, который использует swap.

mylength – предикат считающий длину списка ( для нахождение номера центрального элемента).

getelem – извлекает из списка элемент с указанным номером. Реализуется подсчетом номера, пока не доходим до нужного нам, после чего в результирующую переменную вносим голову списка, и выходим с предиката.

calcmediana – принимает 2 параметра – отсортированный список, его длину и

возвращает медиану, извлекая центральный элемент в случае нечетный длины, или 2 центральных элемента в случае четной длины, и возвращая их полусумму.

mediana – вызывает эти предикаты в таком порядке: mysort, mylength, calcmediana, и выводит полученный результат.

add – предикат, который используется для добавление элемента в список.

twolists – предикат, который принимает список, и возвращает 2, один – из положительных, 2-ой из отрицательных, с помощью функции twolist.

twolist – предикат, который формирует список из положительных элементов и остальных. Для этого он использует 5 параметров, 1-ый из которых передаваемый нами список, во 2-ом и 3-ем, он накапливает 2 списка, прибавляя элементы методом add, и 4-ый и 5-ый параметры – результирующие списки.

goal – предикат который является последовательным вызовом выше описанных, в соответствии с последовательностью заданий, с наличием сопровождающей информации.

Результаты тестирования:

  1. Создать список L всех элементов, содержащихся либо в списке S1, либо в списке S2, но не одновременно в S1 и S2.

Распишем подробнее как должна работать программа. Допустим мы имеем список [a,b,c] и [c, d, e] тогда результатом будет [a, b, d, e,]. Если есть повторяющиеся элементы в 1-ом из списков то мы их тоже включаем в результирующий, так как не указано что их надо удалять. Например

[a, a, b, b, c ,d] [b, c, d] результат – [a, a]

Код программы:

del(_,[],[]).

del(X,[X|T],L1):-del(X,T,L1).

del(X,[Y|T],[Y|T1]):- del(X,T,T1).

delfrom(L,[],L).

delfrom(L,[H|T],L1):-del(H,L,L2),delfrom(L2,T,L1).

readlist([],;).

readlist([X|Y],_):- read(X), readlist(Y,X).

goal:-write('Simvol okon4aniya stroki - ;\nVvedite 1-ii spisok\n'),readlist(X,z),write(X),write('\n'),

write('Vvedite 2-oi spisok\n'),readlist(Y,z),write(Y),write('\n'),delfrom(X,Y,X1),delfrom(Y,X,Y1),append(X1,Y1,Z),write(Z).

Предикат readlist принимает элементы то тех пор пока мы не введем “;”. Предикат del – удаляет все вхождения переданного ему элемента.

delfrom – поочередно берем каждый элемент 1-го списка, и удаляет его из 2-го, используя del.

В предикате goal мы сначала вводим 2 списка, а для того чтобы получить результат, мы вначале удаляем все элементы содержащиеся в 1-ом списке, которые есть во 2-ом, и затем наоборот, удаляем все элементы во 2-ом списке, которые есть в 1-ом, и затем объединяем 2 полученных списка.

Результаты тестирования:

3. Имеется база фактов, описываемая предикатом person(имя,

зарплата):

person(иванов, 1500)

person(петров, 2000)

person(сидоров, 1200)

person(иванов, 1200)

……..

1. Составить список ставок на предприятии (для приведенного

примера [1500,2000,1200])

2. найти среднюю зарплату на предприятии

3. опубликовать список лиц, получающих зарплату ниже

средней.

4. найти суммарную зарплату Ивановых.

Код программы:

person(ivanov,1000).

person(petrov,2000).

person(sidorov,1200).

person(ivanov, 1200).

add([H|T],El,[H|Res]):- add(T,El,Res).

add([],El,[El]).

helpstavki(L,RESULT):-person(_,Y),nomember(Y,L),add(L,Y,L1),!,helpstavki(L1,RESULT).

helpstavki(L,L).

nomember(_,[]):-!.

nomember(N,[H|_]):- H == N,!,fail.

nomember(N,[_|T]):-nomember(N,T).

srednzarpl([],SUM,K,R):- R is SUM / K.

srednzarpl([H|L],SUM,K,R):-SUM1 is SUM + H, K1 is K +1, srednzarpl(L,SUM1,K1,R).

malzarpl([],[],_).

malzarpl([H|L],[Name|LName],R):-H < R, write(Name),write(' '),malzarpl(L,LName,R).

malzarpl([_|L],[_|LName],R):-malzarpl(L,LName,R).

sum([],S,S):-!.

sum([H|L],S,RES):- S1 is S + H, sum(L,S1,RES).

stavki:-helpstavki(_,RESULT),write('Stavki na predpriyatii - '),write(RESULT),findall(S,person(_,S),L),srednzarpl(L,0,0,R),

write('\nSredniyaZarplata='),write(R),findall(D,person(D,_),LName),write('\nImeut zarplaty niwe crednei: '),malzarpl(L,LName,R),findall(S,person('ivanov',S),LSum),

sum(LSum,0,RES),write('\nZarplata ivanovix='),write(RES).

Вначале у нас идет набор фактов. Мы будем его на время тестов изменять.

1). Первое задание было выполнено не используя предикат findall. Для этого мы использовали предикат add (добавление элемента в список), и предикат nomember – он возвращал истину, если элемент не является членом списка. Мы рекурсивно вызываем предикат helpstavki, который вызывает рекурсию заново, когда мы добавили какой-то новый элемент в накапливаемый список (проверка идет с помощью nomember). Таким образом если элемент уже содержится в списке, мы переходим к следующему факту, если же предикат вернул истину, то для того чтобы мы имели возможность пройти по дальнейшему списку фактов, мы заново вызывает предикат helpstavki, и уже пройдем элемент который мы только что добавляли, и перейдем к следующему. В случае когда факты закончатся, подействует тривиальный случай, и результату будет присвоен текущий список.

2). Для извлечения зарплат используется findall(S,person(_,S),L), и далее рассчитывается среднее арифметическое по аналогии, как в 1-ом задании, предикатом srednarifm.

3). Чтобы получить фамилии людей, имеющих зарплату ниже средней, нам необходимо в рекурсии использовать 2 списка – список фамилий и ставок. Если ставка меньше среднего значения, то мы выводим соответствующую ей фамилию. Для этого мы последовательно одновременно берем головы и 1-го и 2-го списка. И далее тоже самое применяем и для хвостов. Это реализуется в предикате malzarpl.

4). Для вычисление зарплаты ивановых мы используем предикат findall findall(S,person('ivanov',S),LSum), и потом суммирует полученные значения списка LSum.

Результаты тестирования: Факты:

person(ivanov,1000).

person(petrov,2000).

person(sidorov,1200).

person(ivanov, 1200).

person(petrov,2000).

Person(ivanov,2000).

person(ivanov,1000).

person(petrov,2000).

person(kozlov,1500).

person(sidorov,1200).

person(ivanov, 1200).

person(ivanov, 500).

В случае отсутствия факта person программа, выдает ошибку, что является верным, т. к. отсутствуют необходимые для обработки факты

Вывод

В данной лабораторной работе были получены необходимые навыки работы со списками, поверхностно была рассмотрена работа с символами в SWI прологе, а так же, был изучен предикат findall, который использовался для извлечения значений из фактов.