Tìm hiểu bản chất vòng lặp foreach trong php
Nhiều lập trình viên mới vào nghề hay thậm chí cả những lập trình viên chuyên nghiệp đều biết cách sử dụng vòng lặp foreach nhưng không thực sự hiểu sâu về nó, vì vậy họ rất bối rối khi gặp những câu hỏi hoc búa mà các nhà tuyển dụng phỏng vấn. Vì thế trong bài này tôi và các bạn lập trình viên sẽ cùng nhau thảo luận vấn đề này nhé. Nếu có gì sai các bạn góp ý để cùng nhau hiểu và nắm vững các cách sử dụng vòng lặp foreach trong php.
Như các bạn đã biết ở mỗi vòng lặp foreach php sẽ tạo ra các bản sao để chúng ta lấy dữ liệu của phần tử đang lặp đó, bản sao này sẽ được loại bỏ ngay lập tức sau khi kết thúc vòng lặp. Và ở mỗi vòng lặp sẽ kiểm tra còn phần tử liền kề hay không? nếu còn thì vòng lặp được tiếp tục, ngược lại thì sẽ dừng. Ví dụ dưới đây là một cách sử dụng vòng lặp foreach trong php rất thông dụng:
$mang = array('Trường', 'Sa', 'Hoàng', 'Sa', 'Là', 'Của', 'Việt', 'Nam'); foreach ( $mang AS $item ) { echo $item.'<br/>'; }
Chạy lên kết quả sẽ là:
Bài viết này được đăng tại [free tuts .net]
Trường Sa Hoàng Sa Là Của Việt Nam
Các bạn thấy mặc dù các bản sao của biến $mang
được tạo ra nhưng các nhà phát triển không nhận thấy, tại vì sau khi thực thi vòng lặp các bạn thử xem giá trị mảng hoàn toàn không bị thay đổi, các bạn xem lại ví dụ dưới đây:
$mang = array('Trường', 'Sa', 'Hoàng', 'Sa', 'Là', 'Của', 'Việt', 'Nam'); foreach ( $mang AS $item ) { $item = strtoupper($item); } echo '<pre>'; print_r($mang);
Kết quả sẽ là:
Array ( [0] => Trường [1] => Sa [2] => Hoàng [3] => Sa [4] => Là [5] => Của [6] => Việt [7] => Nam )
Các bạn thấy, trong vòng lặp mình đã biến đổi giá trị của các phần tử sang chữ hoa, nhưng kết thúc vòng lặp mình in ra giá trị của mảng thì nó không hề thay đổi.
Vậy có cách nào để thay đổi giá trị của phẩn tử đang lặp thông qua biến copy $item
? Nếu các bạn từng học C hay C++ thì các bạn sẽ biết đến con trỏ, con trỏ sẽ tham chiếu đến vị trí trong ô nhớ của biến nào đó. Nhưng trong PHP thì khái niệm con trỏ không có mà chúng ta sử dụng tham chiếu cũng y như con trỏ. Vì thế để thay đổi giá trị của phần tử hiện tại ta sẽ sử dụng tham chiếu để thay đổi địa chỉ trong ô nhớ của phần tử trong mảng, như vậy trình biên dịch sẽ tự hiểu chạy vào mảng gốc chứ không copy mảng mới. Các bạn xem ví dụ:
$mang = array('Trường', 'Sa', 'Hoàng', 'Sa', 'Là', 'Của', 'Việt', 'Nam'); foreach ( $mang AS &$item ) { $item = strtoupper($item); } echo '<pre>'; print_r($mang);
Kết quả:
Array ( [0] => TRườNG [1] => SA [2] => HOàNG [3] => SA [4] => Là [5] => CủA [6] => VIệT [7] => NAM )
Các bạn thấy rõ ràng giá trị các phần tử của mảng đã bị thay đổi sang chữ in hoa sau khi vòng lặp foreach kết thúc. Như vậy ta có kết luận rằng khi bắt đầu vòng lặp foreach, PHP sẽ tạo ra một bảng sao của mảng để lặp nên khi chúng ta thay đổi hoàn toàn không ảnh hưởng, nhưng nếu chúng ta sử dụng tham chiếu để truy xuất đến mảng gốc thì sẽ không có vụ tạo mảng copy nên mọi thứ đều gọi tới mảng gốc, từ đó ta có thể thay đổi giá trị cho mảng gốc. Các bạn xem tiếp ví dụ dưới đây:
$mang = array('Trường', 'Sa', 'Hoàng', 'Sa', 'Là', 'Của', 'Việt', 'Nam'); foreach ( $mang AS $item ) { $mang[] = $item; } echo '<pre>'; print_r($mang);
Kết quả:
Trường Sa Hoàng Sa Là Của Việt Nam
Array ( [0] => Trường [1] => Sa [2] => Hoàng [3] => Sa [4] => Là [5] => Của [6] => Việt [7] => Nam [8] => Trường [9] => Sa [10] => Hoàng [11] => Sa [12] => Là [13] => Của [14] => Việt [15] => Nam )
Các bạn thấy trong mỗi vòng lặp ta đã gán thêm một phẩn tử vào mảng thì đáng lẽ ra vòng lặp sẽ dẫn đến lặp vô hạn. Nhưng không, tại vì trong vòng lặp đang lặp mảng copy của mảng gốc, vì thế khi ta thay đổi mảng gốc thì không ảnh hưởng gì, và sau khi vòng lặp kết thúc mảng copy sẽ được xóa và mảng gốc đã bị thay đổi.
Bây giờ giả sử tôi sử dụng tham chiếu các phần tử trong mỗi lần lặp xem thế nào?
$mang = array('Trường', 'Sa', 'Hoàng', 'Sa', 'Là', 'Của', 'Việt', 'Nam'); foreach ( $mang AS &$item ) { $mang[] = $item; echo $item . ' '; } echo '<pre>'; print_r($mang);
Chạy lên các bạn sẽ thấy rõ ràng vòng lặp foreach bị lặp vô hạn. Tại sao vậy? Tại vì mảng copy KHÔNG được tạo ra, vì vậy nó sẽ sử dụng đến mảng gốc và sau mỗi vòng lặp mảng gốc sẽ thêm một phần tử, ở cuối mỗi vòng lặp foreach sẽ kiểm tra còn phần tử liền kề hay không để tiếp tục lặp, rõ ràng ta đang truy xuất đến mảng gốc nên phần tử liền kề luôn luôn tồn tại. Nhưng giả sử mảng chỉ có một phần tử thì sao? Các bạn xem ví dụ:
$mang = array('Trường'); foreach ( $mang AS $key => &$item ) { $mang[] = $item; echo($item); }
Chạy lên thì nó lại không lặp vô hạn. Tại vì theo như trên tôi trình bày trình biên dịch sẽ kiểm tra còn phần tử kế tiếp hay không, lúc này chỉ có 1 phần tử nên nó chạy luôn và sẽ ngưng. Tuy nhiên bạn vẫn có thể thay đổi giá trị cho mảng đó nhé vì ta đang tham chiếu mà.
Nhưng mọi chuyện không phải đơn giản như vậy, ta xét ví dụ tiếp theo sau đây:
$mang = array('Trường', 'Sa'); $a = &$mang; foreach ( $mang AS $item ) { $mang[] = $item; echo($item); }
Chạy lên vòng lặp foreach vẫn bị lặp vô hạn. Như vậy nếu mảng bị tham chiếu ở ngoài vòng lặp thì bản thân nó vẫn bị lặp vô hạn. Trường hợp này tôi không thể giải thích một cách chính xác, nhưng tôi nghĩ lý do là do lúc ta tham chiếu thế này thì ta đã có 2 biến tham chiếu đến vị trí ô nhớ đó nên trình biên dịch sẽ không copy mà chạy thẳng vào mảng gốc. Vì thế dẫn đến vô hạn.
Xét ví dụ tiếp theo:
$mang = array('Trường', 'Sa', 'Hoàng', 'Sa', 'Là', 'Của', 'Việt', 'Nam'); $a = &$mang; foreach ( $mang AS $item ) { $item = strtoupper($item); } echo '<pre>'; print_r($mang);
kết quả sẽ là:
Array ( [0] => Trường [1] => Sa [2] => Hoàng [3] => Sa [4] => Là [5] => Của [6] => Việt [7] => Nam )
Các bạn thấy trong vòng lặp tôi đã thay đổi giá trị của các phần tử sang in hoa nhưng kết thúc vòng lặp mảng lại không bị ảnh hưởng.
Lời Kết:
Trong bài này tôi chỉ đưa ra những vấn đề về vòng lặp foreach với hy vọng nhận được các ý kiến đóng góp của các lập trình viên chuyên nghiệp để bài viết được hoàn hảo hơn. Lời cuối cùng xin cám ơn các bạn đã dành thời gian đọc bài này.
P/s: Mình có một serie php căn bản bạn có hứng thú thì vào đọc bài nhé.