2011-05-03 52 views
1

我寫了下面的函數用於平陣列:當迭代的數組/集合在循環內部被修改時,foreach控制結構是如何工作的?

function flatten() { 
    $args = func_get_args(); 
    $items = array(); 

    for ($i = 0; $i < count($args); $i++) { // <-- (*) 
     $arg =& $args[$i]; 

     if (is_array($arg)) 
      foreach ($arg as &$item) 
       $args[] =& $item; 
     else 
      $items[] = $arg; 
    } 

    return $items; 
} 

我想用簡單的foreach ($args as &$arg)更換for線。基於什麼?我曾經寫過一個實現Iterator接口的類,它基本上是控制結構如何工作的基礎。如果正確地記得什麼foreach控制結構做如下:

  1. 使用rewind()方法將一個內部索引變量設置爲所述第一元件的位置。
  2. 使用valid()方法測試是否該陣列的端部已經達到。如果是這樣,退出。
  3. 使用`Iterator接口的key()current()方法來檢索數組的當前元素的鍵和值。
  4. 使用next()方法的當前一個之後立即設置內部變量索引的元素的位置。
  5. 轉到2.

至少,這是它與用戶定義的類的作品。我不太確定如何使用內置數組類型。它會以同樣的方式工作嗎?我可以使用foreach替換for行嗎?

回答

2

首先,我要指出的是,在一般情況下,修改集合的內容在遍歷它被認爲是「壞事」 - 如果你嘗試了很多語言會拋出異常。但是PHP不是這些語言之一。

有兩件事是與此有關的提問:

首先,使用數組時,PHP的的foreach使得陣列和迭代該副本。在這種情況下,您可以安全地修改原始數組,但是您的foreach將不會看到任何這些更改。在你的情況下,這是行不通的 - 你粘貼到$ args結尾的新值不會出現在foreach中。

您可以強制PHP通過迭代對數組的參考使用原來的陣列。在這種情況下,內部行爲將變得相關。PHP保留一個指向「下一個」數組元素的內部指針。如果您更改「foreach」已經看到的數組元素的內容,則不會看到更改。如果您在當前元素之外的某個位置更改數組的內容,則會看到這些更改。這應該爲你工作,但我不知道如果我信任它。

+0

這是一個很棒的答案。如果可以的話,我會加倍努力。如果在迭代它們之前我已經知道PHP **拷貝**數組,我將總是*遍歷數組的引用。我每天都討厭PHP多一點。 – pyon 2011-05-03 20:27:49

+0

'is_array'和'array_merge'函數是否也通過值而不是通過引用接受數組? – pyon 2011-05-03 20:31:55

+0

據我瞭解,PHP引用是同一事物的別名,所以引用應該與原始文件完全相同。引用一個對象只是強制PHP對原始文件執行操作,因爲所有的變化都需要一次對所有的引用都可見。 – 2011-05-03 20:46:35

1

for和foreach是可以互換的,但在這種情況下,不能將元素添加到args數組並使用foreach立即處理它們,所以如果使用foreach,你的函數將不會以相同的方式運行。

有幾件事要指出代碼: 1)將count($ args)放入for循環的第2個參數中意味着它在循環中每次迭代都會被處理,如果您有一個非常大的數組很貴。

我會在處理循環之前計算args的數量,將它存儲在一個變量中並在for參數中使用它來代替count($ args),然後每次添加一個新元素時將count加1到args數組。這會更快,並使用更少的內存。

2)這可以被清理過的功能使用遞歸,這將做同樣的事情,而不多重循環,並會使用略少的代碼。

+0

循環比遞歸更有效。 – pyon 2011-05-03 20:33:01

+0

然而,對於多維數組而言,情況當然是這樣,遞歸會更加理想,因爲它提供了輕鬆遍歷數組的每個維度的能力。儘管如果做得正確,仍然可以在循環內完成,但作爲個人偏好,我會用遞歸來進行。 即使腳本與OOP相比,腳本編寫更加高效,並且有很多額外的工作,可以完成OOP所做的一切,但OOP的好處大大超過了任何性能,OOP更加清潔和優雅。 – 2011-05-10 23:51:13

1
$nums = array(1, 2, 3, 4); 

$newNum = max($nums) + 1; 
foreach ($nums as $num) { 
    echo $num; 
    if ($newNum > 10) { 
     break; 
    } 
    $nums[] = $newNum++; 
} 

print_r($nums); 

/* output 
1234 

Array 
(
    [0] => 1 
    [1] => 2 
    [2] => 3 
    [3] => 4 
    [4] => 5 
    [5] => 6 
    [6] => 7 
    [7] => 8 
) 
*/ 

使用參考foreach ($nums as &$num)

/* output 
1234567 

Array 
(
    [0] => 1 
    [1] => 2 
    [2] => 3 
    [3] => 4 
    [4] => 5 
    [5] => 6 
    [6] => 7 
    [7] => 8 
    [8] => 9 
    [9] => 10 
) 
*/ 
相關問題