«Забытые» парадигмы программирования



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

    Ладно. Введение это очень весело, но вы его все равно не читаете, так что кому интересно — добро пожаловать под кат!

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



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

    Это были машинные коды, языки ассемблера и ранние высокоуровневые языки, вроде Fortran.

    Ключевые моменты:


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

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

    В более высокоуровневых (таких как Си) состояние — это только память, инструкции могут быть сложнее и вызывать выделение и освобождение памяти в процессе своей работы.

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

    Основные понятия:


    — Инструкция
    — Состояние

    Порожденные понятия:


    — Присваивание
    — Переход
    — Память
    — Указатель

    Языки поддерживающие данную парадигму:


    Как основную:

    — Языки ассемблера
    — Fortran
    — Algol
    — Cobol
    — Pascal
    — C
    — C++
    — Ada

    Как вспомогательную:

    — Python
    — Ruby
    — Java
    — C#
    — PHP
    — Haskell (через монады)

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

    Структурное программирование



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

    Основоположниками структурного программирования были такие знаменитые люди как Э. Дейкстра и Н. Вирт.

    Языками-первопроходцами в этой парадигме были Fortran, Algol и B, позже их приемниками стали Pascal и C.

    Ключевые моменты:


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

    В структурном программировании мы по прежнему оперируем состоянием и инструкциями, однако вводится понятие составной инструкции (блока), инструкций ветвления и цикла.

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

    Иногда goto все-же делает код читабельнее, благодаря чему он до сих пор широко используется, несмотря на все заявления его противников.

    Основные понятия:


    — Блок
    — Цикл
    — Ветвление

    Языки поддерживающие данную парадигму:


    Как основную:

    — C
    — Pascal
    — Basic

    Как вспомогательную:

    — C#
    — Java
    — Python
    — Ruby
    — JavaScript

    Поддерживают частично:
    — Некоторые макроассемблеры (через макросы)

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

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



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

    Собственно еще раз были введены дополнительные понятия, которые позволили по-новому взглянуть на программирование.

    Этим понятием на этот раз была процедура.

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

    Ключевые моменты:


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

    В современном программировании процедура может иметь несколько точек выхода (return в C-подобных языках), несколько точек входа (с помощью yield в Python или статических локальных переменных в C++), иметь аргументы, возвращать значение как результат своего выполнения, быть перегруженной по количеству или типу параметров и много чего еще.

    Основные понятия:


    — Процедура

    Порожденные понятия:


    — Вызов
    — Аргументы
    — Возврат
    — Рекурсия
    — Перегрузка

    Языки поддерживающие данную парадигму:


    Как основную:

    — C
    — C++
    — Pascal
    — Object Pascal

    Как вспомогательную:

    — C#
    — Java
    — Ruby
    — Python
    — JavaScript

    Поддерживают частично:
    — Ранний Basic

    Стоит отметить, что несколько точек входа из всех этих языков поддерживаются только в Python.

    Модульное программирование



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

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

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

    Благодаря модулям впервые в программировании появилась серьезная инкапсуляция — возможно использовать какие-либо сущности внутри модуля, но не показывать их внешнему миру.

    Ключевые моменты:


    Модуль — это отдельная именованная сущность программы, которая объединяет в себе другие программные единицы, близкие по функциональности.

    Например файл List.mod включающий в себя класс List
    и функции для работы с ним — модуль.

    Папка Geometry, содержащая модули Shape, Rectangle и Triangle — тоже модуль, хоть и некоторые языки разделяют понятие модуля и пакета (в таких языках пакет — набор модулей и/или набор других пакетов).

    Модули можно импортировать (подключать), для того, чтобы использовать объявленные в них сущности.

    Основные понятия:


    — Модуль
    — Импортирование

    Порожденные понятия:


    — Пакет
    — Инкапсуляция

    Языки поддерживающие данную парадигму:


    Как основную:

    — Haskell
    — Pascal
    — Python

    Как вспомогательную:

    — Java
    — C#
    — ActionScript 3

    Поддерживают частично:
    — C/C++

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

    Вместо заключения


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

    Также я ничего не написал про экзотические парадигмы, вроде автоматного, аппликативного, аспект/агент/компонент-ориентированного программирования. Я не хотел делать статью сильно большой и опять-же если тема будет востребована, я напишу и об этих парадигмах, возможно более подробно и с примерами кода.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 18

      +3
      Странно, C# в процедурном.

      >>Стоит отметить, что несколько точек выхода из всех этих языков поддерживаются только в Python
      что? оО
        +2
        Упс, опечатка. Имелось ввиду несколько точек входа. И то с помощью yield.
          +1
          Про существование yield (и заодно await) в C# вы не в курсе?
            +2
            Теперь в курсе, спасибо, почитаю.
              +6
              в php так же есть yield, и в javascript тоже (если брать ES6), да и в ruby…
                0
                Можно и lua до кучи :)
          +4
          В императивной парадигме стоило бы упомянуть Fort.
          В модульной — Ada и OCaml.
            +1
            Forth*
              0
              Помню легенду, что 'h' было отброшено, что бы название в два слова поместилось. Но подтверждения найти не могу.
                0

                Это u было отброшено


                Forth is so named because in 1968 "the file holding the interpreter was labeled FOURTH, for 4th (next) generation software—but the IBM 1130 operating system restricted file names to 5 characters."[10]
            +1
            Процедура

            возвращать значение как результат своего выполнения

            Процедура != подпрограмма и не возвращает значения, просто выполняет список инструкций. Состояние же изменяется за счет глобальных переменных и передачи аргумента по ссылке.
              +6
              Классифицировано так грубо и условно, что в процессе совершенно исказился смысл.

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

              Или другой некорректный пример — Си++: в отличие от Си, тут файл (.cpp) вовсе не является модулем, а заголовок (.hpp) не является интерфейсом такогого модуля. А в Си — являлся, и назывался объектом, единицей трансляции. В этом отношении (но не в других) Си++ оказывается дальше от ООП, чем классический Си, что забавно.

                0
                А нас, кстати, так учили.
                  +1
                  В TAPL разбираются отличия ООП и модулей — с точки зрения теории типов они оказываются двойственными.
                    +3
                    К примеру, совершенно непонятно, как противопоставлять модульное программирование и ООП, если ООП — это разновидность модульного программирования, когда единственной единицей модульности является класс.


                    И вообще, все перечисленные парадигмы являются разновидностями императивной парадигмы. Помимо которой вполне себе заметны и развиты декларативная парадигма, парадигма метапрограммирования и событийно-ориентированная парадигма.

                    Вообще, достаточно хороший список есть на ru.wikipedia.org/wiki/Парадигма_программирования
                      0
                      Не ООП вообще разновидность модульного программирования, с классом=модулю, а только ООП в его общеизвестном виде. Это свойство распространенных сейчас языков, идущее, очевидно, из Явы, не более того.
                      +1
                      В руби есть yield и модули
                        +1
                        О как Pascal «универсален» — сразу во всех категориях!

                        Only users with full accounts can post comments. Log in, please.