Сравнение парадигм программирования

редактировать

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

Содержание
  • 1 Основные подходы к парадигме
  • 2 Различия в терминологии
  • 3 Поддержка языков
  • 4 Сравнение производительности
    • 4.1 Управляемый код
    • 4.2 Примеры псевдокода, сравнивающие различные парадигмы
      • 4.2. 1 Подпрограмма, служебные данные вызова метода
      • 4.2.2 Выделение динамической памяти для хранилища сообщений и объектов
      • 4.2.3 Динамически отправляемые вызовы сообщений v. Служебные данные прямого вызова процедур
    • 4.3 Сериализация объектов
    • 4.4 Параллельные вычисления
  • 5 См. Также
  • 6 Ссылки
  • 7 Дополнительная литература
  • 8 Внешние ссылки
Основные парадигмальные подходы

Существуют два основных подхода к программированию:

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

Ниже перечислены общие типы программирования, которые могут быть реализованы с использованием различных парадигм:

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

Парадигма ОписаниеОсновные чертыСвязанные парадигмыКритика Примеры
Императивные Программы как операторы, которые напрямую изменяют вычисленное состояние (поля данных )Прямые присвоения, общие структуры данных, глобальные переменные Эдсгер В. Дейкстра, Майкл А. Джексон C, C ++, Java, Kotlin, PHP, Python, Ruby, Wolfram Language
Structured Стиль императивное программирование с более логичной структурой программыСтруктурограммы, отступ, отсутствие или ограниченное использование операторов goto ИмперативноеC, C ++, Java, Kotlin, Pascal, PHP, Python, Wolfram Language
Процедурное Получено из структурного программирования на основе n концепция модульного программирования или вызова процедурыЛокальные переменные, последовательность, выбор, итерация и модуляризация Структурированная, императивнаяC, C ++, Lisp, PHP, Python, Wolfram Language
Функциональный Обрабатывает вычисления как оценка математических функций, избегающих состояния и изменяемых данныхлямбда-исчисления, композиционности, формулы, рекурсия, ссылочная прозрачность, без побочных эффектов ДекларативныйC ++,C#,Clojure, Coffeescript, Elixir, Erlang, F#, Haskell, Java (начиная с версии 8), Kotlin, Lisp, Python, R,Ruby, Scala, SequenceL, Standard ML, JavaScript, Elm, Wolfram Language
Управляемый событиями, включая управляемый временем Поток управления определяется в основном событиями, такие как щелчки мыши или прерывания, включая таймерОсновной цикл, обработчики событий, асинхронные процессы Процедурные, поток данных JavaScript, ActionScript, Visual Basic, Elm
Объектно-ориентированный Обрабатывает поля данных как объекты, управляемые только с помощью предопределенных методов Объекты, методы, передача сообщений, скрытие информации, абстракция данных, инкапсуляция, полиморфизм, наследование, сериализация -маршаллингпроцедурныйВикипедия, другиеCommon Lisp, C ++, C#, Eiffel, Java, Kotlin, PHP, Python, Ruby, Scala, JavaScript
Декларативный Определяет логику программы, но не подробно поток управления Языки четвертого поколения, электронные таблицы, отчет генераторы программ SQL, регулярные выражения, Prolog, OWL, SPARQL, XSLT
Программирование на основе автоматов Рассматривает программы как модель конечного автомата или любые другие формальные автоматыСостояние перечисление, управляющая переменная, изменение состояния, изоморфизм, переход состояния таблица Императивный, управляемый событиямиАбстрактный язык конечного автомата
Различия в терминологии

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

Языковая поддержка

Синтаксический сахар - это улучшение функциональности программы путем введения языковых функций, которые облегчают конкретное использование, даже если конечный результат может быть достигнут без них. Одним из примеров синтаксического сахара, вероятно, могут быть классы, используемые в языках объектно-ориентированного программирования. Императивный язык C может поддерживать объектно-ориентированное программирование с помощью своих возможностей указателей функций, преобразования типов и структур. Однако такие языки, как C ++, стремятся сделать объектно-ориентированное программирование более удобным за счет введения синтаксиса, специфичного для этого стиля кодирования. Более того, специализированный синтаксис подчеркивает объектно-ориентированный подход. Точно так же функции и синтаксис циклов в C (и других процедурных и структурированных языках программирования) можно считать синтаксическим сахаром. Язык ассемблера может поддерживать процедурное или структурированное программирование через свои средства для изменения значений регистров и выполнения ветвления в зависимости от состояния программы. Однако в таких языках, как C, введен синтаксис, специфичный для этих стилей кодирования, чтобы сделать процедурное и структурированное программирование более удобным. Возможности языка C # (C Sharp), такие как свойства и интерфейсы, аналогичным образом не позволяют использовать новые функции, но предназначены для того, чтобы сделать хорошие методы программирования более заметными и естественными.

Некоторые программисты считают эти возможности несущественными или даже несерьезными. Например, Алан Перлис однажды пошутил, ссылаясь на языки, разделенные скобками, что «синтаксический сахар вызывает рак точки с запятой » (см. Эпиграммы по программированию ).

Расширением этого является синтаксический сахарин или бесплатный синтаксис, который не упрощает программирование.

Сравнение производительности

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

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

Управляемый код

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

Примеры псевдокода, сравнивающие различные парадигмы

A псевдокод сравнение императивного, процедурного и объектно-ориентированного подходов, используемых для вычисления площади круга (πr²), предполагая, что нет подпрограммы встраивания, нет макроса препроцессоров, регистровой арифметики и взвешивания каждого шага инструкции как только 1 инструкции - как грубая мера длина пути команды - представлена ​​ниже. Шаг инструкции, который концептуально выполняет изменение состояния, в каждом случае выделяется жирным шрифтом. Арифметические операции, используемые для вычисления площади круга, одинаковы во всех трех парадигмах, с той разницей, что процедурная и объектно-ориентированная парадигмы заключают эти операции в вызов подпрограммы, что делает вычисления универсальными и повторно используемыми. Тот же эффект может быть достигнут в чисто императивной программе с использованием препроцессора макросов только за счет увеличения размера программы (только на каждом сайте вызова макроса) без соответствующей пропорциональной стоимости времени выполнения (пропорциональной n вызовам - который может быть расположен, например, внутри внутреннего цикла ). И наоборот, встраивание подпрограмм компилятором может уменьшить процедурные программы до чего-то похожего по размеру на чисто императивный код. Однако для объектно-ориентированных программ, даже со встраиванием, сообщения все равно должны быть построены (из копий аргументов) для обработки объектно-ориентированными методами. Накладные расходы на вызовы, виртуальные или иные, не зависят от изменения потока управления, а из-за окружающего соглашения о вызовах затрат, таких как пролог и эпилог, настройка стека и передача аргумента (см. здесь более реалистичную длину пути инструкций, стек и другие затраты, связанные с вызовами на платформе x86 ). См. Также слайд-презентацию Эрика С. Робертса («Распределение памяти по переменным», глава 7), иллюстрирующую использование стека и кучи при суммировании трех рациональных чисел на объектно-ориентированном языке Java.

Императивная Процедурная Объектно-ориентированная
загрузка r; 1 r2 = r * r; 2 результат = r2 * "3.142"; 3...................... хранение............. переменная результата константа "3.142"
area proc (r2, res): push stack 5 load r2; 6 r3 = r2 * r2; 7 res = r3 * "3.142"; 8 pop stack 9 return; 10............................................... основной процесс : load r; 1 зона разговора (r, результат); + загрузка p = адрес списка параметров; 2 + загрузка v = адрес подпрограммы «область»; 3 + goto v с возвратом; 4........ хранилище............. константа переменной результата "3.142" список параметров переменная функция указатель (==>область) стек хранилище
метод circle.area (r2): push stack 7 нагрузка r2; 8 r3 = r2 * r2; 9 res = r3 * "3,142"; 10 pop stack 11 return (res); 12,13............................................... основной процесс: load r; 1 результат = circle.area (r); + выделить память в куче; 2 + скопировать r в сообщение; 3 + загрузка p = адрес сообщения; 4 + нагрузка v = адрес. метода 'circle.area' 5 + goto v с возвратом; 6...... хранилище............. переменная результата (предполагается предварительно выделенная) неизменяемая переменная "3.142" (окончательная) (куча) переменная сообщения для вызова метода круга vtable (==>область) стековое хранилище
  1. ^См. раздел: Выделение динамической памяти для хранения сообщений и объектов

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

Подпрограмма, накладные расходы на вызов метода

Наличие (вызываемой) подпрограммы в программе не вносит ничего лишнего в функциональность программы независимо от парадигмы, но может в значительной степени способствовать структурированию и общности программы, что значительно упрощает ее написание, изменение и расширение. Степень, в которой различные парадигмы используют подпрограммы (и связанные с ними требования к памяти), влияет на общую производительность всего алгоритма, хотя, как Гай Стил указал в статье 1977 года, хорошо спроектированная реализация языка программирования может иметь очень низкие накладные расходы на процедурную абстракцию (но сожалеет, что в большинстве реализаций они редко достигают этого на практике - будучи «довольно легкомысленными или небрежными в этом отношении»). В той же статье Стил также рассматривает случай программирования на основе автоматов (с использованием вызовов процедур с хвостовой рекурсией ) и приходит к выводу, что «мы должны проявлять здоровое уважение к вызовам процедур». (потому что они мощные), но предлагается "использовать их экономно"

Частота вызовов подпрограмм:

  • Для процедурного программирования степень детализации кода в значительной степени определяется числом дискретных процедур или модулей.
  • Для функционального программирования частые вызовы подпрограмм библиотеки являются обычным явлением, но часто могут быть встроены оптимизирующим компилятором
  • Для объектно-ориентированного программирования, количество вызываемых вызовов методов также частично определяется степенью детализации структур данных и, таким образом, может включать в себя множество доступов только для чтения к объектам низкого уровня, которые инкапсулированы и, следовательно, не доступны никаким другим, более прямым способом. Поскольку повышенная степень детализации является предпосылкой для более широкого повторного использования кода , наблюдается тенденция к мелкозернистым структурам данных и соответствующему увеличению количества дискретных объектов (и их методов) и, следовательно, вызовов подпрограмм. Создание божественных объектов активно не приветствуется. Конструкторы также добавляют к счетчику, поскольку они также являются вызовами подпрограмм (если они не встроены). Проблемы производительности, вызванные чрезмерной детализацией, могут не проявиться до тех пор, пока масштабируемость не станет проблемой.
  • Для других парадигм, где может использоваться сочетание вышеперечисленных парадигм, использование подпрограмм менее предсказуемо. 350>Выделение динамической памяти для хранения сообщений и объектов

    Уникально, объектно-ориентированная парадигма включает выделение динамической памяти из кучи как для создания объектов, так и для передачи сообщений. Тест 1994 года - «Затраты на выделение памяти в больших программах на C и C ++», проведенный Digital Equipment Corporation на разнообразном программном обеспечении с использованием инструмента профилирования на уровне инструкций, позволил измерить, сколько инструкций требуется для распределения динамической памяти.. Результаты показали, что наименьшее абсолютное количество выполненных инструкций составляло в среднем около 50, но другие достигали 611. См. Также «Куча: Удовольствия и боли» Мурали Р. Кришнана, в котором говорится: «Реализации кучи обычно остаются общими для всех платформ, и следовательно, имеют большие накладные расходы ". В статье IBM 1996 года «Масштабируемость алгоритмов динамического распределения памяти» Аруна Айенгара из IBM демонстрируются различные алгоритмы динамической памяти и их соответствующее количество инструкций. Даже рекомендованный алгоритм MFLF I (HS Stone, RC 9674) показывает количество инструкций в диапазоне от 200 до 400. Приведенный выше пример псевдокода не включает реалистичную оценку этой длины пути выделения памяти или задействованных накладных расходов префикса памяти и связанного с этим мусора. накладные расходы по сбору. Настоятельно предполагая, что выделение кучи - нетривиальная задача, один программный микрокаллокатор с открытым исходным кодом, разработанный разработчиком игры Джоном В. Рэтклиффом, состоит почти из 1000 строк кода.

    Динамически отправляемые вызовы сообщений против прямых накладных расходов на вызов процедур

    В своей аннотации «Оптимизация объектно-ориентированных программ с использованием статического анализа иерархии классов» Джеффри Дин, Дэвид Гроув и Крейг Чемберс из Департамента компьютерных наук и Инженеры из Вашингтонского университета утверждают, что «интенсивное использование наследования и динамически связанных сообщений, вероятно, сделает код более расширяемым и многоразовым, но это также накладывает значительные накладные расходы на производительность по сравнению с эквивалентными, но нерасширяемая программа, написанная не объектно-ориентированным способом. В некоторых областях, таких как пакеты структурированной графики, стоимость дополнительной гибкости, обеспечиваемой за счет использования сильно объектно-ориентированного стиля, является приемлемой. Однако в других областях успешно В качестве базовых библиотек структур данных, пакетов для численных вычислений, библиотек рендеринга и фреймворков моделирования на основе трассировки стоимость передачи сообщений может быть слишком высокой, что вынуждает программиста избегать объектно-ориентированного программирования в «горячих точках» своего приложения. "

    Сериализация объектов

    Сериализация накладывает большие накладные расходы при передаче объектов из одной системы в другую, особенно когда передача осуществляется в удобочитаемых форматах, таких как Extensible Markup Language (XML ) и нотации объектов JavaScript (JSON ). Это контрастирует с компактными двоичными форматами для не объектно-ориентированных данных. И кодирование, и декодирование значения данных объекта и его атрибутов участвуют в процессе сериализации, который также включает понимание сложных проблем, таких как наследование, инкапсуляция и скрытие данных.

    Параллельные вычисления

    Университет Карнеги-Меллона Профессор Роберт Харпер в марте 2011 года написал: «В этом семестре мы с Дэном Ликатой совместно преподаем новый курс по функциональное программирование для перспективных специалистов первого года обучения CS... Объектно-ориентированное программирование полностью исключено из вводной учебной программы, поскольку оно является одновременно антимодульным и антипараллельным по самой своей природе и, следовательно, не подходит для современной CS Учебный план. Предлагаемый новый курс по методологии объектно-ориентированного проектирования будет предложен на втором курсе для тех студентов, которые хотят изучать эту тему. "

    См. также
    Ссылки
    Дополнительная литература
    Внешние ссылки
Последняя правка сделана 2021-05-15 08:04:59
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте