Christian Seiler: for vs. foreach

Beitrag lesen

Hallo,

bei einfachen Zählern nutze ich ab und zu die Methode

foreach(range(0,5) as $i)

Bei kleinen Zahlen ist das egal, aber bei großen Zahlen wird das problematisch, weil Du damit ein riesiges Array anlegst, was ewig viel Speicher frisst; das gleiche Problem hast Du auch in Python, wenn Du »for i in range(...)« machst. In Python gibt's xrange() als Iterator-basierte Implementierung, die den Speicher schont. In PHP kannst Du Dir eine Iterator-basierte Implementierung selbst basteln:

<?php  
class XRange implements Iterator {  
  private $min;  
  private $max;  
  private $current;  
  private $counter;  
  private $step;  
  
  public function __construct ($min, $max, $step = 1) {  
    $this->min = (int)$min;  
    $this->max = (int)$max;  
    $this->step = (int)$step;  
    $this->current = $this->min;  
    $this->counter = 0;  
  }  
  
  public function rewind () {  
    $this->current = $this->min;  
    $this->counter = 0;  
  }  
  
  public function current () {  
    return $this->current;  
  }  
  
  public function key () {  
    return $this->counter;  
  }  
  
  public function next () {  
    $this->current += $this->step;  
    return $this->valid ();  
  }  
  
  public function valid () {  
    return ($this->step > 0 && $this->current <= $this->max  
      || $this->step < 0 && $this->current >= $this->max);  
  }  
}  
  
function xrange ($min, $max, $step = 1) {  
  return new XRange ($min, $max, $step);  
}  
?>

Dann kannst Du bspw. auch sowas machen wie:

foreach (xrange (0, 1000000) as $i) {  
  if ($i % 100000 == 0) echo "$i\n";  
}

Ohne, dass Dir das Ding gleich Megabyteweise den Speicher wegfrisst. Dafür ist es *deutlich* langsamer. Mal ein paar Vergleiche:

Code 1:
-------

foreach (xrange (0, 1000000) as $i) {  
  if ($i % 100000 == 0) echo "$i\n";  
}

Code 2:
-------

foreach (range (0, 1000000) as $i) {  
  if ($i % 100000 == 0) echo "$i\n";  
}

Code 3:
-------

for ($i = 0; $i <= 1000000; $i++) {  
  if ($i % 100000 == 0) echo "$i\n";  
}

Variante    | Ausführzeit (s)    | Peak-Memory-Usage (Bytes)
------------+--------------------+----------------------------
1 (xrange)  | 17.38              | 93560
2 (range)   |  1.30              | 104291308 (fast 100 MiB!)
3 (for)     |  0.88              | 93176

Interessant wäre höchstens noch, wie sehr die Ausführzeit zurückginge, wenn man xrange() direkt als C-Erweiterung implementiert. Mache ich nachher vielleicht noch, dann liefere ich das Ergebnis noch nach.

Viele Grüße,
Christian