Функциональное программирование и императивное программирование (LINQ to XML)

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

Функциональное и императивное программирование

Принципы функционального программирования формулировались специально для поддержки чисто функционального подхода к решению проблем. Функциональное программирование является одной из форм декларативного программирования. В отличие от этого, большинство традиционных языков, в том числе такие языки объектно-ориентированного программирования (OOP), как C#, Visual Basic, C++ и Java, разрабатывались в основном для императивного (процедурного) программирования.

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

Characteristic Императивный подход Функциональный подход
Основная направленность усилий программиста Способы выполнения (алгоритмы) задач и отслеживания изменений в их состоянии. Требуемые данные и преобразования.
Изменения состояний Важное — Не существует.
Порядок выполнения Важное — Низкая значимость.
Управление основным потоком данных Циклы, условия и вызовы функций (методов). Вызовы функций, включая рекурсивные.
Основная единица обработки Экземпляры структур или классов. Функции как полноценные объекты и коллекции данных.

Безусловно, большинство языков программирования было разработано в целях поддержки определенных подходов к программированию, но многие языки общего назначения являются достаточно гибкими, чтобы поддерживать несколько подходов. Например, большинство языков, содержащих указатели на функции, могут использоваться для надежной поддержки функционального программирования. Кроме того, C# и Visual Basic включают явные расширения языка для поддержки функционального программирования, включая лямбда-выражения и вывод типов. Одной из форм декларативного, функционального программирования является технология LINQ.

Функциональное программирование с помощью XSLT

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

Тем не менее XSLT доказал свою полезность при чисто функциональном подходе для преобразования XML из одного вида в другой. Чисто функциональное программирование с помощью LINQ to XML во многом похоже на XSLT. Однако конструкции программирования, представленные LINQ to XML, C#и Visual Basic, позволяют создавать чистые функциональные преобразования, которые являются более читаемыми и обслуживаемыми, чем XSLT.

Преимущества чистых функций

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

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

Переход для разработчиков OOP

Традиционное объектно-ориентированное программирование (OOP) является таковым, что большинство разработчиков привыкает писать код в императивном (процедурном) стиле. Переходя к разработке в чисто функциональном стиле, они должны изменить свое мышление и подход к разработке.

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

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

К счастью, C# и Visual Basic не требуют полного скачка к функциональному программированию, так как они поддерживают как императивные, так и функциональные подходы к программированию. Разработчик может сам выбирать нужный подход в зависимости от конкретного сценария. В действительности в программах часто сочетаются оба стиля.

См. также