Все задачи

Композиция функций

22 Apr 2013

У программиста в проекте встречался примерно такой код:

sub g
{
    my ($n) = @_;
    if ( $n > 30 || $n <= 0 ){
        return;
    } elsif ( $n == 1 ){
        return [1];
    }
    my @arr = (1,1);
    for my $i (2 .. $n-1){
        $arr[$i] = $arr[$i-1] + $arr[$i-2];
    }
    return \@arr;
}

sub f 
{
    my ($arr, $x) = @_;

    if (!$arr){
        return '';
    }

    for my $i (0 .. @$arr - 1){
        if ($arr->[$i] == $x){
            return $i;
        }
    }

    return '';
}

Но вот беда: при некоторых сочетаниях параметров код некорректно завершается. Например, f(g(25),144) работает нормально, а f(g(50),1134903170) – падает.

Подсказка

Показать

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

$z = g(50); 
print STDERR "g(50): $z\n";
f($z, 1134903170); 

Удивительно, но с отладочной печатью все сработало правильно!

Подсказка-2

Показать

Впрочем, появился ворнинг:

Use of uninitialized value $z in concatenation (.) or string

Разоблачение

Показать

Функция g выполняется в списковом контексте, а в списковом контексте return без параметра возвращает пустой список, и при интерполяции всех параметров для f получается список из одного элемента: (1134903170). Таким образом, первоначальный вызов f(g(50),1134903170) равнозначен f(1134903170), то есть порядок аргументов f портится. С отладочной же печатью получается f(undef, 1134903170).

Мораль: если функция используется ради возвращаемого значения (не ради побочных эффектов) – лучше избегать в ней оператора return без параметра.