Рекурсивные анонимные функции PHP
1 июня 2011
// считаем факториал $fact = function($n) use (&$fact) { if($n==1) return 1; return $fact($n-1)*$n; }; echo $fact(5); // =120
1 июня 2011
// считаем факториал $fact = function($n) use (&$fact) { if($n==1) return 1; return $fact($n-1)*$n; }; echo $fact(5); // =120
© 2005—2025, Александр Макаров (Sam Dark)
~ дизайн: fazeful design //Отработало за 0.01400 с. Скушано памяти: 0.9MB
Комментарии RSS по email OK
У кого-то из зенд снова воспалилось воображение не иначе: смысл необходимости рекурсии в анонимных функциях на столько туманен, что начинаешь подозревать грибы в причастности к делу.
Что-то и правда не понятно зачем. Ну или пусть бы оно хоть как в JavaScript было что-ли: alert(( function(n) { if(n==1)return 1; return arguments.callee(n-1)*n } )(5)); А так оно уж что-то совсем бесполезно.
А что, к примеру рекурсивная обработка при обходе через array_map может быть реализована.
psih, она и без лямбд может быть организована и будет при этом выглядеть много более удобоваримо.
idle, 1) если юзать array_map без анонимных функций, придётся создавать функцию с именем и хранить её - это менее удобно. 2) это просто пример, свет клином не сошёлся на одном примере. Само наличие возможности создавать рекурсии в closures может оказаться очень полезным для других целей, которые не так тривиальны.
OZ, 1) «создавать и хранить» функцию придётся в любых случаях, аргумент про неудобство обычных функций мне вовсе не понятен; 2) не сошёлся, однако, я на этой странице вижу два примера — один Сашин, другой от psih — и в обоих не использование лямбд представляется мне наиболее верным вариантом.
Если имеете жизненный пример (пусть даже не тривиальный, о которым вы пишете), где лямбды с рекурсией просто необходимы, укажите, я с удовольствием рассмотрю.
idle, 1) не в любом случае. При анонимной функции передаётся только указатель на неё, а при именованной - имя функции. Имя функции - использование пространства имён (если действие происходит внутри автономной функции, это может быть вообще недоступный вариант), лишняя связанность, и при изменении имени функции не всякая IDE может подсказать, что в параметре array_map тоже нужно что-то поменять.
2) у меня нет примера, но в чьей-то голове этот пример может создать мысль "ага! точно! теперь я знаю как сделать это просто и красиво!". Поэтому я считаю этот пример полезным. И наличие возможности рекурсии - лучше, чем отсутствие такой возможности.
OZ, 1) Не стану углубляться, но вы тут весьма вольно трактуете код из zend_execute_API.c. Более того вы, по всей видимости, забываете, что здесь мы говорим не столько о самих лямбдах, сколько об их рекурсивном варианте. Полезности лямбд в некотрых случаях я не отрицал. 2) Подход понятный, весьма похожий на подход с появлением goto в обсуждаемом языке.
goto - отличная вещь и в PHP отлично реализована.
Что значит отлично реализована?
zorro, внутрь циклов и функций переходить нельзя.
Жестокий изврат однако
Ох, ввели в PHP замыкания - это хорошо. Но вот убогий
такой жести, пожалуй, больше нигде нет...
Сергей #3, по всей видимости разработчики пхп решили его обезопасить по-максимуму когда создавали функции, потому что простой пример сломал бы все:
По всей видимости на это они и ориентировались, хотя такой убогий код никто уже не пишет, хотя проблем с областями видимости и стало меньше, но убогости use это не отменяет.
Конструкция
use
- это, насколько я понял, явный способ указать переменные, которые фукнция должна "замкнуть". Често говоря, весьма сомнительна полезность данной конструкции для пользователя - лучше бы интерпретатор сам определял, какие переменные используются, чтобы не использовать все (если это критично).Видимо, PHP пишется так, чтобы было удобно интерпретатору, а не пользователю.
Насчет организации рекурсии с помощью конструкции
use
есть замечательный анти-пример: если переопределить переменную, ссылающуюся на рекурсивную функцию, ниже в той же области видимости - получим удар подводными граблями.Поэтому огласен с bjaka_max в том, что у функции должен быть специальный способ ссылаться на себя, по аналогии с
arguments.callee
в JavaScript.idle, что касается примера использования:
Иногда необходимо возвращать в качестве результата рекурсивные функции - например, фабрика генераторов списков, где генераторы параметризуются. Т.е. есть функция, которая, принимая параметр генератора, возвращает генератор (анонимную функцию) с этим параметром.
К сожалению, в PHP для этого все равно придется создавать переменную именно из-за того, что нет аналога
arguments.callee
, о котором выше.Поэтому вы правы: организация рекурсии в анонимных функциях таким вот способом не дает никаких преимуществ перед использованием обычных функций. Все равно будет нужно создавать переменную.
Возможно, есть разница в деталях реализации обычных функций и анонимных, но даже в этом случае я бы не стал использовать такой неудобный способ. К тому же я уверен, что 99,(9)% пользователей PHP в такие тонкости никогда вдаваться не будут.
0!=1
Сегодня я заюзал не рекурсии, но переменную по ссылке (&$var) в use, и это помогло создать очень красивое решение, и для сохранения данных в памяти потребовалось гораздо меньше места.
Спасибо за эту статью подсказку :)
Из той же оперы
А мне рекурсивная анонимная функция пригодилась в вводе дерева каталогов. Чтобы не засорять контроллер вспомогательными рекурсивными функциями рендера (тем более, что многие используются всего один раз в одном месте), я включил анонимную функцию прямо в шаблон. И вполне изящно получилось.
Да охранит бог верстальщиков и сочувствующих от изящных шаблонов с рекурсивными лямбдами в частности и php кодом в целом.
OZ, "goto" во внутрь циклов и функций - это логический бред.
Рекурсия - зло. Циклы намного лучше ;-) ИМХО Конечно, бывают и умные компиляторы \ интерпретаторы, которые преобразуют рекурсивные вызовы функций в циклические, на счёт PHP, я не в курсе. Эм, ещё хотел добавить про JavaScript ... ребят, гибче и ужаснее языка высокого уровня я наверное не встречал, хотя ... есть Perl ;-)
Отлично, большое спасибо. Уже который раз использую эту фишку, и который раз приходится гуглить "анонимная рекурсивная функция", и который раз забываю эту несчастную передачу по ссылке.
Для тех кто еще не понимает зачем, вам повезло, вы еще просто не сталкивались. На фронте это скорее всего излишне. А вот на серверных частях приложений порой думаешь, чтобы я делал, если бы сейчас был какой-нибудь 2000 год и замыканий бы не было.
Прошло три года, теперь, Павел, вы приведите пару ваших примеров: судя по "в который раз" их есть у вас %)
Ну, и про php на фронте тоже не мешало бы раскрыть тему.
Спасибо автору, очень выручил.
if($n==1) лучше переписать в if($n <= 0), при передаче отрицательного числа - рекурсия уйдет в бесконечность