Pull to refresh

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

Reading time3 min
Views68K
imageКонечно многие из нас знакомы с этим понятием, однако данная статья рассчитана на новичков. В данном посте постараюсь рассмотреть данный феномен и привести примеры использования. Для начала необходимо понять что же такое лямбда-функция. Итак, лямбда-функция, часто ее называют анонимной, т. е. функция при определении которой не нужно указывать ее имя. Возвращаемое значение такой функцией присваивается переменной, через которую в последствие эту функцию можно вызывать.
До выхода PHP 5.3 определять лямбда-функции было возможно, но их нельзя было назвать полноценными. Сейчас я приведу пару примеров и продолжим рассматривать данные понятия.
<?php
$temp = create_function( '$match',
        'return (preg_match(\'/^{(.*)}$/\',$match[1],$m) ? "cms_$m[1]" : $match[1]);');
$query = 'SELECT * FROM {documents}';
$regExp = '/([^{"\']+|\'(?:\\\\\'.|[^\'])*\'|"(?:\\\\"|[^"])*"|{[^}{]+})/';
echo preg_replace_callback($regExp, $temp, $query);
//Выведет SELECT * FROM cms_documents
?>

Конечно динамическое создание функций не решает всех проблем, однако порой написание такой одноразовой функции может быть полезным. Можно расширить наш пример:
<?php
class Builder {
  private $query;
  private $prefix;

  public function __construct($prefix='' ) {
    $this->query = $query;
    $this->prefix = $prefix;
  }

  public function replaceCallback( $match ) {
    return ( preg_match('/^{(.*)}$/',$match[1],$m)
      ? ( empty($this->prefix) ? $m[1] : "{$this->prefix}_$m[1]" )
      : $match[1]
    );
  }

  public function build($query) {
    static $regExp = '/([^{"\']+|\'(?:\\\\\'.|[^\'])*\'|"(?:\\\\"|[^"])*"|{[^}{]+})/';
    return preg_replace_callback($regExp, array(&$this, "replaceCallback"), $query);
  }
};
$builder = new Builder(‘cms’);
echo $builder->build(“SELECT * FROM {documents}”);
//Выведет SELECT * FROM cms_documents
?>

Понятие замыкания наверняка знакомо программистам на JavaScript, а так же программистам на многих других языках. Замыкание — это функция, охватывающая или замыкающая текущую область видимости. Что бы понять все это, рассмотрим пример:
<?php 
$x = function( $number ) {
	return $number * 10;
};
echo $x(8);
// Выведет 80
?>

Как вы уже могли заметить, функция не имеет имени и результат присваивается переменной. Лямбда-функция, созданная таким образом, возвращает значение в виде объекта типа closure.
В PHP 5.3 стало возможно вызывать объекты как если бы они были функциями. А именно магический метод __invoke() вызывается каждый раз, когда класс вызывается как функция.
Переменные недоступны внутри функции, если они не объявлены глобальными, так же переменные из дочернего контекста недоступны если только не используется зарезервированное слово use. Обычно в PHP переменные передаются в замыкание значением, это поведение можно изменить с помощью ампермсанда перед переменной в выражении use. Рассмотрим пример:
<?php 
class ClosureTest { 
	public $multiplier; 
	public function __construct( $multilier ) { 
		$this->multiplier = $multilier; 
	}	 
	public function getClosure() { 
		$mul = &$this->multiplier; 
		return function( $number ) use( &$mul ) { 
			return $mul *	$number; 
		}; 
	} 
} 
$test = new ClosureTest(10); 
$x = $test->getClosure(); 
echo $x(8); // Выведет 80 
$test->multiplier = 2; 
echo $x(8); // Выведет 16 
?> 

Если убрать амперсанды то оба раза выведется 80, т. к. переменная $mul внутри замыкания будет копией, а не ссылкой.
Итак, осталось только выяснить как это можно применить на практике.
Рассмотрим пример:
<?php
class QueryBuilder extends Builder { 
	public function getQueryObject($query) { 
		$self = $this; 
		return function() use ($self,$query) { 
		$argv = func_get_args(); 
		foreach ( $argv as $i => $arg ) 
		$argv[$i] = mysql_escape_string($arg); 
		array_unshift($argv, $self->build($query)); 
		return call_user_func_array( “sprintf”, $argv); 
	}; 
	} 
}; 
$builder = new QueryBuilder(); 
$deleteBook = $builder->getQueryObject(“DELETE FROM {documents} WHERE id=%d”); 
$deleteBook( $_GET[‘id’] ); 
?>

Этот пример уже можно использовать для достаточно гибкого прототипирования. Достаточно объявить методы для всех SQL-операций с объектом.
Автор не призывает всех придерживаться такой практики, равно как и не считает что так лучше, все вышеописанное лишь пример использования, причем возможно не самый техничный и интересный, и не более того.
UPD Говоря о том самом длинном регулярном выражении, я не стал подписывать его в комментариях и решил вынести сюда. Оно лишь ищет строки в одинарных и двойных кавычках, а так же имена таблиц и экранирует их.
Tags:
Hubs:
+1
Comments58

Articles