Функция постепенно уменьшает статическую переменную, но не должна. PHP

Есть функция:

<?php 

 function test(){

     static $count = 0; 
     $count++;  
     echo $count;

     if ($count < 10) {
          test();
     }       

     $count--;  
     echo $count;
}

test();  

?>

Этот код производит: 123456789109876543210

Когда $count увеличивается до 10 - это понятно, он работает до тех пор, пока выражение if не будет оценено как false. но почему он постепенно уменьшается до 0? В моей логике новичка, $count-- в этом коде должен уменьшиться до 9 только один раз, и функция должна остановиться. Нет петли или цикла, который мог бы постепенно уменьшать его значение. Но мы видим, что он здесь и работает. Почему так происходит?


person Leonid    schedule 01.06.2016    source источник
comment
Почему функция должна останавливаться после первого декремента? Вы создали рекурсивную функцию - фактически выполняется первая половина функции, затем создается новый экземпляр функции, если $count < 10 истинно. Когда это в конечном итоге оказывается ложным, выполняется вторая половина функции; затем каждый экземпляр функции продолжается с того места, где он остановился, раскручивая все вызовы функций.   -  person andrewsi    schedule 02.06.2016
comment
вы видели рекурсивную функцию?   -  person Yehia Awad    schedule 02.06.2016


Ответы (2)


Возможно, все станет немного яснее, если вы измените вывод, чтобы подчеркнуть, как работает условный рекурсивный вызов:

<?php
function test($level=0) {
    static $count = 0;
    $count++;
    echo str_pad('a:', $level+2, ' ', STR_PAD_LEFT), $count, PHP_EOL;

    if ($count < 10) {
        test($level+1);
    }

    $count--;
    echo str_pad('b:', $level+2, ' ', STR_PAD_LEFT), $count, PHP_EOL;
}

test();

отпечатки

a:1
 a:2
  a:3
   a:4
    a:5
     a:6
      a:7
       a:8
        a:9
         a:10
         b:9
        b:8
       b:7
      b:6
     b:5
    b:4
   b:3
  b:2
 b:1
b:0

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

<?php
function c() {
    echo 'c in', PHP_EOL;
    echo 'c out', PHP_EOL;
}

function b() {
    echo 'b in', PHP_EOL;
    c();
    echo 'b out', PHP_EOL;
}

function a() {
    echo 'a in', PHP_EOL;
    b();
    echo 'a out', PHP_EOL;
}

a();

отпечатки

a in
b in
c in
c out
b out
a out

Достаточно легко, не так ли? Когда функция выходит/возвращается, выполнение продолжается с того места, где был сделан вызов. И это не связано с именем функции, а с самим вызовом функции, см., например. https://en.wikipedia.org/wiki/Call_stack .
Таким образом, даже если функция вызывает себя как

<?php
function foo($param) {
    echo "foo($param) in", PHP_EOL;
    if ( $param!==1 ) {
        foo(1);
    }
    echo "foo($param) out", PHP_EOL;
}

foo(0);

это не приводит к тому, что один выход/возврат отменяет все вызовы функций «одной и той же» функции, а просто поднимает одну «область»/кадр в стеке вызовов. Поэтому выход

foo(0) in
foo(1) in
foo(1) out
foo(0) out

Теперь сравните это с выводом «улучшенного» скрипта выше: надеюсь, это немного прояснит для вас ситуацию.

person VolkerK    schedule 01.06.2016
comment
Мне очень нравятся разные примеры, особенно второй с входом и выходом для представления кода до и после вызова функции в коде OP. - person Rizier123; 02.06.2016
comment
@Rizier123 Rizier123 Это единственный известный мне способ визуализировать что-то подобное в таком месте, как SO ;-) С другой стороны, это именно то, что я хочу, чтобы разработчики не делали: отслеживать поток выполнения с помощью строгих отладчиков printf ;-) - person VolkerK; 02.06.2016

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

Вот что происходит (аннотация):

//Iteration 1
function(){
    //Code before runs

    if stmt (TRUE)   //Iteration 2   
        function(); → function(){
               ←┐         //Code before runs
    //Code after│
}               │         if stmt (TRUE)          //Iteration 10  
                │             function(); → ... → function(){
                │                        ←┐           //Code before runs
                │         //Code after    │
                │     }                   │           if stmt (FALSE)
                └──── ← function returns  │
                        value and goes    │           //Code after runs
                        back to next line │       }
                                          └────── ← function returns value and goes back to next line


  • Таким образом, в 10-й функции вызов оператора if равен FALSE и больше не вызывает функции.

  • Теперь 10-й вызов функции доходит до конца и уменьшает переменную.

  • Он возвращает возвращаемое значение (NULL) и возвращается к предыдущему вызову функции, который выполняет остальную часть вызова функции, что также уменьшает переменную и так далее...

person Rizier123    schedule 01.06.2016