- •Приложения
- •1. Основная терминология
- •II. Полезные программы Различные процедуры
- •Процедуры сбора множества ответов
- •Процедуры ввода-вывода низкого уровня
- •Программа "отобразить_состояние"
- •Экранно-ориентированная программа, предназначенная для выполнения запросов к базе данных
- •III. Показательный пример программа планирования работы завода Ситуация
- •Прототип программы
- •Примеры сеансов работы с программой
- •Пользование программой
- •Программа Алгоритм планирования
- •Оценка алгоритма планирования
- •Чтение текста программы
Оценка алгоритма планирования
В приведенной здесь форме планирующая компонента программы не способна найти оптимальный план для всех ситуаций, с которыми может столкнуться мастер. Она корректно работает только в тех производственных ситуациях, которые считаются нормальными. Однако для того, чтобы можно было работать в экстраординарных ситуациях, программа снабжена большим числом точек входа, обращаясь к которым мастер сможет манипулировать критически важными ресурсами с тем, чтобы повлиять на алгоритм планирования. Важной точкой входа, отсутствующей в прототипе, является точка входа, которая обеспечила бы возможность корректировки базы данных "заказ".
Чтение текста программы
Ниже приводится текст программы. Каждая компонента программы помечена в соответствии с ее уровнем и категорией (интерфейс с пользователем, вывод и т.д.). План программы, приведенный ранее, поможет разобраться в ней.
Соглашения об обозначениях
В программе содержится много процедур, названия которых заканчиваются цифрой "0". Это делается в соответствии с соглашением о том, что такие процедуры вызываются другими процедурами с точно таким же названиями, но без цифры "0" в конце. Например, процедура "построить_список накопит0" вызывается процедурой "построить_список_накопит". Назначение процедуры "построить_список_накопит" состоит в том, что она подготавливает аргументы для процедуры "построить_список_накопит0"; именно процедура "построить_список_накопит0" и выполняет основную работу.
% Программа планирования работы завода
% Соглашения об именах переменных:
% Накоп Наименование накопителя, т.е. 1,2,3,4 или 5
% НакопА, НакопБ НакопБ — это накопитель, располагающийся
% следующим после накопителя НакопА
% Накопители Это список, показывающий, какое
% количество изделий каждого заказа распо-
% лагается в каждом накопителе, т.е. список
% вида: [в (Накоп.Заказы),... ]
% НачНакопители Начальный список накопителей
% ЗакНакопители Заключительный список накопителей (после
% окончания обработки изделий)
% НакопПуть Список, состоящий из списков накопителей,
% по одному на каждый час
% Заказы Список заказов вида: [заказ (ИД, Количество),
% ...I
% ЗаказыХ Список заказов в час Х
% ЗаказыY Список заказов в час Y, где Y = Х + 1
% ЗаказыА Список заказов в накопителе А
% ЗаказыБ Заказы в накопителе Б (Б — это накопитель,
% расположенный вслед за Накопителем А)
% ЗаказыХА Список заказов в час Х в накопителе А
% ИД Уникальный идентификатор заказа
% СписокОпер Список количества операторов, приписанных к
% каждой установке, т.е. список вида:
% [оп(Установка, Количество),...]
% ОпПуть Список, элементами которого являются списки
% "СписокОпер", по одному на каждый час
% НачЧас Начальный час
% ЗакЧас Заключительный час
% Индекс Относительная мера того, насколько много
% работы сделано к концу смены
% ОстПроизводительность Остаточная производительность уста-
% новки в единицу времени после того, как
% был выполнен некоторый объем работ
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ПРОЦЕДУРЫ ИНТЕРФЕЙСА С ПОЛЬЗОВАТЕЛЕМ
%
% СТАРТ (уровень 1; интерфейс с пользователем)
текущий _час (8).
% + старт (Час) :-
retract (текущий _ час (_)),
assert (текущий _ час (Час) ),
nl,печ(' (Час равен '.Час,')'),
%
вводполя (' Нужно ли вводить новое задание? (да или нет) ,
Ответ, нет, элемент (Ответ, [да, нет])),
(Ответ = да, добавить_работу (Час, _) ; Ответ = нет ), ! ,
обработка_смены(меню, план. Час, [],[]).
% ---------------------------------------------------------------
% ОБРАБОТКА СМЕНЫ (уровень 2; интерфейс с пользователем)
% Процедура "обработка_смены" представляет собой самый высокий
% уровень интерфейса с пользователем - цикл опроса пользователя.
% Каждому элементу меню соответствует одно правило.
% Последней подцелью каждого правила является рекурсивный
% вызов данной процедуры.
конец_смены(13). % час, когда заканчивается смена.
% Условие окончания:
% . + + + + +
обработка_ смены (меню, _, Час, НакопПуть, ОпПуть) : -
конец_смены (Час),
печ (' Конец смены в час', Час, . ),
построить_список_накопит (Час, ЗакНакопители),
n1, печ(' Состояние заказов:'),
выдать _посл_ накопители ([ЗакНакопители]), ! .
обработка_ смены (меню, Умалч_знач, Час, НакопПуть, ОпПуть) :-
n1, печ(' (Час равен ', Час,')'), (
Умалч_ знач = план,
печ(' (требуется новый план) ')
;
true
),
печ(' команда? ( план , обработка» помощь - для вывода
списка '),
вводполя (' возможных команд) ',
Ответ, Умалч_знач,
( ОпПуть = [ ], % если список ОпПуть пуст:
( Ответ = план
;
печ(' Вы должны построить план! '),
fail
)
;
ОпПуть \ = = [ ], % если список ОпПуть не пуст:
( элемент (Ответ, [план, обработка, помощь,
' показать работу , ' показать операторов ',
' показать план' , 'конец дня' ])
;
печ (' Вы ошибочно ввели ', Ответ),
печ (' Правильные команды: '),
печ_элем(' ', [план, обработка, помощь,
' показать работу ',
' показать операторов ', ' показать план ',' конец дня ' ]),
fail
)
)
% конец аргумента "Условие" процедуры
% "вводполя" ).
! , обработка_ смены (Ответ, обработка. Час, НакопПуть, ОпПуть).
обработка_смены (план, Умалч_знач, Час, _, _) : -
план_ смены (Час, НакопПуть, ОпПуть),
! , обработка_смены (меню,Умалч_знач, Час, НакопПуть. ОпПуть).
обработка_смены (обработка, Умалч_знач, Час,
[Накопители [ НакопПуть],
[СписокОпер 1 ОпПуть] ) :—
НовЧас is Час + 1,
retract (текущий _ час (Час) ),
assert (текущий _ час (НовЧас)),
% добавить факты "заказ/4" для нового часа:
обработка _ часа (НовЧас, Накопители),
! , обработка_смены(меню, Умалч_знач, НовЧас, НакопПуть, ОпПуть).
обработка _ смены (по мощь, Умалч_знач, Час, НакопПуть, ОпПуть) : —
печ (' Возможные команды: '),
печ_элем(' ',
[' план (составить план на оставшуюся часть смены) ',
' обработка (смоделировать работу за следующий час) ',
' помощь ',
' показать работу (повторно вычислить и показать ',
' текущее состояние заказов) ',
' показать операторов (текущее распределение операторов) ',
' показать план (план на оставшуюся часть смены) ',
' конец дня']),
! , обработка_смены (меню, У малч_знач, Час, НакопПуть, ОпПуть).
обработка_смены (' показать работу ', Умалч_знач, Час,
[Накопители | НакопПуть],
[СписокОпер | ОпПуть] ) : —
% построить список "НовНакопители", чтобы заменить
% список "Накопители" для данного часа:
построить_ список _накопит (Час, ТНакопители),
макс_операторов (Час, МаксОпер),
от_накопит_к_накопит (МаксОпер, ТНакопители, СписокОпер,
НовНакопители),
%
печ(' Заказы на ', Час,' часов '),
write ('Накопитель '),
заголовок_списка_накопит (4),
выдать_список_накопит (ТНакопители),
вводполя (' Нужно ли вводить новое задание? (да или нет) ',
Ответ, нет, элемент (Ответ, [да,нет])),
% (Ответ = да, добавить работу (Час, РабФлаг)
;
Ответ = нет, РабФлаг_нет_ новой работы
),
!,
( РабФлаг = новая работа,
write(' Заказ был добавлен, так как существовал '),
write (' текущий план '), n1,
печ(' Строится новый план ...'),
обработка_смены (план, Умалч_знач, Час, [],[])
;
РабФлаг = нет_новой_работы,
обработка смены (меню, Умалч_ знач. Час,
[НовНакопители | НакопПуть],
[СписокОпер | ОпПуть])
).
обработка_смены(' показать операторов ', Умалч_знач, Час,
НакопПуть, [СписокОпер ! ОпПуть] ) : -
печ(' Операторы в ', Час,' часов '),
выдать_список_операторов (СписокОпер),
write (‘Добавить или удалить операторов? '),
вводполя (' (добавить, удалить, продолжить)
;
Ответ= продолжить,
элемент (Ответ, [добавить, удалить, продолжить] )),
( ( Ответ = добавить ; Ответ = удалить ),
корр_операторов (Час, Ответ)
;
Ответ = продолжить
),
! , обработка_ смены (меню, Умалч_ знач. Час, НакопПуть,
[СписокОпер | ОпПуть]).
обработка _ смены ('показать план ', Умалч_знач, Час, НакопПуть, ОпПуть) :-
n1,
печ(' Текущее распределение операторов: '),
выдать_оп_путь(Час, ОпПуть), n1,
%
конец_ смены (Конец),
write (' приведет к нижеследующему состоянию '),
печ(' заказов ',' к ', Конец,' часам:'),
выдать _ посл _ накопители (НакопПуть),n1,
!, обработка_ смены (меню, Умалч_знач, Час, НакопПуть, ОпПуть).
%-----------------------------------------------------------------------
% СПЛАНИРОВАТЬ СМЕНУ (уровень 3; интерфейс с пользователем)
%
% Построить план на оставшуюся часть смены, начиная с часа
% Час и кончая концом смены
% • + - -
план смены (Час, НакопПуть, ОпПуть) :—
конец _ смены (ЗакЧас),
найти_оптимальное_распределение(Час, ЗакЧас, НачНакопители,
ННакопПуть, НОпПуть),
%
% Переменная НачНакопители содержит описание работы за час
% (перед началом обработки).
n1,
печ (' Нижеследующее распределение операторов: '),
выдать_оп_путь(Час, НОпПуть), n1,
%
печ (' приведет к нижеследующему состоянию заказов ', к ', ЗакЧас, ' часам: ' ),
% вывести последний список накопителей, содержащийся в
% новой переменной НакопПуть:
выдать_посл_накопители (ННакопПуть),n1,
%
печ (' введите принять, если план подходит, или '),
вводполя (' корректировка, если план нужно изменить ',
Ответ, принять,
( элемент (Ответ, [ принять,корректировка ])
;
печ(' неверный ответ '), fail)
), ( Ответ = принять,
НакопПуть = ННакопПуть,
ОпПуть = НОпПуть
;
Ответ = корректировка,
корр_ плана (Час, НачНакопители, ННакопПуть, НОпПуть,
НакопПуть, ОпПуть)
).
%--------------------------------------------------------------------
% ОБРАБОТКА ЧАСА (уровень 3; интерфейс с пользователем)
%
% Добавить факты "заказ" с ненулевыми количествами изделий.
% + +
обработка_ часа (Час, [ в (Накоп,3аказы) | Накопители] ) :-
обработка_ накопителя (Час, Накоп, Заказы),
! , обработка часа (Час, Накопители).
обработка_часа(_, [ ] ).
% + + +
обработка накопителя (Час, Накоп, [заказ (ИД,Количество) ; Заказы])
:-
( Количество \ = = 0,
assert( заказ (Час, Накоп, ИД, Количество))
;
Количество = 0
),
! , обработка_накопителя (Час, Накоп, Заказы).
обработка _ накопителя (_, _, [ ] ).
%-------—------------------------
% ДОБАВИТЬ РАБОТУ (уровень 3; интерфейс с пользователем)
% + -добавить_работу (Час, Флаг) : —
% проверить ИД по списку принятых заказов.
вводполя(' Введите наименование заказа или слово "конец" ',
ИД, конец, true),
( ИД = конец,
Флаг = нет _ новой _ работы
;
ИД \ = = конец,
вводполя ( ' Количество изделий?',
НКоличество, 0, НКоличество > 0),
assert ( заказ (Час, 1, ИД, НКоличество) ),
печ (' Заказ добавлен '),
Флаг = новая _ работа,
добавить_работу(Час, _)
).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ПРОЦЕДУРЫ ПЛАНИРОВАНИЯ
%
% НАЙТИ ОПТИМАЛЬНОЕ РАСПРЕДЕЛЕНИЕ операторов смены.
% (уровень 4; планирование)
% ++---_
найти_оптимальное_распределение (НачЧас, ЗакЧас, _,_,_) :-
% построить начальный список накопителей, НачНакопители:
построить _ список _ накопит (НачЧас, НачНакопители),
assert (оптимальный план (НачНакопители, 0, НачНакопители,
[НачНакопители], [ ])) ,
%
% поиск с возвратом по всем возможным распределениям на
% смену:
найти_ распределение (НачЧас, ЗакЧас, НачНакопители,
ЗакНакопители, НакопПуть, ОпПуть),
индекс_списка_накопителей(ЗакНакопители, Индекс),
оптимальный_план(_, ОптимальныйИндекс, _, _,__),
( Индекс > ОптимальныйИндекс,
один_раз (retract (оптимальный_план (__,_,_,_,_) ) ),
assert (оптимальный _ план (ЗакНакопители, Индекс,
НачНакопители,
НакопПуть, ОпПуть) )
;
Индекс = < ОптимальныйИндекс
), fail.
найти _ оптимальное _ распределение (_, _, Накопители, НакопПуть,
ОпПуть) :-
retract( оптимальный_план(_, _, Накопители,
НакопПуть, ОпПуть) ), ! .
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
% ПОСТРОИТЬ СПИСОК НАКОПИТЕЛЕЙ (уровень 5; планирование)
% Выяснить объем работы на каждом накопителе для часа Час:
% + -
построить_список_накопит(Час, Накопители) :-
findall(S, накопитель (S), S Список),
сп_неповт_знач(ИДО, заказ (_,_, ИДО, _) .СписокИД),
построить_список_накопит0 (Час, СписокИД, S Список, Накопители), ! . построить_список_накопит0(_,_,[],[])
% + + +
построить список _ накопит0 (Час, СписокИД,[S | SСписок],
[ в (S, Заказы) | Накопители]) : -
findаll(заказ (ИД1, Количество),
(элемент (ИД1, СписокИД),
( заказ (Час, S, ИД1, Количество)
;
not( заказ (Час, S, ИД1, Количество) ),
Количество =0 % 0 если отсутствуют факты "заказ"
)
), % конец второго аргумента предиката "findall"
Заказы),
! , построить_ список _накопит0 (Час, СписокИД, SСписок, Накопители).
%___________________________
% ИНДЕКС СПИСКА НАКОПИТЕЛЕЙ (уровень 5; планирование)
% Вычислить значение индекса для списка накопителей. Это значение
% характеризует объем выполненной работы. Для каждого
% накопителя умножить число, являющееся обозначением накопителя,
% на объем работы на этом накопителе.
индекс_ списка_ накопителей ([], 0).
% . +
индекс списка накопителей ([в (Накоп, Заказы) | Накопители],
%
S Индекс) :—
индекс_ списка_заказов (Заказы, Индекс3),
!, индекс списка накопителей (Накопители, SИндекс0),
SИндекс is SИндекс0 + (Накоп * Индекс3):
индекс _ спи ска_ заказов ([ ], 0).
индекс списка заказов ([заказ (_, Количество) | Заказы | , ИндексЗ) :-
!, индекс__списка_ заказов (Заказы, Индекс30),
Индекс3 is Индекс30 + Количество.
%--------------------------------------------------------
% НАЙТИ РАСПРЕДЕЛЕНИЕ (уровень 5; планирование)
%
% Это — базовый блок построения плана. Он вычисляет ОпПуть (т.е.
% распределение операторов на период от НачЧас до ЗакЧас) и
% НакопПуть (т.е. объем работы, который будет выполнен в резуль-
% тате данного распределения операторов). Процедура рекурсивно
% вызывает сама себя один раз на каждый час смены.
% Окончить, если достигнут заключительный час:
% + + +
найти_ распределение (ЗакЧас, ЗакЧас, ЗакНакопители, ЗакНакопители,
% - -
[], []).
найти _ распределение (НачЧас, % + начальный час
ЗакЧас, %+заключительный час
НакопителиХ, % + работа, выполненная за
% предыдущий час
ЗакНакопители, %—работав конце смены
[НакопителиY | НакопПуть], % -
[СписокОпер | ОпПуть ] % -
):-
НачЧас< ЗакЧас,
макс_операторов (НачЧас, МаксОпер),
%
% поиск с возвратом по всем распределениям часа:
распределить__операторов (НачЧас, МаксОпер, НакопителиХ,
СписокОпер),
от _ накопит _к_ накопит (МаксОпер, НакопителиХ, СписокОпер,
Накопители Y),
НовЧас is НачЧас + 1, найти _ распределение (НовЧас, ЗакЧас, НакопителиY,
ЗакНакопители, НакопПуть, ОпПуть).
%---------------------------------------------------------------------------
% РАСПРЕДЕЛИТЬ ОПЕРАТОРОВ (уровень 6; планирование)
%
% По заданному значению переменной "Накопители" (список того,
% как много работы собрано в каждом накопителе) выработать
% значение переменной "СписокОпер" (распределение операторов) -
% Для каждого накопителя осуществляется рекурсивный вызов
% данной процедуры.
распределить _ операторов (Час, % +
Опер, % +операторы, подлежащие
% распределению
[в (НакопА, ЗаказыА) \ Накопители], % спи-
% сок накопителей (+)
[оп(Уст,Чис) [СписокОпер] % —распре-
% деления операторов
) :-Опер > 0, % не пользоваться данным правилом, если 0
% операторов
наименование _ установки (Уст, НакопА, НакопБ),
( пусто (ЗаказыА), % оставшиеся заказы — нулевое коли-
% чество изделий
Чис = 0,
НовОпер is Опер
;
not ( пусто (ЗаказыА) ),
производит _ уст (Уст, Опер, Чис, _), % сгенерировать значение
% переменной "Чис".
%
% послать на установку, по крайней мере, одного оператора,
% если в накопителе НакопА есть работа:
Чис > 0, % не разрешается умалчиваемое значение произ-
% водительности, равное нулю
НовОпер is Опер — Чис
),
распределить_операторов (Час, НовОпер, Накопители, СписокОпер) .
% Приписать к установке 0 операторов, е.сли не осталось свободных
% операторов (нулевое значение второго аргумента) или если список
% ЗаказыА пуст; и списке Накопители (третий аргумент) должно быть
% не менее двух элементов:
распределить_ операторов (Час, % +
0, % +
опер [ в (НакопА, ЗаказыА), % +
в(НакопБ, ЗаказыБ) | Накопители ],
[оп(Уст, 0) | СписокОпер ] % -
) :-
пусто (ЗаказыА),
наименование _ установки (Уст, НакопА, _),
распределить_операторов (Час, 0,
[в (НакопБ, ЗаказыБ) i Накопители],
СписокОпер).
% Следует распределить всех операторов. Процедура "распределить _
% операторов" потерпит неудачу и вернется назад, если значение
% переменной "Опер" (второй аргумент) не равно нулю для последней
% структуры "в (_,_)" в списке накопителей:
распределить_ операторов (_, 0, [в(_ , )], [ ]).
% ПУСТОЙ СПИСОК ЗАКАЗОВ (используется в процедуре
% "распределить_операторов")
% Проверить то, что во всех структурах "заказ (_, _) " имеется
% нулевое количество изделий:
пусто ([ заказ (_,0) [Заказы]) :.—
пусто (Заказы).
пусто([]).
%-----——------------------------------
% ОТ НАКОПИТЕЛЯ К НАКОПИТЕЛЮ (уровень 6; планирование)
% По заданным значениям переменных НакопителиХ (положение
% заказов в час X) и СписокОпер (распределение операторов) вычислить
% значение переменной Накопители Y (положение заказов в час Y,
% гдеY=Х+1).
% На каждый накопитель приходится один рекурсивный вызов данной
% процедуры.
% + + +
от _ накопит _к_ накопит (МаксОпер, НакопителиХ, СписокОпер,
% -
Накопители Y) :—
% построить пустой список "ПрибывающаяРабота":
сп _ неповт _ знач (заказ (ИД, 0), заказ ( _, _, ИД, _ ) ,
Прибывающая Работа),
от _ накопит _ к накопитО (Мак сОпер, Прибывающая Работа,
НакопителиХ, СписокОпер, НакопителиY).
% последний элемент "в(_, _)" в списке накопителей:
от_ накопит _ к _накопит0(_, % +
ПрибывающаяРабота, % +
[в(_,ЗаказыХ)], % +
СписокОпер, % +
[в(_,ЗаказыY)] % -
) :-
от заказа к заказу (0, ПрибывающаяРабота, ЗаказыХ, ЗаказыY), !
от накопит_к_накопит0 (МаксОпер, %+
ПрибывающаяРабота, % +
[в (НакопА,ЗаказыХА) , % + работа час X
в (НакопБ ,ЗаказыХБ) ; НакопителиХ],
СписокОпер, % +
[в (НакопА,ЗаказыYА), % - работа час Y
в (НакопБ ,ЗаказыYБ); НакопителиY]
) :-
% найти значения переменных "Число_опер" и "Установка" для нако-
% пителей А и Б в списке "СписокОпер":
найти_установку (СписокОпер, Установка, НакопА, НакопБ,
Число_опер),
производит_уст (Установка, МаксОпер, Число_опер,
Производительность),
от_заказа_к_заказу (Производительность, ПрибывающаяРабота,
ЗаказыХА, ЗаказыYА,
СделаннаяРабота),
!, от_накопит_к_накопит0 (МаксОпер,
СделаннаяРабота,
[в (НакопБ,ЗаказыХБ); НакопителиХ],
СписокОпер,
[в (НакопБ ,ЗаказыYБ); НакопителиY]) .
% ----------------------------------------------------------------
% ОТ ЗАКАЗА К ЗАКАЗУ (уровень?; планирование)
%
% По заданным значениям переменных "Производительность" (произ-
% водительность установки), "ЗаказыХ" (список работы последнего
% часа) и по списку прибывающей работы, поступающей в виде обра-
% ботанных за этот час изделий, выработать значение переменной
% "ЗаказыY" (новый список работы на следующий час) .
% Осуществляется по одному рекурсивному вызову данной процедуры
% на каждый заказ.
от_заказа_к_заказу (_, [], [], [], [)).
от_заказа_к_ заказу (Производительность, %+
[заказ (Наименование,КоличествоПР) | ПрибывающаяРабота],
% + Прибывающая работа
[заказ (Наименование,КоличествоХ) | ЗаказыХ],
% + Заказы в час Х
[заказ (Наименование.КоличествоY) | ЗаказыY],
% — Заказы в час Y,
[заказ (Наименование,КоличествоСР) | СделаннаяРабота]
% — Сделанная работа
) :-
Производительность > 0,
Фактически_сделано (КоличествоХ, Производительность,
КоличествоСР),
КоличествоY is КоличествоХ — КоличествоСР + КоличествоПР,
ОстПроизводительность is Производительность — КоличествоСР,
!, от_заказа_к_заказу (ОстПроизводительность,
ПрибывающаяРабота, ЗаказыХ, ЗаказыY,
СделаннаяРабота).
!, от_заказа_к_заказу (0, % + Производительность
[заказ (Наименование,КоличествоПР) | ПрибывающаяРабота],
% + Прибывающая работа
[заказ (Наименование,КоличествоХ) | ЗаказыХ],
% + Заказы в
% час Х
[заказ (Наименование.КоличествоY) | ЗаказыY],
% — Заказы в
%часY
[заказ (Наименование, 0) | СделаннаяРабота] % Сделанная работа
) :-%
добавить объем поступившей работы:
КоличествоY is КоличествоХ + КоличествоПР,
% Сделанный объем работ равен нулю:
!, от_заказа_к_заказу(0, ПрибывающаяРабота, ЗаказыХ,
ЗаказыY, СделаннаяРабота).
% _ _ _ _ _ _ _ _ _ _ _
% НАЙТИ УСТАНОВКУ (уровень 7; планирование) %
% По заданному значению переменной "СписокОпер" (список распре-
% деления операторов) и по наименованиям двух накопителей,НакопА
%и НакопБ, выработать значения переменной Уст (наименование ус-
%тановки, которая обрабатывает изделия, поступающие с НакопА,
% и помещает их в НакопБ) и переменной Чис (число операторов на
% установке).
% + - + +
найти_-установку ([оп (Уст,Чис) | СписокОпер], Уст, НакопА, НакопБ,
%
Чис) :-
наименование_установки (уст, НакопА, НакопБ), !.
найти_установку ([_ | СписокОпер], Уст, НакопА, НакопБ, Чис) :—
найти_установку (СписокОпер, Уст, НакопА, НакопБ, Чис),!.
%----------------------------------------
% ФАКТИЧЕСКИ СДЕЛАННАЯ РАБОТА (уровень 8; планирование)
%
% Если Объем работы меньше имеющейся Производительности,
% то Количество обработанных изделий равно Объему работы,
% в противном случае Количество обработанных изделий равно
% Производительности:
% + + -
фактически—сделано (О, П, К) : — О< 0, К = О, !.
фактически_сделано (О, П, К) :-О-П>0, К=П,!.
фактически„сделано(О,П,К) :-О-П=< 0, К=О,!.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% БАЗЫ ДАННЫХ И ИНТЕРФЕЙСЫ БАЗ ДАННЫХ
%------——-----------------------
% БАЗЫ ДАННЫХ "ПРОИЗВОДИТЕЛЬНОСТЬ УСТАНОВКИ"
% (уровень 7; планирование) %
% Производительность установок в час; Переменная "Число" обозна-
% чает число операторов.
% . +Наим.+ ?
производит_уст (альфа, МаксОпер, Число, Производительность) :—
( % если переменная "Число" не конкретизирована, то
% сгенерировать ее значение:
var (Число), ген_чисел (МаксОпер, Число)
;
nonvar (Число), Число > 0
),
% 5 изделий на оператора в час:
Производительность is Число * 5.
производит_уст (бета, МаксОпер, Число, Производительность) :—
( var (Число),
ген_чисел (МаксОпер, Число)
;
nonvar (Число), Число > О
),
( Число =1, Производительность = 15
;
Число = 2, Производительность = 20
;
Число > 2,
Производительность is 20 + ( (Число — 2) * 2)
% если на установке работает более двух операторов,
% то установка производит по два дополнительных изделия
% на каждого оператора сверх двух.
).
производит_уст (гамма, _, Число, Производительность) :-
% на установке "гамма" должны работать ровно два оператора:
( var (Число), Число = 2
;
nonvar (Число), Число >= 2
),
Производительность = 20.
производит—уст (дельта, МаксОпер, Число, Производительность) :—
( var (Число), ген_чисел (МаксОпер, Число)
;
nonvar (Число) , Число > О
),
% 8 изделий на оператора в час:
Производительность is Число * 8.
% умалчиваемое значение:
производит_уст(_, _, 0,0).
%-------------------------------
% МАКСИМАЛЬНОЕ ЧИСЛО ОПЕРАТОРОВ
% (уровень 6; планирование) % + _
макc_операторов (Час, Число) :—
% построить список операторов, все еще находящихся на работе
% в течение часа "Час":
findall(X, послать (Час, оператор (X) ), Список),
length(Список,Число),!. %_____________________________________________________________________
%БАЗА ДАННЫХ "ОПЕРАТОР" (уровень 7; планирование)
% Данные факты вводятся в программу интерфейсом с таймером.
% 8 = 8 часов утра (время прихода оператора на работу)
оператор (8, джек).
оператор (8, марта).
оператор (8, сюзан).
оператор(8,барни).
оператор (8, фред).
% дуг приходит позже:
% оператор (9, дуг).
% барни и фред уходят с работы в 11.00:
% опровергнуть(11,оператор(8,барни)),
% опровергнуть (11, оператор (8, фред) ).
%_____________________________________________________________________
% БАЗА ДАННЫХ "НАКОПИТЕЛЬ" (уровень 6; планирование)
% Накопитель — это место, где накапливаются изделия перед обработ-
% кой на установке и после обработки.
% 1 == совершенно не обработано;
% 5 == обработка закончена.
накопитель (1). % начало.
накопитель (2).
накопитель (3).
накопитель (4).
накопитель (5). % конец.
%_____________________________________________________________________
% БАЗА ДАННЫХ С НАИМЕНОВАНИЯМИ УСТАНОВОК
% (уровень 7; планирование)
% установка "альфа" обрабатывает детали, поступающие из накопителя
% 1 и помещает обработанные детали в накопитель 2:
наименование_установки (альфа, 1, 2).
наименование_установки (бета, 2, 3).
наименование_установки (гамма, 3, 4),
наименование_установки (дельта, 4, 5).
%_____________________________________________________________________
% БАЗА ДАННЫХ "ЗАКАЗ" (уровень 6; планирование)
% Каждый заказ, подлежащий обработке, представляется в программе
% в виде фактов нижеследующей формы:
% Час Накопитель ИД Изделий
заказ(8, 1, доу, 18).
заказ(8, 1, ' макгроу, 25).
%_____________________________________________
% БАЗА ДАННЫХ С ИНФОРМАЦИЕЙ О ВРЕМЕНИ
%
% Данная база данных позволяет процедуре "послать" находить кор-
% ректное число операторов для заданного часа.
порождение (9, 8).
порождение (10, 9).
порождение (11, 10).
порождение (12, 11).
порождение (13, 12).
порождение (14, 13).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ПРОЦЕДУРЫ ВЫВОДА ИНФОРМАЦИИ
%
% ВЫДАТЬ ОП ПУТЬ (уровень 3; вывод)
% Вывести распределения операторов по часам рабочей смены.
выдать_оп_путь( , []).
выдать_оп путь (Час, [СписокОпер ! ОпПуть]) :—
length (СписокОпер, Чис), % Чис = число установок.
write (' Час '),
оп _путь_заголовок (Чис).
выдать_оп_путь0 (Час, [СписокОпер! ОпПуть]).
оп_путь_заголовок (0) :— nl. % переход к новой строке.
оп_путь_заголовок (Длина) :—
Длина > 0,
write (' Уст. On. '),
НовДпина is Длина — 1,
оп_путь_заголовок (НовДпина).
выдать_оп_путь0 (_, [ ] ) .
выдать—оп_путь0 (Час, [СписокОпер \ ОпПуть]) :—
write (Час), write (' '), % табуляция
выдать_список_операторов (СписокОпер),
НовЧас is Час + 1,
Выдать_oп_путь0 (НовЧас, ОпПуть) .
выдать_список_операторов ([]):— nl. % переход к новой строке.
выдать_список_операто ров ([оп (Установка,Опер) | СписокОпер]) :—
write (Установка), write (' '), % табуляция
write (Опер), write (' '),
выдать_список_операторов (СписокОпер).
%_____________________________________________________________________
% ВЫДАТЬ ПОСЛЕДНИЙ СПИСОК НАКОПИТЕЛЕЙ
% (уровень 3; вывод)
%
% Перейти к последней переменной "Списки", а затем вызвать про-
% цедуру "выдать список_накопит"
выдать_посл_накопители ([Накопители 1 НакопПуть] ) :-
НакопПуть \ == [ ],
выдать_посл_накопители (НакопПуть).
выдать_посл_накопители( [Накопители]) :— % последний элемент
% списка "НакопПуть".
write (' Накопитель ' ),
заголовок_списка„накопит (4),
выдать_список„накопит (Накопители).
заголовок_списка__накопит (0) :— nl.
заголовок_списка_накопит (Длина) :—
Длина > 0,
write (' Идентификатор Кол-во '),
НовДпина is Длина — 1,
заголовок_списка_накопит(НовДлина).
%_____________________________________________________________________
% ВЫДАТЬ СПИСОК НАКОПИТЕЛЕЙ (уровень 4; вывод)
%
выдать_список_накопит( [ ]).
выдать_список_накопит ([в (Накоп.Заказы); Накопители]) :-
write (Накоп), write (' '), % табуляция
выдать_список_заказов (Заказы), nl,
выдать_список_накопит (Накопители).
выдать_список_ заказов ([ ]).
выдать_список_заказов ([заказ (ИД,Количество); Заказы]) :-
write (ИД), write (' '),
write (Количество), write (' '),
выдать_список_заказов (Заказы).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ПРОЦЕДУРЫ КОРРЕКТИРОВКИ
%
% КОРРЕКТИРОВКА ОПЕРАТОРОВ (уровень 3; корректировка)
корр_операторов (Час, добавить) :—
write (' Введите имя оператора или слово "конец" '),
вводполя (' для продолжения работы ',
Ответ, конец, true),
( Ответ = конец
;
Ответ \ == конец,
assert (оператор (Час, Ответ) ),
write (' Оператор добавлен '),
корр„операторов (Час, добавить)
).
корр_операторов (Час, удалить) :—
write (' Введите имя удаляемого оператора или '),
вводполя (' слово "конец" ', Ответ, конец, true),
( Ответ = конец
;
Ответ \ == конец,
assert ( опровергнуть (Час, оператор (_, Ответ)) ),
write (' Оператор удален '),
корр_операторов(Час,удалить)
).
%_______________________________________________________________
% КОРРЕКТИРОВКА ПЛАНА (уровень 4; корректировка)
корр_плана (Час, %+
НачНакопители, %+ работа для часа "Час"
[НакопителиХ ! НакопПутьХ], % + текущ. НакопПуть
[СписокОперХ; ОпПутьХ], %+ текущ. ОпПуть
[НакопителиY [ НакопПутьY], % — откорр. НакопПуть
[СписокОперY ; ОпПутьY] % - откорректир. ОпПуть
) :-
корр_списка_опер(Час, НачНакопители, НакопителиY,
СписокОперХ, СписокОперY),
НовЧас is Час + 1,
корр_плана (НовЧас, НакопителиY, НакопПутьХ, ОпПутьХ,
НакопПутьY, ОпПутьY).
% значение переменной "НакопителиY" вырабатывается процеду-
% рой, "корр_списка_опер" и является вторым аргументом
% при рекурсивном вызове
корр_плана (Час, НачНакопители, [ ], [ ], [ ], [ ]) :—
nl.
печ (' Планируемая работа завода к концу смены: '),
write (' Накопитель ' ),
заголовок_списка_накопит(4),
выдать_список_накопит(НачНакопители),!.
%________________________________________________________________
% КОРРЕКТИРОВКА СПИСКА ОПЕРАТОРОВ (уровень 5; корректировка)
%
% интерфейс с процедурой "корр_списка_опер0".
% + + _ +
корр_списка_.опер(Час, НачНакопители, НовНакопители, СписокОперХ,
%
СписокОперY) : -
n1,
печ(' Планируемая работа завода на ', Час, ' часов: '),
write (' Накопитель ),
заголовок_списка_накопит (4),
выдать_список_накопит (НачНакопители),
%
n1,
печ (' Текущее распределение операторов: ' ),
выдать_список_операторов (СписокОперХ),
n1,
%
макс_операторов (Час, МаксОпер),
корр_списка_опер0 (МаксОпер, СписокОпер, СписокОперY),
%
% вычислить значение переменной "НовНакопители" (резуль-
% тат нового распределения операторов) :
от_накопит_к_накопит (МаксОпер, НачНакопители,
СписокОперУ, НовНакопители) .
корр_списка_опер(_, _, [ ], [ ], [ ]).
% КОРР_СПИСКА_ОПЕРО
% Корректировать все распределения операторов для заданного часа. корр_списка_опер0 (МаксОпер, %+
[оп (Уст,ЧисХ) | СписокОперХ], %+
[оп (Уст,ЧисY) | СписокОперY] %-
):-
наименование_установки (Уст, НакопА, НакопБ),
печ(' Установка: ', Уст, ' между ', НакопА, ' и ',
НакопБ, ' '),
% проверить новое значение:
вводполя (' Количество операторов: ', ЧисY, ЧисХ,
( ЧисY > = 0, ЧисY =< МаксОпер ) ) ,
!, корр_списка_опер0 (МаксОпер, СписокОперХ,
СписокОперY).
корр списка_опер0 (—, [], []):—!.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ПРОЦЕДУРЫ ВВОДА-ВЫВОДА НИЗКОГО УРОВНЯ
% ПЕЧАТЬ
% Печать от одного до семи аргументов и перевод строки:
печ(А) :—write(A), nl,
печ (А, Б) :-writf(A),write(Б),nl.
печ(А,Б,В) :-write(A),write(Б),write(B), nl.
печ (А, Б, В, Г) :-write(A),write(Б),write(B),write(Г),nl.
печ(А, Б, В, Г, Д) :- write(A), write (Б), write(B),
write (Г), write (Д),n1.
печ(А, Б, В, Г, Д, Е) :- write(A), write (Б), write(B), write (Г),
write(Д), write (E),nl.
печ(А, Б, В, Г, Д, Е, Ж) :- write(A), write (Б), write(B), write (Г) ,
write (Д), write (Е), write (Ж), nl.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% РАЗЛИЧНЫЕ ПРОЦЕДУРЫ
%
% ПЕЧАТЬ ЭЛЕМЕНТОВ
печ_элем (Смещение, [A | R]) :-
write (Смещение), write(A), nl,
печ _элем (Смещение, R).
печ_элем(_, []).
% ГЕНЕРАТОР ЧИСЕЛ
% Сгенерировать целое число N из диапазона от 1 до Макс, включая
% границы диапазона:
ген_чисел (Макс, N) :-
ген_чисел0 (Макс, 1, N).
ген_чисел0 (Макс, Начало, Начало).
ген_чисел0 (Макс, Начало, N) :—
НовНачало is Начало + 1,
НовНачало =< Макс,
ген_чисел0 (Макс, НовНачало, N).
% СУММА СПИСКА ЧИСЕЛ
сумма([], 0).
сумма ([F \ R], Итог) :-
сумма (R, Врем),
Итог is F + Врем.
% ЧИСТКА
чистка :- % избавиться от всех заказов, добавленных после
% 8 часов утра.
заказ (Час, А, Б, В),
Час>8,
retract (заказ (Час, А, Б, В) ), fail.
чистка :— % переустановить часы
retract (текущий_час (_) ),
assert (текущий_час (8) ).
% ИСПОЛЬЗУЕМЫЙ ФАЙЛ
:-consult(' послать '). % механизм наследования