Добавлен: 21.05.2023
Просмотров: 326
Скачиваний: 4
Массивы в Паскаль и С++ похожи тем, что требуют указывать точный диапазон использования элементов массива, не позволяя расширять его динамически.
Для хранения большого количества данных в Python вместо массивов используют такие структуры данных как кортежи и списки. Таким образом, понятие массивов претерпевает изменения. Классифицируются типы данных, заданных пользователем, на изменяемые и неизменяемые. К неизменяемым относятся кортежи, к изменяемым – списки и словари.
Кортеж используется для представления неизменяемой последовательности разнородных объектов. Его элементы записываются в круглых скобках. Так как кортеж – один из типов последовательностей, то всё, что можно применять к строкам в Python, верно и по отношению к кортежам. Можно найти длину кортежа, вывести все его элементы на экран с помощью цикла for, а также, пользуясь оператором in, проверить кортеж на вхождение какого-либо элемента. Кортежи можно сцеплять, создавать на них срезы.
Кортежи – хороший способ манипулировать элементами разных типов в составе одной последовательности. Но в силу того, что кортеж неизменяем, иногда возникают небольшие проблемы. Однако существуют последовательности другого типа, называемые списками. Главным преимуществом списка является то, что он изменяем. С ним можно проводить те же самые операции, что и с кортежем и даже больше: его элементы можно удалять, а также добавлять новые. Список, как и кортеж, представляет собой последовательность элементов, пронумерованных от 0, как символы в строке. Список можно задать перечислением элементов в квадратных скобках.
Кортежи и списки позволяют упорядочить данные в виде последовательностей. Со словарями дело обстоит немного иначе. В них информация представлена не как последовательность элементов, а как набор пар. Заметно сходство словарей с такой структурой данных как вложенный массив. Элементы словаря заключаются в фигурные скобки.
При работе с кортежами, словарями и списками удобно пользоваться функцией len(s), где s последовательность. Данная функция определяет длину последовательности. Необходимо заметить, что в Паскаль и С++ такой функции нет! Чтобы в Паскале и С++ определить количество элементов в массиве, необходимо инициализировать новую переменную, присвоить ей значение 0, а затем с помощью цикла for перебрать все элементы массива, увеличивая созданную переменную с каждым элементом на 1 – стандартный цикл обхода массива.
Большим достоинством языка программирования Python является то, что к его изменяемым структурам данных, то есть к спискам и словарям, применяется определенный ряд встроенных методов, значительно упрощающих работу с этими типами данных.
Еще одним важным элементом сравнения для анализируемых языков программирования является запись блоков или составных операторов.
Для логически верного использования конструкций языка важно помнить, что в Паскаль блоки выделяются с помощью служебных слов begin – end, в С++ – с помощью фигурных скобок {}. В языке программирования Python дело обстоит гораздо иначе. Здесь большую роль играют отступы. Вложенные инструкции объединяются в блоки по величине отступов. Отступ может быть любым, главное, чтобы в пределах одного вложенного блока отступ был одинаков. Но не стоит забывать про читаемость кода! Именно эта особенность Python и повышает читаемость кода только лишь спецификацией языка.
Например, при записи условного оператора в первой строке размещается условие (логическое выражение), а в блоке с отступом – практически любое действие. Если условие истинно, то выполняется код в блоке после команды if, а если ложно, этот код игнорируется.
Нередко в программе требуется проверять более двух возможных ситуаций. Для этого в Python предусмотрен синтаксис if – elif – else. Python выполняет только один блок в этой цепочке. Все условия проверяются по порядку до тех пор, пока одно из них не окажется истинным. Далее выполняется код, следующий за этим условием, а все остальные проверки Python пропускает. Для языков С++ и Паскаль реализация подобных ситуаций в коде требует использование вложенных конструкций, что усложняет читабельность кода и требует усиления внимания программиста к выполнению правил их использования («else относится к ближайшему if»).
Использование в Python конструкции if – elif – else похоже на использование операторов выбора сase оf и switch() в Паскале и С++ соответственно.
Циклы while с предусловием и постусловием в данных языках программирования особого отличия не имеют.
Цикл со счетчиком или, как он еще называется — цикл с параметром в Паскале служит для организации повторений с заранее известным их количеством. Оператор в теле конструкции может быть одиночным или составным оператором. Счетчик цикла или параметр цикла – это обычная переменная, которая может быть только порядкового (целочисленного, символьного, логического) или перечислимого типа. При прямом направлении цикла счетчик в каждой итерации всегда увеличивается на единицу, при обратном направлении – уменьшается на единицу. Важно знать: параметр цикла нельзя изменять внутри тела цикла! Также нельзя менять его начальное и конечное значения.
В С++ дело обстоит немного иначе. Сначала инициализируется параметр цикла, после чего ставится точка с запятой. Затем задается конечное значение счетчика цикла. Снова ставим точку с запятой. Задаем шаг цикла. Шаг цикла – это значение, на которое будет увеличиваться или уменьшаться счетчик цикла при каждом проходе.
Чтобы в Python использовать цикл for необходимо сначала написать служебное слово for, потом имя переменной, в которой предполагается сохранять значения элементов, потом in, затем имя перебираемой последовательности, двоеточие и, наконец, тело цикла. Вот и всё.
В проведенном выше анализе языков программирования высокого уровня: Паскаль, С++ и Python были выделены основные преимущества последнего и одновременно представлена линейка синтаксических усовершенствований, которые дают представления о направлениях эволюции языков программирования.
2.2 Язык логического программирования PROLOG
Язык логического программирования PROLOG (далее – ПРОЛОГ) большинству программистов представляется чем-то запутанным и малопригодным для практического применения. В то же время, Интернет основан на символьной информации, поэтому практически все современные программисты сталкиваются с необходимостью обрабатывать символьные структуры данных, а ведь для этого и предназначен язык логического программирования ПРОЛОГ. Этот язык – идеальный для работы с символьными структурами, текстовыми файлами и для построения интеллектуальных программ.
После многолетней работы с ПРОЛОГом, столкнулся с новомодным Haskell, о котором все авторы гордо говорят – «высокий порог вхождения». По-моему, «высота» этого порога образовалась в результате запутанного синтаксиса и нагромождения встроенных процедур. Еще один источник этой «высоты» — высокомерие авторов многочисленных руководств, которые написаны так, будто читатель никогда программ не писал.
Сразу захотелось назад, к «родному» ПРОЛОГу – простому и понятному. Думаю, если объяснить без нагромождений, типа декларативной семантики, время вхождения в логическое программирование для «обычного» программиста не превысит 30 минут.
Попробую описать его с точки зрения процедурного программирования.
ПРОЛОГ имеет два основных отличия от процедурных языков — способ организации вычислений и способ представления данных. Оба этих аспекта языка коренным образом отличаются от традиционных языков программирования. Но не будем забывать, что логическое программирование реализуется на тех же машинах с фон — Неймановской архитектурой.
Представим себе язык программирования, в котором все операции сводятся к вызову процедур. При этом каждая процедура существует в нескольких вариантах и программа сама выбирает себе правильный, точнее говоря, пригодный для каждого конкретного варианта исходных данных вариант реализации (тело) процедуры. Как определяется пригодность – каждый вызов процедуры, во время исполнения программы, по результатам исполнения процедуры, получает логическое значение – TRUE или FALSE. Совокупность процедур с одинаковым именем и арностью называется предикатом.
Каждый предикат в языке ПРОЛОГ идентифицируется двумя параметрами –именем и арностью – количеством параметров. Например, предикат инвертирования списков идентифицируется, как nrev/2, а предикат соединения списков как append/3. Никакого объявления параметров или переменных не требуется, но можно ограничить параметры паттерном в заголовке процедуры. Например, в одном варианте предиката первый параметр может быть указан как [] – пустой список, в другом – как [A,B,C] – список из трех элементов, а в третьем как [H|T] – произвольный непустой список.
Если какое-то условие в теле процедуры не выполняется, выполнение прекращается и вызов процедуры, точнее говоря, выполнение очередного варианта тела процедуры, прекращается и получает значение FALSE. В этом случае, интерпретатор языка запускает следующий по порядку вариант тела этой процедуры. Если же все варианты оказались непригодными, происходит возврат к предыдущей процедуре и для нее также происходит перебор вариантов, таким образом, вычисление в ПРОЛОГе сводится к обходу дерева вложенности процедур.
Возврат к предыдущему вызову называется бэктрекингом (backtracking). По существу это откат, поскольку все значения, полученные переменными в отмененной процедуре, отменяются. Бэктрекинг – уникальное средство данного языка, не имеющее аналогов ни в одном из других массовых языков программирования. Бэктрекинг обеспечивает полный обход дерева логического вывода, что обеспечивает основу для решения интеллектуальных задач.
Алгоритм интерпретации программы (выполнения цели) в системе ПРОЛОГ.
Вход: цель p(A1,A2,…,An)
1. Найти в памяти процедуру по имени p с арностью n.
2. Унифицировать входные параметры цели с заголовком найденной процедуры.
3. Если унификация параметров прошла, выполнить первую цель из тела найденной процедуры.
4. Если унификация не прошла, найти следующий вариант процедуры P/n.
5. Если следующий вариант процедуры P/n не найден, закончить работу с результатом FAIL.
6. Если первая цель выполнилась, перейти к выполнению следующей цели.
7. Если следующая цель не выполнилась, вернуться к предыдущей цели и выполнить следующий вариант ее реализации.
8. Если текущий вариант реализации процедуры P/n не выполнился, перейти к следующему варианту.
9. Если все цели из тела процедуры выполнены, закончить работу с результатом TRUE.
В связи с такой структурой вычислений каждый вызов процедуры называется целью, которая может быть достигнута или нет. Всякое вычисление в ПРОЛОГе имеет логическое значение. Вычисление еще называют поиском вывода (доказательства), а язык ПРОЛОГ – языком логического программирования.
Наверное, синтаксически ПРОЛОГ – самый простой среди языков программирования. Основной синтаксической процедурой является предикат – выражение, аналогичное вызову процедуры — имя и список аргументов –
P(x1, …xn). Имена пишем с малой буквы, а переменные – с большой.
Если опустить детали, синтаксис ПРОЛОГа можно описать следующим образом:
<ПРОЛОГ-предложение> ::= <правило> | <факт> | <запрос>
<правило> ::= <заголовок> ‘:-’<тело>
<факт> ::= <заголовок> ‘.’
<запрос> ::= <тело>‘.’
<тело> ::= <цель> /’,’<цель>/’.’
<заголовок>::= <предикат>
<цель>::= <предикат> |<выражение>
<предикат>::= <имя>/ ‘(‘<терм> /’,’<терм>/ ‘)’/
<терм>::= <атом> |<предикат>|<список>
<атом>::= <переменная> |<число> |<строка>|<имя>
<список>::= <список с заголовком>| <простой список>
<список с заголовком >::= ‘[‘ <терм >/’,’<терм>/’|’ < терм>’]’
< простой список>::= ‘[‘ <терм >/’,’<терм>/’]’|‘['’]’
<выражение>::= <терм> /<оператор><терм>/
<оператор>::= ‘is’ | '=' | ‘==' | ’\=' | ’>=' | ’=<’ | ‘=\=' |
Как видно из синтаксиса, в ПРОЛОГе нет зарезервированных имен за исключением предиката “is”, хотя и применяются различные встроенные предикаты для удобства и связи с внешней средой.
Пример – семейные отношения.
Определим правила для отношений – супруг, ребенок, мать, дочь, брат, потомок. Семейные отношения задаются бинарными и унарными предикатами – правилами (предикаты с переменными) и фактами (предикаты с константами). Правила легко понять, поскольку семейные отношения всем понятны. Например, первое правило гласит: X является матерью Y, если Y есть ребенок X и X женского пола.
mother(X,Y):-child(X,Y),female(X).
daughter(X,Y):-child(Y,X),female(X).
grandchild(X,Y):-child(X,Z),child(Z,Y).
descendant(X,Y):-child(X,Y).
descendant(X,Y):-grandchild(X,Z).
Факты описывают родственников конкретной семьи.
marrow(john, meri). female(meri).
child(john, jack). child(meri, jack). child(john, kate).
marrow(jack,joan). marrow(kate, dick).
child(jack,henry). child(kat,josh).
male(john). female(mery).
male(henry). female(joan).
male(josh). male(dick).
Один набор правил (программа) может применяться к различным наборам фактов (данные).
Набор возможных запросов определяется существующим набором правил. Заголовок каждого правила является основой построения запросов.
Запросы могут быть двух видов – проверочный и поисковый.
Примеры поисковых запросов:
Чья дочь Kat? – daughter(X,kat).
Чей внук Henry? — grandchild(X,henry)
Примеры проверочных запросов: