2016-12-20 3 views
2

Я хочу понять, почему эта функция ничего не возвращает.Рекурсивная функция не возвращает никакого значения

function fact($n, $p = 1) { 
    if ($n > 1) { 
     $p *= $n--; 
     fact($n, $p); 
    } else { 
     return $p; 
    } 
} 

var_dump(fact(5)); // NULL 
+2

попробуйте 'return fact ($ n, $ p);' –

+1

Поскольку вы возвращаетесь только в базовый регистр, результаты не базового случая никогда не передаются обратно. Как сказал выше, вам нужно вернуться. – Carcigenicate

ответ

2

Рекурсия - это петлевая конструкция, которая исходит из функциональных языков. Так что да, как отмечали другие, ваша функция работает некорректно, потому что ветвь true вашего оператора if ничего не возвращает. Тем не менее, у меня есть дополнительные замечания по поводу кода

function fact($n, $p = 1) { 
    if ($n > 1) { 
     // this makes it hard to reason about your code 
     $p *= $n--; 
     return fact($n, $p); 
    } else { 
     return $p; 
    } 
}

Вы на самом деле мутирует две переменные здесь в один выражение. Это умно, если вы пытаетесь сохранить код более коротким, но на самом деле есть еще лучший способ.

function fact($n, $p = 1) { 
    if ($n > 1) { 
     $p *= $n--; 
     // just compute the next values; no need to update $p or $n 
     return fact($n - 1, $p * $n); 
    } else { 
     return $p; 
    } 
}

Теперь мы не должны думать о том, как $p и $n изменения в индивидуальном порядке. Мы просто знаем, что мы снова вызываем fact с следующими значениями для каждого состояния $p и $n.

Имейте в виду, что эти принципы настолько сильны в некоторых функциональных языках программирования, что переназначение переменных нравится $p и $n даже не разрешено.


Наконец, мы должны говорить о вашем вашей утечке API, $p. Если кто-то указать значение при вызове fact, они могли бы получить неправильный ответ или вызвать ошибку

// bad ! 
fact(5, 10); // => 1200 

Это возможно только потому, что $p фактически подвергается в общественном API.Чтобы обойти эту проблему, у вас есть несколько вариантов

Один из них сделать, как @RonaldSwets предлагает:

function fact($n) { 
    // 1 is the base case, like you had for $p in your code 
    if ($n == 0) 
     return 1; 
    // otherwise return $n times the next value 
    else 
     return $n * fact($n - 1); 
} 

Другой использовать вспомогательную функцию, которая предназначена только для личного пользования

// function used by `fact` 
function fact_aux ($n, $p) { 
    if ($n == 0) 
    return $p; 
    else 
    return fact_aux($n - 1, $p * $n); 
} 

// function meant to be used by others 
function fact ($n) { 
    return fact_aux($n, 1); 
} 
+1

Большое вам спасибо. С помощью вас я, наконец, понимаю рекурсию – Aram810

3

Потому что, если условие if является true то не return заявление не когда-либо сталкивался. Возможно, вы имели в виду это:

if ($n > 1) { 
    $p *= $n--; 
    return fact($n, $p); // return the value 
} 
+0

Большое спасибо. Вы поможете мне лучше понять рекурсии. – Aram810

3

Вы пытаетесь назначить переменную, которую вы не прошли по ссылке. Либо передайте $ p по ссылке (&$p), либо используйте возвращаемое значение. В этом случае возвращаемое значение будет лучше.

Во-вторых, $n-- - это пост декремент, то есть ваш код не читает это красиво.

function fact($n) { 
    if ($n == 0) return 1; 
    return $n * fact($n - 1); 
} 

var_dump(fact(5)) 
+1

Не забывайте, что '0!' На самом деле ** 1 **. Просто измените свой предикат на '$ n == 0', а затем вы добры. – naomik

+0

Хорошая точка naomik –

1

Вы действительно возвращаете значение только тогда, когда $n == 1. Когда $n > 1, вы должны вернуть значение fact(n-1) в fact(n).

Смежные вопросы