Функциональное программирование в PHP: функции высшего порядка

Как выучить PHP2 Изучение

Если вы изучите несколько фреймворков и крупномасштабных приложений, вы обязательно увидите функцию более высокого порядка. Многие языки поддерживают идею функций высшего порядка, включая JavaScript, Java,.NET, Python и даже PHP, и это лишь некоторые из них.

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

Первоклассные функции

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

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

Что такое функции высшего порядка?

Есть две основные характеристики, которые определяют функцию высшего порядка. Функция высшего порядка может реализовывать только одну или обе следующие идеи: функция, которая принимает одну или несколько функций в качестве входных данных или возвращает функцию в качестве выходных данных. В PHP есть ключевое слово, которое явно указывает на то, что функция является функцией высшего порядка: ключевое слово callable. Хотя это ключевое слово не обязательно должно присутствовать, ключевое слово позволяет легко их идентифицировать. Если вы видите функцию или метод с вызываемым параметром, это означает, что он принимает функцию в качестве входных данных. Еще один простой признак — если вы видите, что функция возвращает функцию, используя свой returnоператор. Оператор return может быть просто именем функции или даже анонимной / встроенной функцией. Ниже приведены несколько примеров каждого типа.

// Simple user-defined function we'll pass to our higher-order function
function echoHelloWorld() {
  echo "Hello World!";
}

// Higher-order function that takes a function as an input and calls it
function higherOrderFunction(callable $func) {
  $func();
}

// Call our higher-order function and give it our echoHelloWorld() function. 
// Notice we pass just the name as a string and no parenthesis.
// This echos "Hello World!"
higherOrderFunction('echoHelloWorld'); 

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

// Returns an existing function in PHP called trim(). Notice it's a simple string with no parentheses.
function trimMessage1() {
  return 'trim';
}

// Here's an example using an anonymous inline function
function trimMessage2() {
    return function($text) {
        return trim($text);
    };
}

// Returns the 'trim' function
$trim1 = trimMessage1(); 

// Call it with our string to trim
echo $trim1('  hello world  ');

// Returns the anonymous function that internally calls 'trim'
$trim2 = trimMessage1(); 

// Call it again with our string to trim
echo $trim2('  hello world  ');

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

Зачем вам использовать или создавать функцию высшего порядка?

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

Читайте также:  Отображение содержимого текстового файла в командной строке Linux

Добавление гибкости кода

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

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

Легко расширяйте свой код

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

Подражайте чертам другого языка

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

Примеры существующих функций высшего порядка в PHP

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

Динамический дуэт высшего порядка: array_map and array_filter

$arrayOfNums = [1,2,3,4,5];

// array_map: example of taking an inline anonymous function
$doubledNums = array_map(function($num) {
  return $num * 2;
}, $arrayOfNums);

var_dump($doubledNums); // Prints out array containing [2, 4, 6, 8, 10];

Давайте посмотрим, как использовать еще один, на этот раз с функцией фильтра, которую мы определили отдельно:

$arrayOfNums = [1,2,3,4,5];

// Example of creating a function and giving it to a higher-order function array_filter()
function isEven($num) {
  return ($num % 2) === ;
}

// array_filter is a higher-order function
$evenNums = array_filter($arrayOfNums, 'isEven');

var_dump($evenNums); // Prints out array containing [2, 4]

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

function printCustomMessage($message) {
  echo "$message";
}

call_user_func('printCustomMessage', 'Called custom message through the use of call_user_func!');

Как создавать свои собственные функции высшего порядка

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

function add($a, $b) {
  return $a + $b;
}

function subtract($a, $b) {
  return $a - $b;
}

function multiply($a, $b) {
  return $a * $b;
}

// Higher-order function that passes along $n1 and $n2
function calc($n1, $n2, $math_func) {
  return $math_func($n1, $n2);
}

$addedNums = calc(1, 5, 'add');
$subtractedNums = calc(1, 5, 'subtract');
$multipliedNums = calc(1, 5, 'multiply');

// Prints 6
echo "Added numbers: $addedNums\n";

// Prints -4
echo "Subtracted numbers: $subtractedNums\n";

// Prints 5
echo "Multiplied numbers: $multipliedNums\n";

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

Но подождите минутку: наш босс просто попросил нас добавить функцию для сложения двух строк в нашей существующей системе. Но объединение строк — не обычная математическая операция. Однако с нашей настройкой здесь нам просто нужно добавить конкатенацию строк и передать ее по конвейеру calc():

function addTwoStrings($a, $b) {
  return $a . " " . $b;
}

// Prints "Hello World!"
$concatenatedStrings = calc('Hello', 'World!', 'addTwoStrings');

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

Читайте также:  Что такое worker connections в Nginx?

Как это соотносится с другими языками, в которых есть функции высшего порядка? Давайте возьмем простой пример декоратора на Python и сравним его с JavaScript, а затем сравним с PHP. Таким образом, если вы знаете Python или JS, вы можете увидеть, насколько они эквивалентны.

Параллельное сравнение с Python и JavaScript

def say_message(func):
    def wrapper():
        print('Print before function is called')
        func()
        print('Print after function is called')
    return wrapper

def say_hello_world():
    print("Hello World!")

# Pass our hello world function to the say_message function. 
# It returns a function that we can then call
# Note: This line could have been replaced with the @say_message (pie syntax) on the say_hello_world method
say_hello_world = say_message(say_hello_world)

# Call the created function
say_hello_world()

# Output...
# Print before function is called
# Hello World!
# Print after function is called

Теперь давайте посмотрим, как этого можно достичь в функциях высшего порядка JavaScript:

// Takes a function, returns a function.
function say_message(func) {
  return function() {
      console.log("Print before function is called");
      func();
      console.log("Print after function is called");
  }
}

function say_hello_world() {
  console.log("Hello World!");
}

// Again pass our function to say_message as an input parameter
// say_message returns a function that is wrapped around the say_hello_world function
say_hello = say_message(say_hello_world);

// Call the function
say_hello();

// Output...
// Print before function is called
// Hello World!
// Print after function is called

Теперь, наконец, давайте сравним это с PHP:

// Takes a function, returns a function.
function say_message($func) {
  // We use 'use' so that our anonymous function can see $func coming in
  return function() use ($func) {
      echo "Print before function is called";
      $func();
      echo "Print after function is called";
  };
}

function say_hello_world() {
  echo "Hello World!";
}

// Again pass our function to say_message as an input parameter
// say_message returns a function that is wrapped around the say_hello_world function
$say_hello = say_message('say_hello_world');

// Call the function
$say_hello();

// Output...
// Print before function is called
// Hello World!
// Print after function is called

Как видно из параллельного сравнения, версия PHP очень похожа на синтаксис JavaScript. Но если вы немного знаете о Python и декораторах, вы можете увидеть, как декораторы могут быть переведены на один из двух других языков программирования.

Несколько слов о Lambdas/Anonymous функциях

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

Заключение

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

Затем мы показали несколько примеров существующих функций высшего порядка в PHP, таких как array_mapи array_filter, а затем перешли к созданию нашей собственной вызываемой функции высшего порядка calc(), которая принимала несколько входных функций и оперировала парой аргументов. Затем мы перешли к параллельному сравнению, показывающему, как выглядит решение PHP по сравнению с декоратором Python и реализацией JavaScript.
Надеюсь, вы узнали немного больше о функциях высшего порядка и о том, почему вы можете захотеть рассмотреть их в своем следующем крупномасштабном решении. Они могут предложить множество преимуществ и уменьшить количество избыточных кодов на типовой пластине.

Оцените статью
bestprogrammer.ru
Добавить комментарий