Xử lý con trỏ mảng trong foreach và hàm tính toán liên tiếp với PHP

Vấn đề con trỏ mảng trong foreach

Khi sử dụng vòng lặp foreach, PHP tự quản lý một con trỏ mảng riêng biệt, độc lập với con trỏ mảng chính. Ngay cả khi bạn ép buộc sử dụng tham chiếu &, PHP vẫn tạo ra một bản sao địa chỉ biến riêng, không chia sẻ cùng một con trỏ. Điều này dẫn đến lỗi khi bạn cố gắng thao tác trực tiếp lên con trỏ mảng bên trong foreach, dễ gây ra tình trạng key vượt quá độ dài mảng.

Demo kiểm tra sự đồng bộ con trỏ

Đoạn code dưới đây cho thấy foreach không bị ảnh hưởng bởi hàm next():

$numbers = [3, 4, 5, 6, 7];

foreach ($numbers as $index => $value) {
    var_dump($value);
    var_dump(next($numbers));
}

Kết quả: Các giá trị trong foreach không thay đổi dù ta đã gọi next().

Để thao tác phức tạp với con trỏ mảng, nên dùng vòng lặp while kết hợp key()current():

$data = [3, 4, 5, 6, 7];

while (key($data) !== null) {
    echo key($data) . " => " . current($data) . "\n";
    next($data);
}

Ứng dụng: Tính toán liên tiếp (cộng, trừ, nhân, chia) nhiều số

Khi cần thực hiện phép tính liên tiếp trên một mảng số (ví dụ: lấy phần tử đầu chia phần tử thứ hai, rồi kết quả chia tiếp phần tử thứ ba, ...), bạn có thể dùng vòng lặp với các hàm thao tác mảng như array_shift, array_unshiftreset.

Ví dụ cơ bản: Chia liên tiếp

$nums = [10, 20, 30, 40, 50, 60];
$resultArray = $nums;

for ($i = 1; $i <= (count($resultArray) - 1); $i++) {
    reset($nums);
    $a = current($nums);
    array_shift($nums);

    $b = current($nums);
    array_shift($nums);

    $res = bcdiv($a, $b, 2);
    array_unshift($nums, $res);
}

Phiên bản đóng gói (cải tiến)

Hàm multiple_parameters_bc cho phép tính toán liên tiếp nhiều số với các phép tính +, -, *, /, sử dụng bcmath để đảm bảo độ chính xác cao:

if (!function_exists('multiple_parameters_bc')) {
    function multiple_parameters_bc(string $type = '+', int $accuracy = 2, ...$parameters) {
        $calcPrecision = 30;
        if (count($parameters) < 2) {
            throw new \Exception('Cần ít nhất 2 tham số');
        }

        $temp = $parameters;
        for ($i = 1; $i <= (count($temp) - 1); $i++) {
            reset($parameters);
            $first = current($parameters);
            array_shift($parameters);
            $second = current($parameters);
            array_shift($parameters);

            switch ($type) {
                case '+':
                    $result = bcadd($first, $second, $calcPrecision);
                    break;
                case '-':
                    $result = bcsub($first, $second, $calcPrecision);
                    break;
                case '*':
                    $result = bcmul($first, $second, $calcPrecision);
                    break;
                case '/':
                    if ((int)$second === 0) {
                        throw new \Exception('Số chia không thể bằng 0');
                    }
                    $result = bcdiv($first, $second, $calcPrecision);
                    break;
                default:
                    throw new \Exception('Loại phép tính không hợp lệ');
            }
            array_unshift($parameters, $result);
        }

        $finalValue = $parameters[0];
        if ($type === '+' || $type === '-') {
            return number_format($finalValue, $accuracy, '.', '');
        } else {
            return round($finalValue, $accuracy);
        }
    }
}

Lưu ý khi sử dụng

  • Đối với phép chia, kết quả trung gian có thể bị sai số nếu làm tròn quá sớm. Nên đặt độ chính xác tính toán ($calcPrecision) cao hơn độ chính xác đầu ra.
  • Hàm multiple_parameters_bc tự động xử lý làm tròn: cộng/trừ chỉ cắt bớt số thập phân, nhân/chia làm tròn theo $accuracy.
  • Luôn kiểm tra số chia bằng 0 trước khi thực hiện phép chia.

Thẻ: php forEach array pointer bcmath array_shift

Đăng vào ngày 16 tháng 6 lúc 02:28