Сравнение парадигм программирования
В этой статье есть несколько проблем. Пожалуйста, помогите улучшить его или обсудите эти проблемы на странице обсуждения . ( Узнайте, как и когда удалять эти шаблонные сообщения )
|
В этой статье делается попытка изложить различные сходства и различия между различными парадигмами программирования в виде сводки как в графическом, так и в табличном формате со ссылками на отдельные обсуждения этих сходств и различий в существующих статьях Википедии.
подходы парадигмы Основные
Существует два основных подхода к программированию: [1]
- Императивное программирование – фокусируется на том, как выполнять, определяет поток управления как инструкции программы , которые изменяют состояние .
- Декларативное программирование – фокусируется на том, что выполнять, определяет логику программы, но не подробный поток управления .
Следующие широко считаются основными парадигмами программирования, как видно при измерении популярности языков программирования:
- Процедурное программирование – определяет шаги, которые программа должна предпринять для достижения желаемого состояния.
- Функциональное программирование — рассматривает программы как оценку математических функций и избегает состояния и изменяемых данных.
- Объектно-ориентированное программирование (ООП) – организует программы как объекты : структуры данных , состоящие из атрибутов и методов вместе с их взаимодействиями.
Ниже приведены распространенные типы программирования, которые можно реализовать с использованием различных парадигм:
- Программирование, управляемое событиями программой — поток управления определяется событиями , такими как входные данные датчиков или действия пользователя ( щелчки мыши , нажатия клавиш) или сообщения от других программ или потоков .
- Программирование на основе автоматов - программа или ее часть рассматривается как модель конечного автомата или любого другого формального автомата.
- Реактивное программирование — это парадигма декларативного программирования, связанная с потоками данных и распространением изменений.
Подпрограммы, реализующие методы ООП, в конечном итоге могут быть закодированы в императивном, функциональном или процедурном стиле, который может или не может напрямую изменять состояние от имени вызывающей программы. Между парадигмами неизбежно существует некоторое совпадение, но основные особенности или определяемые различия суммированы в этой таблице:
Различия в терминологии [ править ]
Несмотря на параллельное существование множества (типов) парадигм программирования (иногда с явно противоречивыми определениями), многие из основных фундаментальных компонентов остаются более или менее одинаковыми ( константы , переменные , поля данных , вызовы подпрограмм и т. д.) и неизбежно должны быть включены в каждую из них. отдельная парадигма с одинаково схожими атрибутами или функциями. Приведенная выше таблица не предназначена для определения точных сходств, а скорее является указателем того, где искать дополнительную информацию, исходя из разных названий этих объектов в каждой парадигме. Еще больше усложняют ситуацию нестандартизированные реализации каждой парадигмы во многих языках программирования , особенно в языках, поддерживающих несколько парадигм , каждая из которых имеет свой собственный жаргон .
Языковая поддержка [ править ]
Синтаксический сахар — это улучшение функциональности программы за счет введения языковых функций, облегчающих конкретное использование, даже если результат можно было бы достичь и без них. Одним из примеров синтаксического сахара могут быть классы, используемые в объектно-ориентированных языках программирования. Императивный язык C может поддерживать объектно-ориентированное программирование посредством указателей на функции , приведения типов и структур. Однако такие языки, как C++, стремятся сделать объектно-ориентированное программирование более удобным, вводя синтаксис, специфичный для этого стиля кодирования. Более того, специализированный синтаксис подчеркивает объектно-ориентированный подход. Точно так же функции и синтаксис циклов в C (и других процедурных и структурированных языках программирования) можно считать синтаксическим сахаром. Язык ассемблера может поддерживать процедурное или структурированное программирование благодаря своим возможностям изменения значений регистров и выполнения ветвления в зависимости от состояния программы. Однако в таких языках, как C, был введен синтаксис, специфичный для этих стилей кодирования, чтобы сделать процедурное и структурированное программирование более удобным. Особенности языка C# (C Sharp), такие как свойства и интерфейсы, также не содержат новых функций, но предназначены для того, чтобы сделать хорошие практики программирования более заметными и естественными.
Некоторые программисты считают эти функции неважными или даже несерьезными. Например, Алан Перлис однажды пошутил, ссылаясь на языки, разделенные скобками , что «синтаксический сахар вызывает рак точки с запятой » (см. Эпиграммы по программированию ).
Расширением этого является синтаксический сахарин или необоснованный синтаксис, который не облегчает программирование. [11]
Сравнение производительности [ править ]
Только по общей длине пути команд программа, написанная в императивном стиле и не использующая подпрограммы, будет иметь наименьшее значение. Однако двоичный размер такой программы может быть больше, чем у той же программы, закодированной с использованием подпрограмм (как в функциональном и процедурном программировании), и будет ссылаться на большее количество нелокальных физических инструкций, что может увеличить промахи в кэше и накладные расходы на выборку команд в современных процессорах .
Парадигмы, которые широко используют подпрограммы (включая функциональные, процедурные и объектно-ориентированные) и не используют также значительного встроенного расширения (встраивание посредством оптимизации компилятора ), следовательно, будут использовать большую часть общих ресурсов для связей подпрограмм. Объектно-ориентированные программы, которые не изменяют состояние программы намеренно напрямую, а вместо этого используют методы-мутаторы (или сеттеры ) для инкапсуляции этих изменений состояния, как прямое следствие, будут иметь больше накладных расходов. Это связано с тем, что передача сообщений — это, по сути, вызов подпрограммы, но с тремя дополнительными издержками: динамическое выделение памяти , копирование параметров и динамическая диспетчеризация . Получение памяти из кучи и копирование параметров для передачи сообщений может потребовать значительных ресурсов, намного превышающих те, которые необходимы для изменения состояния. Аксессоры (или геттеры ), которые просто возвращают значения частных переменных-членов, также зависят от аналогичных подпрограмм передачи сообщений вместо использования более прямого присваивания (или сравнения), увеличивая общую длину пути.
Управляемый код [ править ]
Для программ, выполняющихся в среде управляемого кода , такой как платформа .NET , многие проблемы влияют на производительность, на которую существенно влияют парадигма языка программирования и различные используемые языковые функции. [12]
Примеры псевдокода, сравнивающие различные парадигмы [ править ]
Сравнение псевдокода , регистровую императивного, процедурного и объектно-ориентированного подходов, используемых для вычисления площади круга (πr²), предполагая отсутствие встраивания подпрограмм , отсутствие макросов препроцессоров арифметику и взвешивание каждого «шага» инструкции как всего лишь 1 инструкции – как Грубая мера длины пути инструкции – представлена ниже. Шаг инструкции, который концептуально выполняет изменение состояния, в каждом случае выделен жирным шрифтом. Арифметические операции, используемые для вычисления площади круга, одинаковы во всех трех парадигмах, с той разницей, что процедурная и объектно-ориентированная парадигмы заключают эти операции в вызов подпрограммы, что делает вычисление общим и пригодным для повторного использования. Тот же эффект может быть достигнут в чисто императивной программе, использующей препроцессор макросов, только за счет увеличения размера программы (только на каждом месте вызова макроса) без соответствующих пропорциональных затрат времени выполнения (пропорциональных n вызовов - которые могут быть расположены в пределах внутренний цикл , например). И наоборот, встраивание подпрограмм компилятором может уменьшить размер процедурных программ до размера, близкого к чисто императивному коду. Однако для объектно-ориентированных программ, даже со встраиванием, сообщения все равно необходимо создавать (из копий аргументов) для обработки объектно-ориентированными методами. Накладные расходы на вызовы, виртуальные или иные, зависят не от изменения потока управления , а от затрат на сопутствующие соглашения о вызовах , таких как код пролога и эпилога , настройка стека и аргументов . передача [13] (см. здесь [14] для более реалистичной длины пути инструкций, стека и других затрат, связанных с вызовами на платформе x86 ). См. также здесь [15] для слайд-презентации Эрика С. Робертса («Выделение памяти переменным», глава 7) [16] – иллюстрация использования стека и кучи памяти при суммировании трех рациональных чисел в объектно-ориентированном языке Java .
Императив | процедурный | Объектно-ориентированный |
---|---|---|
load r; 1 r2 = r * r; 2 result = r2 * "3.142"; 3 . . . . . . . . . . . . . . . . . . .... storage ............. result variable constant "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 ............................................... main proc: load r; 1 call area(r,result); +load p = address of parameter list; 2 +load v = address of subroutine 'area'; 3 +goto v with return; 4 . . . . .... storage ............. result variable constant "3.142" parameter list variable function pointer (==>area) stack storage |
circle.area method(r2): push stack 7 load r2; 8 r3 = r2 * r2; 9 res = r3 * "3.142"; 10 pop stack 11 return(res); 12,13 ............................................... main proc: load r; 1 result = circle.area(r); +allocate heap storage; 2[See 1] +copy r to message; 3 +load p = address of message; 4 +load v = addr. of method 'circle.area' 5 +goto v with return; 6 . . .... storage ............. result variable (assumed pre-allocated) immutable variable "3.142" (final) (heap) message variable for circle method call vtable(==>area) stack storage |
Преимущества процедурной абстракции и полиморфизма в объектно-ориентированном стиле плохо иллюстрируются небольшим примером, подобным приведенному выше. Этот пример предназначен главным образом для иллюстрации некоторых внутренних различий в производительности, а не для абстракции или повторного использования кода.
Подпрограмма, накладные расходы на вызов метода [ править ]
Наличие (называемой) подпрограммы в программе не вносит ничего дополнительного в функциональность программы независимо от парадигмы, но может в значительной степени способствовать структурированию и универсальности программы, значительно упрощая ее написание, изменение и расширение. [17] Степень, в которой различные парадигмы используют подпрограммы (и соответствующие требования к памяти), влияет на общую производительность всего алгоритма, хотя, как отметил Гай Стил в статье 1977 года, хорошо спроектированная реализация языка программирования может иметь очень низкие накладные расходы на процедурную абстракцию. (но в большинстве реализаций сетует, что на практике этого редко достигают - будучи «довольно необдуманными или небрежными в этом отношении»). В той же статье Стил также приводит аргументы в пользу автоматного программирования (с использованием вызовов процедур с хвостовой рекурсией ) и приходит к выводу, что «мы должны относиться к вызовам процедур со здоровым уважением» (потому что они мощные), но предлагает «использовать их экономно». " [17]
По частоте вызовов подпрограмм:
- Для процедурного программирования степень детализации кода во многом определяется количеством дискретных процедур или модулей .
- Для функционального программирования обычным является частый вызов библиотечных подпрограмм. [ нужна ссылка ] но часто может быть встроен оптимизирующим компилятором
- В объектно-ориентированном программировании количество вызываемых вызовов методов также частично определяется степенью детализации структур данных и, таким образом, может включать в себя множество доступов только для чтения к объектам низкого уровня, которые инкапсулированы и, следовательно, не доступны никаким другим, более прямым, способом. способ. Поскольку повышенная степень детализации является предпосылкой для большего повторного использования кода , наблюдается тенденция к более мелкозернистым структурам данных и соответствующему увеличению количества дискретных объектов (и их методов) и, следовательно, вызовов подпрограмм. Создание божественных объектов активно не поощряется. Конструкторы также увеличивают счет, поскольку они также являются вызовами подпрограмм (если они не встроены). Проблемы с производительностью, вызванные чрезмерной детализацией, могут не проявиться до тех пор, пока масштабируемость не станет проблемой.
- Для других парадигм, где может использоваться сочетание вышеуказанных парадигм, использование подпрограмм менее предсказуемо.
Выделение динамической памяти для хранения сообщений и объектов [ править ]
Уникальность объектно-ориентированной парадигмы заключается в динамическом выделении памяти из динамической памяти как для создания объектов, так и для передачи сообщений. Тест 1994 года «Затраты на выделение памяти в больших программах на C и C++», проведенный Digital Equipment Corporation на различном программном обеспечении с использованием инструмента профилирования на уровне инструкций, измерял, сколько инструкций требуется для динамического выделения памяти. Результаты показали, что наименьшее абсолютное количество выполненных инструкций в среднем составляло около 50, но другие достигали 611. [18] См. также «Куча: удовольствия и боли» Мурали Р. Кришнана. [19] в котором говорится: «Реализации кучи, как правило, остаются общими для всех платформ и, следовательно, имеют большие накладные расходы». Статья IBM 1996 года «Масштабируемость алгоритмов динамического распределения памяти», написанная Аруном Айенгаром из IBM. [20] демонстрирует различные алгоритмы динамического хранения и соответствующее количество инструкций. Даже рекомендуемый алгоритм MFLF I (HS Stone, RC 9674) показывает количество инструкций в диапазоне от 200 до 400. Приведенный выше пример псевдокода не включает в себя реалистичную оценку длины этого пути выделения памяти или задействованных накладных расходов префикса памяти и последующего связанного с этим мусора. сбор накладных расходов. Настоятельно предполагая, что распределение кучи — нетривиальная задача, один с открытым исходным кодом программный микрораспределитель , созданный разработчиком игр Джоном Рэтклиффом , состоит почти из 1000 строк кода. [21]
Динамически отправляемые вызовы сообщений и накладные расходы на вызов процедур прямой
В своем реферате « Оптимизация объектно-ориентированных программ с использованием статического анализа иерархии классов » [22] Джеффри Дин, Дэвид Гроув и Крейг Чемберс с факультета компьютерных наук и инженерии Вашингтонского университета утверждают, что «интенсивное использование наследования и динамически связанных сообщений, вероятно, сделает код более расширяемым и пригодным для повторного использования, но это также налагает значительные издержки производительности по сравнению с эквивалентной, но нерасширяемой программой, написанной необъектно-ориентированным способом. В некоторых областях, таких как пакеты структурированной графики, цена дополнительной гибкости, обеспечиваемой использованием сильно объектно-ориентированного стиля. Однако в других областях, таких как библиотеки базовых структур данных, пакеты численных вычислений, библиотеки рендеринга и среды моделирования на основе трассировки, стоимость передачи сообщений может быть слишком велика, что вынуждает программиста избегать объектно-ориентированного программирования. «горячие точки» их применения».
Сериализация объектов [ править ]
Сериализация приводит к большим накладным расходам при передаче объектов из одной системы в другую, особенно когда передача осуществляется в удобочитаемых форматах, таких как расширяемый язык разметки ( XML ) и нотация объектов JavaScript ( JSON ). Это контрастирует с компактными двоичными форматами необъектно-ориентированных данных. В процессе сериализации участвуют как кодирование, так и декодирование значения данных объекта и его атрибутов, что также включает в себя понимание сложных проблем, таких как наследование, инкапсуляция и сокрытие данных.
Параллельные вычисления [ править ]
Университета Карнеги-Меллона Профессор Роберт Харпер в марте 2011 года писал: «В этом семестре мы с Дэном Ликатой совместно читаем новый курс по функциональному программированию для первокурсников, желающих получить специальность по информатике... Объектно-ориентированное программирование полностью исключено из вводной учебной программы. , поскольку он по своей природе одновременно антимодулен и антипараллелен и, следовательно, не подходит для современной учебной программы по информатике. Предлагаемый новый курс по методологии объектно-ориентированного проектирования будет предлагаться на втором курсе для тех студентов, которые хотят учиться. эту тему». [23]
См. также [ править ]
- Сравнение языков программирования
- Сравнение языков программирования (базовые инструкции)
- Детализация#Вычисления
- Передача сообщений
- Подпрограмма
Ссылки [ править ]
- ^ «Парадигмы программирования: каковы принципы программирования?» . ИОНОС . Проверено 30 мая 2022 г.
- ^ «Архивная копия» (PDF) . Архивировано из оригинала (PDF) 02 февраля 2017 г. Проверено 18 декабря 2015 г.
{{cite web}}
: CS1 maint: архивная копия в заголовке ( ссылка ) - ^ «Функциональное программирование C#» . август 2020 года . Проверено 14 августа 2015 г.
- ^ Руис, Седрик (май 2014 г.). «Функциональный CoffeeScript для нетерпеливых» . Блог Седрика Руиса . Седрик Руис . Проверено 9 августа 2015 г.
- ^ «Функциональное программирование · Advanced R» .
- ^ Шелли, Асаф (22 августа 2008 г.). «Ошибки объектно-ориентированного моделирования» . Сеть программного обеспечения Intel . Проверено 4 июля 2010 г.
- ^ Йегге, Стив (30 марта 2006 г.). «Казнь в царстве существительных» . steve-yegge.blogspot.com . Проверено 3 июля 2010 г.
- ^ «Data-Oriented Design (или почему вы можете выстрелить себе в ногу с помощью ООП) – игры изнутри» .
- ^ Крокфорд, Дуглас. «JavaScript: самый непонятый язык программирования в мире» . crockford.com.
- ^ Крокфорд, Дуглас. «Частные члены в JavaScript» . crockford.com.
- ^ «Файл жаргона v4.4.7: «синтаксический сахар» » .
- ^ Грей, Ян (июнь 2003 г.). «Написание более быстрого управляемого кода: знайте, чего это стоит» . MSDN . Майкрософт.
- ^ «Реальная стоимость звонков» . WordPress.com. 30 декабря 2008 г.
- ^ «Диассемблирование/функции X86 и фреймы стека — Wikibooks, открытые книги для открытого мира» .
- ^ Робертс, Эрик С. (2008). «Искусство и наука Java; Глава 7: Объекты и память» . Стэнфордский университет. Архивировано из оригинала 6 июня 2011 г. Проверено 17 мая 2010 г.
- ^ Робертс, Эрик С. (2008). Искусство и наука Явы . Аддисон-Уэсли. ISBN 978-0-321-48612-7 . Архивировано из оригинала 6 июня 2011 г. Проверено 17 мая 2010 г.
- ↑ Перейти обратно: Перейти обратно: а б Гай Льюис Стил-младший «Разоблачение мифа о« дорогом вызове процедур », или Реализации вызовов процедур, считающиеся вредными, или Лямбда: окончательный вариант GOTO». Лаборатория искусственного интеллекта Массачусетского технологического института. Памятка лаборатории искусственного интеллекта AIM-443. Октябрь 1977 г. [1] Архивировано 29 декабря 2009 г. в Wayback Machine [2] [3]
- ^ Детлефс, Дэвид; Доссер, Эл; Зорн, Бенджамин (июнь 1994 г.). «Стоимость выделения памяти в больших программах на C и C++; страница 532». Программное обеспечение: практика и опыт . 24 (6): 527–542. CiteSeerX 10.1.1.30.3073 . дои : 10.1002/спе.4380240602 . S2CID 14214110 .
- ^ Кришнан, Мурали Р. (февраль 1999 г.). «Куча: Удовольствия и боли» . microsoft.com.
- ^ «Масштабируемость алгоритмов динамического распределения памяти». 1996. CiteSeerX 10.1.1.3.3759 .
- ^ "Микроаллокатор.h" . Гугл-код . Проверено 29 января 2012 г.
- ^ Дин, Джеффри; Гроув, Дэвид; Чемберс, Крейг (1995). «Оптимизация объектно-ориентированных программ с использованием статического анализа иерархии классов». Объектно-ориентированное программирование . Конспекты лекций по информатике. Том. 952. Вашингтонский университет. стр. 77–101. CiteSeerX 10.1.1.117.2420 . дои : 10.1007/3-540-49538-X_5 . ISBN 978-3-540-60160-9 .
- ^ Преподавание FP первокурсникам , из блога Харпера о преподавании вводных информатики. [4]
Дальнейшее чтение [ править ]
- «Распределитель памяти», Дуг Ли
- «Динамическое распределение памяти и связанные структуры данных», автор ( Квалификационное управление Шотландии )
- «Внутри распределителя памяти» доктора Ньюкомера, доктора философии.
Внешние ссылки [ править ]
- Сравнение парадигм программирования , доктор Рэйчел Харрисон и Линс Самаравира
- Сравнение парадигм программирования: оценка функциональных и объектно-ориентированных программ Харрисон Р. , Самаравира Л.Г., Доби М.Р. и Льюис П.Х. (1996), стр. 247–254. ISSN 0268-6961
- «Основные парадигмы программирования», Питер Ван Рой
- «Концепции, методы и модели компьютерного программирования» (2004) Питера Ван Роя и Сейфа Хариди, ISBN 0-262-22069-5
- Истинная стоимость звонков — из блога ученого-компьютерщика Стивена Пиджена «Сложнее, лучше, быстрее, сильнее».