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

metoda_2013

.pdf
Скачиваний:
54
Добавлен:
03.05.2015
Размер:
6.36 Mб
Скачать

ФУНКЦИОНАЛЬНОЕ И ЛОГИЧЕСКОЕ ПРОГРАММИРОВАНИЕ

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

Имея декларативно правильную программу, можно улучшить ее эффективность путем изменения порядка предложений и целей при сохранении ее декларативной правильности. Переупорядочивание - один из методов предотвращения зацикливания.

4. Отсечение. Графическая иллюстрация действия cut. Формальное описание действия отсечения

Все операторы CUT используются для уничтожения точек возврата, находящихся на стеке возвратов. Слово CUT: отмечает на стеке возвратов начало отсекаемой области. -CUT уничтожает все точки возврата, установленные после этой отметки. Конструкция выглядит так:

CUT: <действия> -CUT

В процессе вычисления цели пролог проводит перебор вариантов, в соответствии с установленным порядком. Цели выполняются последовательно, одна за другой, а в случае неудачи происходит откат к предыдущей цели (backtracing)

Однако для повышения эффективности выполнения программы, часто требуется вмешаться в перебор, ограничить его, исключить некоторые цели. Для этой цели в пролог введена специальная конструкция cut - "отсечение", обозначаемая как "!"( Читается

"cut").

Cut подсказывает прологу "закрепить" все решения предшествующие появлению его в предложении. Это означает, что если требуется бэктрекинг, то он приведет к неудаче (fail) без попытки поиска альтернатив.

Графическая иллюстрация действия cut.

Графически действие cut можно показать с помощью boxпредставления логического вывода. В обычном случае бэктрекинг для правила G:-A,B,C. выглядит следующим образом:

Т.е. поиск альтернатив производится для всех целей: G,A,B,C. Заменим цель B на cut.

320

ФУНКЦИОНАЛЬНОЕ И ЛОГИЧЕСКОЕ ПРОГРАММИРОВАНИЕ

Действие cut заключается в отмене поиска альтернатив для целей A,G, стоящих после "!".

Формальное описание действия отсечения. Рассмотрим предложение Н:-B1, B2,..., Bm, !,..., Bn. Это предложение активизируется, когда некоторая цель G, будет сопоставляться с

H. Тогда G называют целью-родителем. Если B1, B2,..., Bm,

выполнены, а после !, например в Bi, i>m, произошла неудача и требуется выбрать альтернативные варианты, то для B1, B2,..., Bm такие альтернативы больше не рассматриваются и все выполнение окончится неудачей. Кроме этого G будет связана с головой H, и другие предложения процедуры во внимание не принимаются. Т.е. отсечение в теле предложения отбрасывает все предложения, расположенные после этого предложения. Формально действие отсечения описывается так: Пусть цель - родитель сопоставляется с головой предложения, в теле которого содержится отсечение. Когда при просмотре целей тела предложения встречается в качестве цели отсечение, то такая цель считается успешной и все альтернативы принятым решениям до отсечения отбрасываются и любая попытка найти новые альтернативы на промежутке между целью-родителем и сut оканчиваются неудачей. Процесс поиска возвращается к последнему выбору, сделанному перед сопоставлением цели родителя.

Т.е. отсечение в теле предложения отбрасывает все предложения, расположенные после этого предложения.

Формально действие отсечения можно описать еще так: Пусть цель-родитель сопоставляется с головой

предложения, в теле которого содержится отсечение. Когда при просмотре целей тела предложения встречается в качестве цели отсечение, то такая цель считается успешной, и все альтернативы принятым решениям до отсечения отбрасываются, и любая попытка найти новые альтернативы на промежутке между целью-родителем и cut оканчиваются неудачей. Процесс поиска возвращается к последнему выбору, сделанному перед сопоставлением цели родителя.

ПРИМЕР: max(X,Y,X):-

X>Y. /* если первое число больше второго, то первое число - максимум */

max(X,Y,Y):-

X<=Y./* если первое число меньше или равно второму, возьмем в качестве максимума второе число */

321

ФУНКЦИОНАЛЬНОЕ И ЛОГИЧЕСКОЕ ПРОГРАММИРОВАНИЕ

Вданной ситуации нам пригодится встроенный предикат, который по-английски называется cut, по-русски - отсечение, а в программе на Прологе он обозначается восклицательным знаком "!". Этот предикат предназначен для ограничения пространства поиска, с целью повышения эффективности работы программ. Он всегда завершается успешно. После того, как до него дошла очередь, он устанавливает "забор", который не дает "откатиться назад", чтобы выбрать альтернативные решения для уже "сработавших" подцелей. То есть для тех, которые расположены левее отсечения. На цели, расположенные правее, отсечение не влияет. Кроме того, отсечение отбрасывает все предложения процедуры, расположенные после предложения, в котором находится отсечение.

С использованием отсечения наше решение будет еще

короче: max2(X,Y,X):-

X>Y,!./* если первое число больше второго, то первое число - максимум */

max2(_,Y,Y). /* в противном случае максимумом будет второе число */

Вслучае, если сработает отсечение, а это возможно, только если окажется истинным условие X>Y, Пролог-система не будет рассматривать альтернативное второе предложение. Второе предложение "сработает" только в случае, если условие оказалось ложным. В этой ситуации в третий аргумент попадет то же значение, которое находилось во втором аргументе. Обратите внимание, что в этом случае нам уже не важно, чему равнялся первый аргумент, и его можно заменить анонимной переменной.

Все случаи применения отсечения принято разделять на "зеленые" и "красные". Зелеными называются те из них, при

отбрасывании которых программа продолжает выдавать те же решения, что и при наличии отсечения. Если же при устранении отсечений программа начинает выдавать неправильные решения, то такие отсечения называются красными.

Пример "красного" отсечения имеется в реализации предиката max2 (если убрать отсечение, предикат будет выдавать в качестве максимума второе число, даже если оно меньше первого). Пример "зеленого" отсечения можно получить, если в запись предиката max добавить отсечения (при их наличии предикат будет выдавать те же решения, что и без них).

В принципе, с помощью отсечения в Прологе можно смоделировать такую конструкцию императивных языков, как ветвление.

Процедура

322

ФУНКЦИОНАЛЬНОЕ И ЛОГИЧЕСКОЕ ПРОГРАММИРОВАНИЕ

S:-

<условие>,!,P.

S :- P2.

будет соответствовать оператору if <условие> then P else P2, то есть если условие имеет место, то выполнить P, иначе выполнить P2. Например, в случае с максимумом, можно расшифровать нашу процедуру как "если X>Y, то M=X, иначе M=Y".

5. Сравнительная характеристика функционального, логического и процедурного подхода к программированию.

Процедурное программирование

Процедурное (императивное) программирование является отражением архитектуры традиционных ЭВМ, которая была предложена фон Нейманом в 40-х годах. Теоретической моделью процедурного программирования служит алгоритмическая система под названием Машина Тьюринга (абстрактная вычислительная машина).

Программа на процедурном языке программирования состоит из последовательностиоператоров (инструкций), задающих процедуру решения задачи.Выполнение программы сводится к последовательному выполнению операторов с целью преобразования исходного состояния памяти, то есть значений исходных данных, в заключительное, то есть в результаты. Основным являетсяоператор присваивания, служащий для изменения содержимого областей памяти.

Таким образом, с точки зрения программиста имеются программа и память, причем первая последовательно обновляет содержимое последней.

Процедурные языки характеризуются следующими особенностями:

необходимостью явного управления памятью, в частности, описаниемпеременных;

малой пригодностью для символьных вычислений;

отсутствием строгой математической основы;

высокой эффективностью реализации на традиционных ЭВМ.

Процедурный язык программирования предоставляет возможность программисту определять каждый шаг в процессе

решения

задачи.

Особенность

таких языков

программирования состоит в

том, что задачи

разбиваются на

323

ФУНКЦИОНАЛЬНОЕ И ЛОГИЧЕСКОЕ ПРОГРАММИРОВАНИЕ

шаги и решаются шаг за шагом. Используя процедурный язык, программист определяет языковые конструкции для выполнения последовательности алгоритмических шагов.

К процедурным языкам относятся: язык Ассемблера, С,

Basic(версии начиная с QuickBasic до появления VisualBasic), Pascal.

Функциональное программирование

Функциональное программирование — раздел дискретной

математики и парадигма

программирования,

в

которой

процесс вычисления трактуется

как

 

вычисление

значений функций в математическом

понимании

последних (в

отличие от функций как подпрограмм

в процедурном

программировании).

 

 

 

 

Противопоставляется

парадигме императивного

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

Функциональное программирование предполагает обходиться вычислением результатов функций от исходных данных и результатов других функций, и не предполагает явного хранения состояния программы. Соответственно, не предполагает оно и изменяемость этого состояния (в отличие от императивного, где одной из базовых концепций является переменная, хранящая своё значение и позволяющая менять его по мере выполнения алгоритма).

Первым функциональным языком программирования был LISP. Роль основной конструкции в функциональных (аппликативных) языках играетвыражение. К выражениям относятся скалярные константы, структурированные объекты, функции, тела функций и вызовы функций.

Аппликативный язык программирования включает следующие элементы:

классы констант, которыми могут манипулировать функции;

набор базовых функций, которые программист может использовать без предварительного объявления и описания;

правила построения новых функций из базовых;

324

ФУНКЦИОНАЛЬНОЕ И ЛОГИЧЕСКОЕ ПРОГРАММИРОВАНИЕ

правила формирования выражений на основе вызовов функций.

Программа представляет собой совокупность описаний функций и выражения, которые необходимо вычислить. Данное выражение вычисляется посредством редукции, то есть серии упрощений, до тех пор, пока это возможно по следующим правилам:

вызовы базовых функций заменяются соответствующими значениями;

вызовы небазовых функций заменяются их телами, в которых параметры замещены аргументами.

Основной особенностью функционального программирования, определяющей как преимущества, так и недостатки данной парадигмы, является то, что в ней реализуется модель вычислений без состояний. Если императивная программа на любом этапе исполнения имеет состояние, то есть совокупность значений всех переменных, и производит побочные эффекты, то чисто функциональная программа ни целиком, ни частями состояния не имеет и побочных эффектов не производит. То, что в императивных языках делается путём присваивания значений переменным, в функциональных достигается путём передачи выражений в параметры функций. Непосредственным следствием становится то, что чисто функциональная программа не может изменять уже имеющиеся у неё данные, а может лишь порождать новые путём копирования и/или расширения старых. Следствием того же является отказ от циклов в пользу рекурсии.

Преимущества:

повышение надёжности;

удобство организации модульного тестирования;

возможности оптимизации при компиляции;

возможности параллелизма.

Недостатки:

Недостатки функционального программирования вытекают из тех же самых его особенностей. Отсутствие присваиваний и замена их на порождение новых данных приводят к необходимости постоянного выделения и автоматического освобождения памяти, поэтому в системе исполнения функциональной программы обязательным

325

ФУНКЦИОНАЛЬНОЕ И ЛОГИЧЕСКОЕ ПРОГРАММИРОВАНИЕ

компонентом становится высокоэффективный сборщик мусора. Нестрогая модель вычислений приводит к непредсказуемому порядку вызова функций, что создает проблемы при вводевыводе, где порядок выполнения операций важен. Кроме того, очевидно, функции ввода в своем естественном виде (например, getchar из стандартной библиотеки языка C) не являются чистыми, поскольку способны возвращать различные значения для одних и тех же аргументов, и для устранения этого требуются определенные ухищрения.

Для преодоления недостатков функциональных программ уже первые языки функционального программирования включали не только чисто функциональные средства, но и механизмы императивного программирования (присваивание, цикл, «неявный PROGN» были уже в LISPе). Использование таких средств позволяет решить некоторые практические проблемы, но означает отход от идей (и преимуществ) функционального программирования и написание императивных программ на функциональных языках.

Логическое программирование

 

 

Логическое

программирование — парадигма

программирования,

основанная

на

автоматическом

доказательстве теорем, а также раздел дискретной математики, изучающий принципы логического вывода информации на основе

заданных

фактов

и

правил

вывода.

Логическое

программирование

основано

на

теории

и

аппарате математической

логики с

использованием

математических принципов резолюций.

Первым таким языком был язык Planner, в котором была заложена возможность автоматического вывода результата из данных и заданных правил перебора вариантов (совокупность которых называлась планом). Planner использовался для того, чтобы понизить требования к вычислительным ресурсам (с помощью метода backtracking) и обеспечить возможность вывода фактов, без активного использования стека. Затем был разработан язык Prolog, который не требовал плана перебора вариантов и был, в этом смысле, упрощением языка Planner.

Языки логического программирования, в особенности Пролог,широко используются в системах искусственного интеллекта.Центральным понятием в логическом программировании является отношение.Программа представляет собой совокупность определений отношений междуобъектами (в терминах условий или ограничений) и цели (запроса). Процессвыполнения программы трактуется как процесс

326

ФУНКЦИОНАЛЬНОЕ И ЛОГИЧЕСКОЕ ПРОГРАММИРОВАНИЕ

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

Языки логического программированияхарактеризуются:

высоким уровнем;

строгой ориентацией на символьные вычисления;

возможностью инверсных вычислений, то есть переменные в процедурах не делятся на входные и выходные;

возможной логической неполнотой, поскольку зачастую невозможно выразить в программе определенные логические соотношения, а также невозможно получить из программы все выводы правильные.

Сравнительная

характеристика

языков

программированияPASCAL, LISP, PROLOG:

 

 

 

 

 

 

 

Характеристи

PASCAL

LISP

PROLOG

 

ка

 

 

 

 

 

тип языка

процедурный

функциональн

логический

 

ый

 

 

 

 

 

типы данных

скаляры,

атомы, списки

атомы,

 

структуры

структуры

 

 

 

 

 

присвоение,

значение

связь

 

 

передача по

переменных

 

обработка

функции,

 

значению,

через

 

данных

передача по

 

передача по

унификацию

 

 

значению

 

 

ссылке

 

 

 

 

 

 

 

 

 

 

 

 

последовате

вычисление

 

 

 

функций,

 

 

 

льное,

 

 

управление

условные

рекурсия,

 

ветвление,

 

программой

вычисления,

бэктрекинг

 

циклы,

 

 

рекурсии,

 

 

 

рекурсия

 

 

 

циклы

 

 

 

 

 

 

 

 

 

 

327

ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ

структуры

блоки,

функции,

правила,

программы

процедуры

LET-блоки

факты

 

 

 

 

 

 

 

область

действия

глобальные,

локальные,

действия

переменной -

переменных

локальные

свободные

одно

 

 

 

 

 

 

предложение

 

 

интерпретатор

интерпретато

транслятор

компилятор

,

р

 

 

компилятор

 

длина

5

3

1

программы

 

 

 

 

 

 

 

скорость

1

2

5

 

 

 

 

 

программы

символьная

ЭС, ИИ,

область

общего

обработка,

прототипы

 

назначения

ИИ

 

 

 

 

 

 

VII. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ

ПРОГРАММИРОВАНИЕ

1.Определение класса в языке С++. Функции-члены класса в языке С++. Друзья класса в языке С++. Область видимости класса в языке С++. Инициализация класса в языке С++.

Механизм классов в C++ позволяет пользователям определять собственные типы данных. По этой причине их часто называют пользовательскими типами. Класс может наделять дополнительной функциональностью уже существующий тип. С помощью классов можно создавать абсолютно новые типы, например Screen (экран) или Account (расчетный счет). Как правило, классы используются для абстракций, не отражаемых встроенными типами адекватно.

Определение класса

Определение класса состоит из двух частей: заголовка, включающего ключевое слово class, за которым следует имя класса, и тела, заключенного в фигурные скобки. После такого определения должны стоять точка с запятой или список объявлений:

328

ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ

class Screen {

public:

 

string

_screen; // string( _height * _width )

string::size_type _cursor; // текущее положение на экране

short

_height;

// число строк

short

_width;

// число колонок

void home();

void move( int, int ); char get();

char get( int, int );

void checkRange( int, int );

//...

}myScreen, yourScreen;

Функции-члены

Функции-члены класса объявляются в его теле. Это объявление выглядит точно так же, как объявление функции в области видимости пространства имен.

Функцию-член можно объявить в любой из секций public, private или protected тела класса. Где именно это следует делать? Открытая функция-член задает операцию, которая может понадобиться пользователю. Множество открытых функцийчленов составляет интерфейс класса. Например, функции-члены home(), move() и get() класса Screen определяют операции, с помощью которых программа манипулирует объектами этого типа.

Поскольку мы прячем от пользователей внутреннее представление класса, объявляя его члены закрытыми, то для манипуляции объектами типа Screen необходимо предоставить открытые функции-члены. Такой прием – сокрытие информации – защищает написанный пользователем код от изменений во внутреннем представлении.

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

Определение функции-члена также можно поместить внутрь тела класса:

class Screen { public:

// определения функций home() и get()

329

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]