Объекты и полиморфизм. Ч.2: различия между версиями
(→Ссылки) |
|||
Строка 109: | Строка 109: | ||
[[Категория:VipLanguage]] | [[Категория:VipLanguage]] | ||
[[en:Objects and Polymorphism]] | [[en:Objects and Polymorphism]] | ||
{{Полиморфизм в объектах. Навигатор}} |
Версия 13:43, 1 ноября 2007
Объекты и полиморфизм |
---|
Пример. Планирование работ
Теперь попытаемся объединить кое-что из вышесказанного для разработки простого, но достаточно мощного планировщика работ.
Работой является некоторый вычислительный процесс, который должен быть выполнен. Вам нужно зарегистрировать работу в планировщике, затем планировщик начнет эту работу в требуемый момент времени. Планировщик сравнительно легко обрабатывает повторяющиеся задачи, задачи, которые начинаются на Х минут ранее заданного времени и т.д. Здесь мы рассмотрим только функциональность планировщика.
Планировщик будет опираться на события таймера: каждый раз, когда событие таймера запускается, планировщик просматривает работы, находящиеся в ожидании и исполняет их. В этом примере время имеет тип integer. В программе могут быть использованы несколько планировщиков, поэтому представлять каждый планировщик будет объект. Планировщик описывается интерфейсом, или типом объекта. Объявление планировщика выглядит так:
interface scheduler domains job = (integer Time) procedure (i). predicates registerJob : (integer Time, job Job). predicates timerTick : (integer Time). end interface scheduler
Job это предикатное значение. Job получает время через параметр. Это сделано в целях иллюстрации и для простоты. В реальности, лучше передать время объекту scheduler, в который мы будем добавлять и другие предикаты. Таким способом, job может сам решить, какая информация ему нужна.
registerJob имеет очевидное назначение.
timerTick должен периодически вызываться, т.к. является сердцем планировщика.
Раздел implement выглядит так:
implement scheduler open core, priorityQueue facts jobQueue : queue{integer, job}. clauses new() :- jobQueue := empty. clauses registerJob(Time, Job) :- jobQueue := insert(jobQueue, Time, Job). clauses timerTick(Time) :- if tuple(T, Job) = tryGetLeast(jobQueue), T <= Time then Job(Time), jobQueue := deleteLeast(jobQueue), timerTick(Time) end if. end implement scheduler
Большинство членов этого раздела очевидны. Главное состоит в том, что приоритет в виде времени используется для сохранения работ. Это удобно, т.к. самые ожидаемые работы оказываются наиболее доступными. Факт jobQueue инициализируется конструктором, а предикат registerJob вставляет работу в очередь с приоритетом. Важный момент имеет место в предикате timerTick. Он вставляет в очередь «наименьшую» работу. Если эта работа должна была стартовать сейчас или ранее, то работа запускается и удаляется из очереди, и затем рассматривается оставшаяся часть очереди. Если «наименьшая» работа не находится в состоянии ожидания, то ничего не происходит, т.е., ожидается следующий тик времени. Перед началом использования планировщика, следует обратить внимание на то, что имеются несколько причин, почему сам планировщик не содержит таймера:
- Может оказаться важной синхронизация с внешними часами
- В некоторых контекстах увеличение параметра Time не должно быть одинаковым
- В некоторых контекстах увеличение параметра Time не соответствует реальному времени (например, шаги в играх, последовательность рабочих операций и т.д.)
- В GUI приложениях может быть важным, чтобы timerTick вызывался оконным событием, т.е. поддержка приложения с одним потоком (thread).
Использование внешнего таймера делает все это возможным. Применим таймер в консольной программе, где часы реализуются простым циклом for.
class predicates traceJob : (integer Time). clauses traceJob(Time) :- stdio::write("Now: ", Time, "\n"). clauses test() :- Scheduler = scheduler::new(), Scheduler:registerJob(500, traceJob), foreach Time = std::fromTo(1,1000) do Scheduler:timerTick(Time) end foreach.
Предикат traceJob печатает время. Мы создаем планировщик и регистрируем время (traceJob) для запуска Time = 500, затем запускаем часы. Можно выполнять работу, которая сама себя планирует, т.е., запускается повторно. Чтобы сделать это, работа должна иметь доступ к планировщику, поэтому сохраним ее в факте:
class predicates cyclicJob : (integer Time). clauses cyclicJob(Time) :- stdio::write("Hello World!\n"), scheduler:registerJob(Time+50, cyclicJob). class facts scheduler : scheduler := erroneous. clauses test() :- scheduler := scheduler::new(), scheduler:registerJob(5, cyclicJob), foreach Time = std::fromTo(1,200) do scheduler:timerTick(Time) end foreach.
В этом планировщике мы использовали (параметрическую) полиморфную очередь с приоритетами. Мы также использовали (категорированный) полиморфизм, связанный с предикатными значениями, с помощью которого мы смогли зарегистрировать работы в планировщиках с разными разделами implement. Параметрический полиморфизм использовался для равномерной обработки элементов с приоритетами, в не зависимости от их природы. Категорированный полиморфизм использовался для работ с неравномерной природой.
Другие новые конструкции
В это статье описываются некоторые новые особенности языка. Одновременно используются новые конструкции foreach и if-then-else, без объяснения их работы. Эти конструкции легко понять, и они не требуют особых объяснений. Однако, иногда, имеют место странные конструкции вроде "catch all" clauses и т.д.. Хотя они выглядят менее похожими на «старый» Пролог, фактически они имеют совершенно ясную прологовскую семантику, и одновременно, они делают код более ясным.
Заключение
Мы полагаем, что параметрический полиморфизм может помочь вам распределить обобщенные решения между разными проблемами, и, таким образом, уменьшить общее время разработки и, в особенности, время на поддержку. Мы знаем, что это дает возможность создавать библиотеки готовых для распределенного использования программ. Мы полагаем, что категорированный полиморфизм может помочь вам в работе в неоднородным мире, окружающим ваши программы. И на уровне примера с нашим планировщиком, и на уровне нашего примера с зарплатами.
Будущие расширения
Visual Prolog будет по-прежнему расширяться. Многие новые свойства уже намечены или находятся в стадии разработки. Особенно хочется отметить два новые свойства.
Параметрический полиморфизм будет расширяться и распространяться как на классы, так и на интерфейсы. Это добавит больше мощьсти такого же характера, как обсуждалось выше. Это так же естественно желать такую же мощность для всех типов данных, а не на ограниченном множестве. Позже параметрический полиморфизм станет F-связанным, но это не материал для обсуждения в этой статье. Те, кто интересуются, могут покопаться в литературе или поискать в Интернете.
Другим интересным свойством является использование значений анонимных предикатов. Это значительно упрощает код, который предназначен для достижения того же эффекта, к примеру, при создании работ в планировщике обсуждавшемся здесь. Дело в том, что несколько неуклюже передавать параметры таким работам: работа сама по себе не требует параметров, определяемых пользователем, поэтому эти параметры должны где-то храниться. Обычно, в фактах объектов, которым принадлежит предикат работы. С использованием анонимных предикатов такая необходимость отпадает.
Ссылки
Объекты и полиморфизм |
---|