<rmcreative>

RSS

Рекурсивные анонимные функции PHP

1 июня 2011

// считаем факториал
$fact = function($n) use (&$fact)
{
    if($n==1)
        return 1;
 
    return $fact($n-1)*$n;
};
echo $fact(5); // =120

Комментарии RSS

  1. №4772
    idle
    idle 01 июня 2011 г., 6:58:17

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

  2. №4773
    bjaka_max
    bjaka_max 01 июня 2011 г., 8:54:54

    Что-то и правда не понятно зачем. Ну или пусть бы оно хоть как в JavaScript было что-ли: alert(( function(n) { if(n==1)return 1; return arguments.callee(n-1)*n } )(5)); А так оно уж что-то совсем бесполезно.

  3. №4774
    psih
    psih 01 июня 2011 г., 10:02:03

    А что, к примеру рекурсивная обработка при обходе через array_map может быть реализована.

  4. №4775
    idle
    idle 01 июня 2011 г., 10:11:26

    psih, она и без лямбд может быть организована и будет при этом выглядеть много более удобоваримо.

  5. №4777
    OZ
    OZ 01 июня 2011 г., 11:21:21

    idle, 1) если юзать array_map без анонимных функций, придётся создавать функцию с именем и хранить её - это менее удобно. 2) это просто пример, свет клином не сошёлся на одном примере. Само наличие возможности создавать рекурсии в closures может оказаться очень полезным для других целей, которые не так тривиальны.

  6. №4778
    idle
    idle 01 июня 2011 г., 11:36:13

    OZ, 1) «создавать и хранить» функцию придётся в любых случаях, аргумент про неудобство обычных функций мне вовсе не понятен; 2) не сошёлся, однако, я на этой странице вижу два примера — один Сашин, другой от psih — и в обоих не использование лямбд представляется мне наиболее верным вариантом.

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

  7. №4780
    OZ
    OZ 01 июня 2011 г., 11:46:50

    idle, 1) не в любом случае. При анонимной функции передаётся только указатель на неё, а при именованной - имя функции. Имя функции - использование пространства имён (если действие происходит внутри автономной функции, это может быть вообще недоступный вариант), лишняя связанность, и при изменении имени функции не всякая IDE может подсказать, что в параметре array_map тоже нужно что-то поменять.

    2) у меня нет примера, но в чьей-то голове этот пример может создать мысль "ага! точно! теперь я знаю как сделать это просто и красиво!". Поэтому я считаю этот пример полезным. И наличие возможности рекурсии - лучше, чем отсутствие такой возможности.

  8. №4781
    idle
    idle 01 июня 2011 г., 12:35:37

    OZ, 1) Не стану углубляться, но вы тут весьма вольно трактуете код из zend_execute_API.c. Более того вы, по всей видимости, забываете, что здесь мы говорим не столько о самих лямбдах, сколько об их рекурсивном варианте. Полезности лямбд в некотрых случаях я не отрицал. 2) Подход понятный, весьма похожий на подход с появлением goto в обсуждаемом языке.

  9. №4782
    OZ
    OZ 01 июня 2011 г., 12:42:42

    goto - отличная вещь и в PHP отлично реализована.

  10. №4783
    zorro
    zorro 01 июня 2011 г., 12:46:40

    goto - отличная вещь и в PHP отлично реализована

    Что значит отлично реализована?

  11. №4784
    OZ
    OZ 01 июня 2011 г., 12:50:31

    zorro, внутрь циклов и функций переходить нельзя.

  12. №4791
    Максим
    Максим 01 июня 2011 г., 21:01:09

    Жестокий изврат однако

  13. №4795
    Сергей #3
    Сергей #3 02 июня 2011 г., 22:07:58

    Ох, ввели в PHP замыкания - это хорошо. Но вот убогий

    use ($some_var)

    такой жести, пожалуй, больше нигде нет...

  14. №4797
    Максим
    Максим 03 июня 2011 г., 13:55:03

    Сергей #3, по всей видимости разработчики пхп решили его обезопасить по-максимуму когда создавали функции, потому что простой пример сломал бы все:

    $a=1;
    // Делаем что-то с $a
    function test($param)
    {
    if($param['admin'])
    $a=1;
    return $a;
    }

    По всей видимости на это они и ориентировались, хотя такой убогий код никто уже не пишет, хотя проблем с областями видимости и стало меньше, но убогости use это не отменяет.

  15. №4798
    Dmitry Scriptin
    Dmitry Scriptin 04 июня 2011 г., 16:16:43

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

    Видимо, PHP пишется так, чтобы было удобно интерпретатору, а не пользователю.

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

    Поэтому огласен с bjaka_max в том, что у функции должен быть специальный способ ссылаться на себя, по аналогии с arguments.callee в JavaScript.

    idle, что касается примера использования:

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

    К сожалению, в PHP для этого все равно придется создавать переменную именно из-за того, что нет аналога arguments.callee, о котором выше.

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

    Возможно, есть разница в деталях реализации обычных функций и анонимных, но даже в этом случае я бы не стал использовать такой неудобный способ. К тому же я уверен, что 99,(9)% пользователей PHP в такие тонкости никогда вдаваться не будут.

  16. №4842
    Ncs
    Ncs 17 июня 2011 г., 0:38:06

    0!=1

  17. №4857
    OZ
    OZ 24 июня 2011 г., 17:51:55

    Сегодня я заюзал не рекурсии, но переменную по ссылке (&$var) в use, и это помогло создать очень красивое решение, и для сохранения данных в памяти потребовалось гораздо меньше места.

    Спасибо за эту статью подсказку :)

  18. №5330
    dckur
    dckur 12 сент. 2011 г., 10:25:58

    Из той же оперы

    $fib = function($n) use(&$fib) {
            if($n == 0 || $n == 1) return 1;
            return $fib($n - 1) + $fib($n - 2);
        };
  19. №6036
    Thorn
    Thorn 12 марта 2012 г., 12:49:49

    А мне рекурсивная анонимная функция пригодилась в вводе дерева каталогов. Чтобы не засорять контроллер вспомогательными рекурсивными функциями рендера (тем более, что многие используются всего один раз в одном месте), я включил анонимную функцию прямо в шаблон. И вполне изящно получилось.

  20. №6037
    idle
    idle 12 марта 2012 г., 13:03:25

    Да охранит бог верстальщиков и сочувствующих от изящных шаблонов с рекурсивными лямбдами в частности и php кодом в целом.

  21. №6786
    userlive
    userlive 02 окт. 2012 г., 16:23:22

    OZ, "goto" во внутрь циклов и функций - это логический бред.

  22. №7521
    Виталий
    Виталий 14 февр. 2013 г., 19:22:21

    Рекурсия - зло. Циклы намного лучше ;-) ИМХО Конечно, бывают и умные компиляторы \ интерпретаторы, которые преобразуют рекурсивные вызовы функций в циклические, на счёт PHP, я не в курсе. Эм, ещё хотел добавить про JavaScript ... ребят, гибче и ужаснее языка высокого уровня я наверное не встречал, хотя ... есть Perl ;-)

  23. №9139
    Павел
    Павел 21 авг. 2014 г., 16:26:06

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

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

  24. №9140
    idle
    idle 21 авг. 2014 г., 18:14:16

    Для тех кто еще не понимает зачем, вам повезло, вы еще просто не сталкивались.

    Прошло три года, теперь, Павел, вы приведите пару ваших примеров: судя по "в который раз" их есть у вас %)

    Ну, и про php на фронте тоже не мешало бы раскрыть тему.

  25. №10382
    Yurii
    Yurii 22 марта 2016 г., 23:28:43

    Спасибо автору, очень выручил.

  26. №12054
    Iurii
    Iurii 10 нояб. 2020 г., 16:55:16

    if($n==1) лучше переписать в if($n <= 0), при передаче отрицательного числа - рекурсия уйдет в бесконечность

  1. Почта опубликована не будет.

  2. Можно использовать синтаксис Markdown или HTML.

  3. Введите ответ в поле. Щёлкните, чтобы получить другую задачу.