【问题标题】:How to store and reset a PHP array pointer?如何存储和重置 PHP 数组指针?
【发布时间】:2010-09-24 14:39:26
【问题描述】:

我有一个关联数组,即

$primes = array(
  2=>2,
  3=>3,
  5=>5,
  7=>7,
  11=>11,
  13=>13,
  17=>17,
  // ...etc
);

那我做

// seek to first prime greater than 10000
reset($primes);
while(next($primes) < 10000) {}
prev($primes);

// iterate until target found
while($p = next($primes)) {
      $res = doSomeCalculationsOn($p);

      if( IsPrime($res) )
          return $p;
}

问题在于 IsPrime 还循环遍历 $primes 数组,

function IsPrime($num) {
    global $primesto, $primes, $lastprime;

    if ($primesto >= $num)
        // using the assoc array lets me do this as a lookup
        return isset($primes[$num]);

    $root = (int) sqrt($num);
    if ($primesto < $root)
        CalcPrimesTo($root);

    foreach($primes as $p) {       // <- Danger, Will Robinson!
        if( $num % $p == 0 )
            return false;

        if ($p >= $root)
            break;
    }

    return true;
}

这会破坏我正在迭代的数组指针。

我希望能够在 IsPrime() 函数中保存和恢复数组的内部指针,这样它就不会产生这种副作用。有没有办法做到这一点?

【问题讨论】:

  • 危险+1,威尔罗宾逊!

标签: php arrays loops


【解决方案1】:

您可以“保存”数组的状态:

$state = key($array);

还有“恢复”(不确定是否有更好的方法):

reset($array);

while(key($array) != $state)
    next($array);

【讨论】:

  • Um... 正确的效果,但是这是数组大小的 O(n),这使得我的算法 O(n^2) 是不可接受的。我想知道扩展 reset($arr, $key=0) 有多难? (重置为开始还是指定的键值?)
  • 这个答案可能不是“最佳实践”,但它是问题的正确答案,... imo。
【解决方案2】:

不要依赖数组指针。请改用迭代器。

您可以将外部代码替换为:

foreach ($primes as $p) {
  if ($p > 10000 && IsPrime(doSomeCalculationsOn($p))) {
    return $p;
  }
}

【讨论】:

  • (smacking forehead) 好的,这行得通——我认为 foreach() 使用了数组自己的指针,但显然不是。它确实重置了数组内部指针,这是一种奇怪的副作用?
  • 我不确定 foreach 的内部实现是如何工作的,但它保持着自己的状态。我不记得曾经在 PHP 中使用数组指针遍历列表。
  • 顺便说一句。为您的值使用 assoc 数组有点奇怪。我会使用一个普通的列表,然后使用 in_array() 来检查一个值是否存在。
【解决方案3】:

如果速度不是问题并且您没有推动 php 内存限制,那么最快的解决方案就是复制您的素数数组并迭代 2 个不同的数组。

$awesomePrimes=$primes;

然后将函数中的 globals 和 foreach 更改为 $awesomePrimes

【讨论】:

  • 对于一个包含 200 万个项目的数组,它使用了额外的 68MB 内存——这不是世界末日,但似乎很浪费。
【解决方案4】:

如何再做一个int -&gt; int 的数组,其中索引是从0 到n 的流水号,值是关联数组的索引?所以,你会:

$pointer = array(
  0 => 2,
  1 => 3,
  2 => 5,
  // ...
);

而不是直接引用$prime,而是使用$prime[$pointer[$i]],或类似的东西?

【讨论】:

    【解决方案5】:

    为您的一次迭代使用“for”循环。例如在您的 IsPrime 方法中使用此循环:

    $primesLength = count($primes); // this is to avoid calling of count() so many times.
    for ($counter=0 ; $counter < $primesLength ; $counter++) {
        $p = $primesLength[$counter];
        if( $num % $p == 0 )
                return false;
    
        if ($p >= $root)
                break;
    }
    

    这样内部数组指针将不会在方法中使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-25
      • 2022-11-17
      • 2021-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-26
      • 1970-01-01
      相关资源
      最近更新 更多