Функции обратного вызова PHP и ссылки на переменные


Я работаю над решением "Очень большой суммы" на ХакеррАнке. Цель состоит в том, чтобы получить сумму всех элементов массива без использования array_sum. Для ясности мой пример кода здесь немного отличается от кода там.

Это работает:

$arr = array( 
    1000000001,
    1000000002,
    1000000003,
    1000000004,
    1000000005
);

$total = 0;
array_walk($arr, 'totalize');
echo '*' . $total;

function totalize( $arr_item ) {
    global $total;
    $total += $arr_item;
}

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

$arr = array( 
    1000000001,
    1000000002,
    1000000003,
    1000000004,
    1000000005
);

$total = 0;
array_walk($arr, 'totalize', $total);
echo '*' . $total;

function totalize( $arr_item, $key, &$total ) {
    $total += $arr_item;
    echo $arr_item . '.' . $key . '.' . $total . '<br />';
}

Это дает это как вывод:

1000000001.0.1000000001
1000000002.1.2000000003
1000000003.2.3000000006
1000000004.3.4000000010
1000000005.4.5000000015
*0

Почему сумма $ суммируется правильно, но затем отбрасывается?

Ответ: Благодаря @miken32 я обнаружил, что это работает:

$arr = array( 
    1000000001,
    1000000002,
    1000000003,
    1000000004,
    1000000005
);

$total = 0;
array_walk($arr, function( $arr_item ) use ( &$total ) { totalize( $arr_item, $total ); } );
echo '*' . $total;

function totalize( $arr_item, &$total ) {
    $total += $arr_item;
    echo $arr_item . '.' . $total . '<br />';
}

Это дает следующий результат:

1000000001.1000000001
1000000002.2000000003
1000000003.3000000006
1000000004.4000000010
1000000005.5000000015
*5000000015
Author: Cyrcle, 2016-05-03

2 answers

В этом конкретном случае вы можете использовать анонимную функцию с ключевым словом use для импорта $total в область действия функции.

$arr = [
    1000000001,
    1000000002,
    1000000003,
    1000000004,
    1000000005
];

$total = 0;
array_walk($arr, function ($arr_item) use (&$total) {$total += $arr_item;});
echo '*' . $total;
 3
Author: miken32, 2016-05-03 19:22:55

Третий аргумент array_walk() не может быть передан в качестве ссылки. Когда вы вызываете array_walk(), он использует значение, которое вы передаете в качестве третьего аргумента, для инициализации локальной переменной. Затем он передает локальную переменную по ссылке на ваш обратный вызов (потому что именно так определяется ваш обратный вызов).

Но нет связи между глобальной переменной $total и переменной, переданной вашему обратному вызову (аргумент $total из totalize()). Вы можете увидеть это сами, если измените totalize(), чтобы также отображать значение глобальной переменной $total.

Более подходящим способом достижения вашей цели является использование array_reduce(). Первый пример в документации - это именно то решение вашей проблемы.

 2
Author: axiac, 2016-05-03 18:57:46