Статьи

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

Многим программистам нравится говорить о функциональном программировании, но если вы спросите их, делали ли они когда-либо это, большинство их ответов будет «Нет». Причина довольно проста: нас учат думать обязательно, когда мы только начинаем учиться программировать, с точки зрения блок-схем и шагов, которые необходимо выполнить в программе. Поэтому в этой статье я объясню некоторые важные концепции функционального программирования и то, как писать функциональный код на PHP.

Важные концепции функционального программирования

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

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

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

неизменность

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

Рекурсия

Рекурсия также очень заметна в функциональном программировании. В императивном программировании мы можем использовать циклические конструкции, такие как for и foreach когда нам нужно манипулировать коллекциями или массивами, проходя через каждый элемент и сохраняя временную переменную для хранения текущего значения. Но из-за неизменности этот подход невозможен в функциональном программировании. Рекурсия — это ответ, потому что такой учет ведется неявно со стеком вызовов.

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

 <?php function sum($array) { if (empty($array)) return 0; else return $array[0] + sum(array_slice($array, 1)); } $total = sum(array(1, 2, 3)); // 6 

Пустой список вернет 0, что является нашим базовым условием. Для массива с более чем одним значением он вернет результаты добавления первого элемента с рекурсивной суммой всех других элементов.

Чистые функции и ссылочная прозрачность

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

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

Функции высшего порядка

Приведенные выше концепции могут быть реализованы практически на любом языке программирования, но функции первого класса и функции более высокого порядка являются двумя наиболее отличительными чертами функционального программирования. Я объяснил, что функции первого класса означают, что функции могут рассматриваться как значения. Функции высшего порядка — это функции, которые могут принимать функции в качестве аргументов и возвращать функцию в качестве результата. Сравнительно недавно были добавлены две важные функции, которые позволили нам писать функции более высокого порядка в PHP: лямбда-выражения и замыкания.

Лямбда-функции

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

Эта концепция была принята на разных языках. Фактически, вы, вероятно, используете лямбда-функции в повседневном программировании на JavaScript, передавая их в качестве функций обратного вызова для различных взаимодействий пользователей и вызовов Ajax.

 $("#myButton").click(function () { // do something }); 

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

PHP представил эту замечательную функцию в версии 5.3, которая позволяет нам писать код PHP аналогичным образом:

 <?php $square = function ($arg) { return $arg * $arg; }; $value = $square(2); // 4 

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

Затворы

Иногда вы захотите ссылаться на переменную из родительской области видимости внутри вашей функции. Замыкания аналогичны лямбда-функциям с небольшим отличием в том, что вы можете обращаться к переменным из внешней области видимости. Мы можем использовать «охват» и связать внешнюю переменную, use ключевое слово use PHP, также введенное в PHP 5.3.

 <?php $rate = .12; $findInterest = function ($value) use ($rate) { return $value * $rate; }; $interest = $findInterest(100); 

В этом случае мы не передаем процентную ставку каждый раз, когда вызываем функцию. Вместо этого мы определили его снаружи и сделали его доступным внутри функции с use ключевого слова use .

Частичные функции и карри

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

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

 <?php $volume = function ($length = 0, $width = 0, $height = 0) use (&$volume) { $args = func_get_args(); $numArgs = func_num_args(); if ($numArgs == 3) { return $length * $width * $height; } else if ($numArgs < 3) { return function() use(&$volume, $args) { $newArgs = array_merge($args, func_get_args()); return call_user_func_array($volume, $newArgs); }; } else { throw new BadFunctionCallException("Too many arguments"); } }; 

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

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

 <?php $standardVolume = $volume(10); $vol = $standardVolume(5, 5); // 250 

Карринг — это особый случай частичных функций, когда вы преобразуете функцию, которая принимает несколько аргументов, в несколько функций, каждая из которых принимает один аргумент. Например, что-то вроде f (x, y, z)f (x) (y) (z) (хотя синтаксис PHP не допускает вложения вызовов функций, подобных этой). Тимоти Борончик написал отличную статью о карри с практическим примером, если вы хотите увидеть больше.

Преимущества и недостатки

Есть много практических применений функциональных возможностей программирования в PHP. Например, лямбда-функции широко используются при работе с обратными вызовами. Например, используя платформу Slim, вы можете определить маршрут следующим образом:

 <?php $app = new SlimSlim(); $app->get("/home", function () { // show home page }); 

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

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

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

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

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

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

Резюме

Функциональное программирование — это не просто парадигма программирования; это способ мышления и рассуждения о вашей программе. Если вы можете мыслить функционально, вы можете заниматься функциональным программированием практически на любом языке.

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

Изображение через Fotolia