Файл: Классификация языков программирования (Основные парадигмы программирования).pdf
Добавлен: 29.03.2023
Просмотров: 407
Скачиваний: 1
СОДЕРЖАНИЕ
1. Классификация языков программирования
1.1 Основные парадигмы программирования
1.2 Объектно-ориентированная парадигма
2. Критерии выбора среды и языка разработки программ
2.1 Выбор первого языка для разработки программ
2.2 Критерии выбора языка и среды разработки для обучения программированию
Объектно-ориентированный подход объединяет данные и функции вместе, это свойство называется инкапсуляцией. Инкапсуляция поддерживает безопасность данных в объектно-ориентированной парадигме. Данные могут быть изменены или доступны только для связанных функций-членов, но объекты обмениваются данными друг с другом, передавая сообщения друг другу.
Наследование – это процесс, посредством которого объекты одного класса приобретают свойства объектов другого класса в иерархии. Новые классы могут быть построены из существующих классов. Это означает, что можно добавить дополнительные функции в существующий класс, не изменяя его. Новый класс называется производным классом или подклассом, а исходный класс называется базовым классом или суперклассом. Следовательно, концепция наследования обеспечивает идею повторного использования. Механизм наследования позволяет программисту повторно использовать класс, который сделан почти аналогичным требуемому, добавив в него еще несколько функций.
Полиморфизм является одной из фундаментальных особенностей объектно-ориентированной парадигмы. Слово полиморфизм состоит из двух греческих слов «поли» и «морфизм». Слово «поли» означает «много», а «морфизм» – «формы», поэтому полиморфизм подразумевает множество форм. В объектно-ориентированной парадигме это означает способность функции-члена обрабатывать данные разных типов. Это свойство, которое позволяет использовать один интерфейс для общего класса действий. В данном случае точное действие определяется деталями конкретной ситуации.
Объектно-ориентированная парадигма предоставляет несколько значимых преимуществ как разработчику программы, так и пользователю. Новая технология обеспечивает большую производительность программиста, лучшее качество программного обеспечения и меньшие затраты на обслуживание. Основными преимуществами являются [5.]:
- Простота разделения задач – поскольку можно сопоставить объекты проблемной области с объектами в программе, работу можно легко упростить, основываясь на свойствах объектов;
- Уменьшение сложности – сложность программного обеспечения может легко управляться;
- Обеспечение расширяемости – объектно-ориентированные системы могут быть легко модернизированы, и, таким образом, легко перейти с небольшой на большую систему;
- Устранение избыточности – с помощью наследования можно устранить избыточный код и расширить использование существующих классов;
- Уменьшение времени разработки и повышение производительность – вместо того, чтобы писать код с нуля, решения можно построить с использованием стандартных рабочих модулей;
- Позволяет создавать безопасные программы – функция сокрытия данных помогает программисту создавать безопасные программы, к которым нет доступа из кода в других частях программы;
- Позволяет создавать более простые интерфейсы – методы передачи сообщений между объектами позволяют упростить описание интерфейса с внешними системами.
1.3 Функциональная парадигма
Отличие императивного понятия «функции» от функции в математическом смысле состоит в том, что императивные функции способны опираться на состояние внешних по отношению к функции переменных и менять их, выполняя побочные эффекты. Это означает, что при вызове одной и той же функции с одинаковыми параметрами в императивном программировании из-за влияния состояния переменных на функцию на разных этапах выполнения алгоритма можно получить разные данные на выходе, в то время как в функциональном языке результат всегда одинаков: выходные данные зависят только от входных [6.]. Благодаря этому свойству среды выполнения программ на функциональных языках могут сохранять результаты функций в памяти и вызывать их в порядке, не определяемом алгоритмом.
Теория, положенная в основу императивного программирования, родилась в 20-х – 30-х годах прошлого столетия благодаря работам Алана Тьюринга и Джона Фон Неймана. Функциональное программирование появилось примерно в то же время. Его математические основы в виде комбинаторной логики были разработаны Моисеем Шейнфинкелем (его ранние работы малоизвестны, поскольку далеки по времени от работ, непосредственно связанных с функциональным подходом). Категориальная комбинаторная логика, предложенная Хаскеллом Карри (в честь него назван язык программирования Haskell), позволила выполнить построение абстрактной машины, выполняющей моделирование среды вычислений. Попытки формализации и анализа понятия вычислимости Алонзо Черча легли в основу лямбда-исчисления, которое представляет собой формальную систему, то есть совокупность абстрактных объектов, в которой используются правила оперирования множеством символов без учета смыслового содержания (семантики), но в строго синтаксической трактовке. Лямбда-исчисление нашло применение во многих современных языках программирования в виде механизма так называемых «анонимных функций» – функций, которые могут быть определены в том месте, где они используются, и которые имеют доступ к локальным переменным текущей функции. Многие функциональные языки можно рассматривать как «надстройку» над лямбда-исчислением [7.].
1960-е ознаменовались появлением различных специфических языков, использующих элементы функционального подхода [8.]. Одним из них был оптимизированный для работы с массивами язык APL, предшественник современных научных вычислительных сред, таких как Matlab, а также языков J и K, применяемых в финансовом анализе. APL оказал значительное влияние на появившиеся впоследствии специфические языки, нашедших различное применение в индустрии: R (статистика), Wolfram (символьная математика), XSLT и XML (языки разметки). Распространенные декларативные языки SQL и Lex / Yacc тоже содержат элементы функционального программирования, поскольку не используют аналоги переменных в императивных языках. Языки, используемые при работе с электронными таблицами, также можно рассматривать как функциональные, потому что функции, задаваемые в ячейках таблиц, зависят только от других ячеек, а моделирование переменных осуществляется при помощи языка макросов, который является императивным.
Модели функциональных языков, разработанные в конце 70-х – начале 80-х годов прошлого века, включали в себя поддержку таких механизмов, как абстракция данных и полиморфизм. Фундаментальная идея абстракции состоит в разделении несущественных деталей реализации программы и характеристик, существенных для ее корректного использования, полиморфизмом называется способность функции обрабатывать данные разных типов. Эти нововведения дали мощный толчок к появлению множества типизированных функциональных языков и различных диалектов. Однако их дальнейшее распространение затруднялось тем, что каждая группа, занимающаяся функциональным программированием, использовала собственный язык. Попытки объединенной группы ведущих исследователей соединить множество идей, накопленных в процессе становления функционального программирования, привели к реализации нового универсального языка Haskell [9.]. Первая версия языка появилась в начале 90-х годов.
Дальнейшие совершенствования функциональных языков связаны с унификацией и обобщением функциональной парадигмы и объектно-ориентированной парадигмы, основанной на представлении программы в виде множества объектов, каждый из которых является экземпляром определенного класса, совокупность которых образует иерархию наследования. Основные технические новшества языка Scala, который появился в 2003-м году, представляли собой сплав этих парадигм программирования. Кроме того, использование механизмов абстракции, композиции и декомпозиции, пришедшее на смену введению большого количества примитивов, добавило возможность с помощью одних и тех же концепций описать как маленькие, так и большие программные части. Типобезопасность и масштабируемость Scala и возможность его интеграции с Java, откуда были заимствованы основные синтаксические соглашения и некоторые концепции, позволяют активно использовать язык в промышленной разработке.
Несмотря на то, что ключевые концепции, специфичные для функциональной парадигмы, могут быть чужды императивной, многие современные языки являются мультипарадигменными и используют некоторые из этих концепций [8.]:
- Краткость и простота;
Программы, написанные на функциональных языках, зачастую оказываются более короткими и простыми для понимания, нежели их аналоги на императивных языках. Эту разницу демонстрирует пример сортировки списка быстрым методом Хоара, ставший классическим при описании преимуществ функциональных языков. Вместо выбора конкретного функционального языка можно использовать некий абстрактный функциональный язык, приближенный к математической нотации. В листинге 1 приведена быстрая сортировка Хоара на языке Си.
Листинг 1. Функция быстрой сортировки Хоара на языке программирования Си
void quickSort (int a[], int l, int r)
{
int i = l;
int j = r;
int x = a[(l + r) / 2];
do
{
while (a[i] < x) i++;
while (x < a[j]) j--;
if (i <= j)
{
int temp = a[i];
a[i++] = a[j];
a[j--] = temp;
}
} while (i <= j);
if (l < j) quickSort (a, l, j);
if (i < r) quickSort (a, i, r);
}
Пример быстрой сортировки Хоара на абстрактном функциональном языке приведен в листинге 2.
Листинг 2. Пример быстрой сортировки Хоара на абстрактном функциональном языке программирования
quickSort ([]) = []
quickSort ([h : t]) = quickSort (n | n ∈ t, n <= h) + [h] + quickSort (n | n ∈ t, n > h)
Этот пример читается так:
Если список пуст, то результатом работы алгоритма также будет пустой список.
Иначе (список не пуст) список разбивается на голову (первый элемент списка) и хвост (список из оставшихся элементов). В этом случае результатом работы алгоритма будет являться конкатенация отсортированного списка, состоящего из всех элементов хвоста, которые меньше либо равны голове списка, самой головы списка и списка, состоящего из всех элементов хвоста, которые больше головы.
Как видно даже на таком простом примере, функциональный стиль написания программ имеет преимущество перед императивным с точки зрения лаконичности и элегантности написанного кода. В отличие от императивного стиля, описывающего шаги, ведущие к достижению цели, функциональный описывает математические отношения между данными и целью.
- Использование функций высших порядков;
Функции высших порядков (ФВП) могут принимать в качестве аргумента и возвращать другие функции. В математике такие функции называются операторами (например, оператор интегрирования, оператор взятия производной). ФВП позволяют использовать каррирование – преобразование функции от многих аргументов в набор функций, берущих свои элементы по одному. Каррирование получило свое название в честь Хаскелла Карри.
- Отсутствие побочных эффектов;
В то время как функции в императивных языках могут читать и изменять значения глобальных переменных, а также осуществлять операции ввода-вывода в процессе своей работы, функциональные языки используют чистые функции, не имеющие побочных эффектов ввода-вывода и памяти. Помимо упрощения анализа программ, использование чистых функций обладает весомым преимуществом при параллельных вычислениях. Поскольку все функции используют только свои параметры, они могут вычисляться в произвольном порядке или параллельно, и это не повлияет на результат вычислений.
- Модульность;
Механизм модульности позволяет разделять программы на несколько независимых частей, облегчая процесс проектирования и дальнейшей поддержки больших программных систем. Несмотря на то, что модульность не является исключительным свойством функциональных языков, она поддерживается большинством таковых.
- Подход к вычислению аргументов;
Во многих императивных языках вызов функции приводит к вычислению всех аргументов. Однако вычисление некоторых аргументов может быть излишним. Например, оператор конъюнкции может не вычислять значение второго аргумента в том случае, когда первый оказывается ложным. Функциональные языки могут быть классифицированы по способу обработки аргументов функции в процессе вычисления. При строгом порядке вычисления заранее подсчитываются значения всех аргументов перед вычислением самой функции, при нестрогом – значения аргументов вообще не вычисляются до тех пор, пока их значение не понадобится при вычислении функции. Нестрогое вычисление используется по умолчанию в несколько чисто функциональных языках, в том числе Miranda, Clean и Haskell.
- Рекурсия;
Функциональная парадигма программирования, в отличие от императивной, не имеет такого понятия, как цикл. Функциональные языки реализуют цикл в виде рекурсии: для повторения операции функции могут вызывать себя рекурсивно. Поскольку рекурсивные вызовы требуют больших затрат по памяти, для их накапливания необходим большой аппаратный стек. Функциональные языки позволяют избежать дополнительных расходов за счет использования хвостовой рекурсии, распознавание и оптимизация которой в код возлагается на компилятор. Код, получаемый в результате подобной операции, аналогичен итерации, используемой в императивном языке программирования. Кроме того, рекурсивные функции могут быть обобщены с помощью ФВП. Понятия «свертки» и «развертки» играют роль цикла в императивных языках программирования.
- Повышение надежности кода.
Модель вычисления без состояний, используемая в парадигме функционального программирования, позволяет повысить надежность кода за счет четкой структуризации и отсутствия необходимости отслеживания побочных эффектов. Все функции работают с локальными данными одинаково независимо от того, где и как они вызываются. Это исключает появление в программе ошибок, которые тяжело обнаружить (таких как, например, случайное присваивание неверного значения глобальной переменной в программах, написанных с использованием императивной парадигмы).