5

在 JavaScript 中,您可以使用延迟函数定义来优化对函数的第 2 次 - 第 N 次调用,方法是仅在第一次调用函数时执行昂贵的一次性操作。

我想在 PHP 5 中做同样的事情,但是不允许重新定义函数,也不允许重载函数。

实际上,我想要做的是如下所示,仅进行了优化,因此第 2 次 - 第 N 次调用(比如 25-100)不需要重新检查它们是否是第一次调用。

$called = false;
function foo($param_1){
  global $called;
  if($called == false){
    doExpensiveStuff($param_1);
    $called = true;
  }
  echo '<b>'.$param_1.'</b>';
}

PS 我曾考虑使用 include_once() 或 require_once() 作为函数中的第一行来只执行一次外部代码,但我听说这些也很昂贵。

有任何想法吗?还是有更好的方法来解决这个问题?

4

8 回答 8

13

使用本地静态变量:

function foo() {
    static $called = false;
    if ($called == false) {
        $called = true;
        expensive_stuff();
    }
}

避免为此使用全局变量。它使全局命名空间变得混乱,并使函数的封装更少。如果函数内部以外的其他地方需要知道它是否被调用,那么将这个函数放在像 Alan Storm 所指出的类中是值得的。

于 2008-09-23T05:06:32.993 回答
6

你真的分析过这段代码吗?我怀疑额外的布尔测试是否会对页面呈现时间产生任何可衡量的影响。

于 2008-09-23T01:55:27.290 回答
4

你可以做条件函数定义。

if( !function_exists('baz') )
{ 
    function baz( $args ){ 
        echo $args; 
    }
}

但是目前,一个函数在定义时就变成了一块砖。

你可以使用create_function,但我建议你不要,因为它很慢,使用大量内存,在 php 退出之前不会获得 free(),并且是一个与 eval() 一样大的安全漏洞。

等到 PHP5.3,我们有“闭包” http://wiki.php.net/rfc/closures

然后你将被允许做

if( !isset( $baz ) ) 
 { 
    $baz = function( $args )
    { 
        echo $args;
    }
}

$baz('hello');

$baz = function( $args )
{ 
       echo $args + "world"; 
}
$baz('hello');

进一步阅读,这就是你想要的效果。

$fname = 'f_first'; 
function f_first( $even ) 
{ 
    global $fname; 
    doExpensiveStuff(); 
    $fname = 'f_others';
    $fname( $even );
    /* code */ 
}
function f_others( $odd ) 
{
     print "<b>".$odd."</b>";
}

foreach( $blah as $i=>$v ) 
{
   $fname($v);
}

它会做你想做的事,但调用可能比普通函数调用贵一点。

在 PHP5.3 这也应该是有效的:

$func = function( $x ) use ( $func ) 
{ 
     doexpensive(); 
     $func = function( $y )
     { 
          print "<b>".$y."</b>";
     }
     $func($x);
}
foreach( range(1..200) as $i=>$v ) 
{ 
    $func( $v ); 
}

(就个人而言,我当然认为所有这些巧妙的技巧都会比你之前对 2 个正位的比较慢得多。;))

如果您真的担心在任何地方都能获得最佳速度

$data = // some array structure
doslowthing(); 
foreach( $data as $i => $v ) 
{
   // code here 
}

但是,您可能无法做到这一点,但是您没有给出足够的范围来澄清。但是,如果您能做到这一点,那么简单的答案通常是最好的:)

于 2008-09-23T01:55:36.523 回答
1

请不要使用include()or include_once(),除非您不在乎是否include()失败。如果您包含代码,那么您在乎。始终使用require_once().

于 2008-09-23T02:27:33.373 回答
0

如果您最终发现额外的布尔测试太昂贵了,您可以将变量设置为函数的名称并调用它:

$func = "foo";    

function foo()
{
    global $func;
    $func = "bar";
    echo "expensive stuff";
};


function bar()
{
    echo "do nothing, i guess";
};

for($i=0; $i<5; $i++)
{
    $func();
}

试一试

于 2008-09-23T02:07:12.847 回答
0

您有什么理由致力于功能样式模式?尽管有匿名函数和关闭计划,PHP 确实不是一种函数式语言。在这里,类和对象似乎是更好的解决方案。

Class SomeClass{
    protected $whatever_called;
    function __construct(){
        $this->called = false;
    }
    public function whatever(){
        if(!$this->whatever_called){
            //expensive stuff
            $this->whatever_called = true;
        }
        //rest of the function
    }
} 

如果您想变得花哨,可以使用魔术方法来避免必须预定义被调用的布尔值。如果您不想实例化对象,请使用静态。

于 2008-09-23T02:12:02.040 回答
0

PHP 没有词法作用域,所以你不能用函数做你想做的事。但是,PHP 有类,它们在概念上以完全相同的方式工作。

在 javascript 中,你会这样做:

var cache = null;
function doStuff() {
  if (cache == null) {
    cache = doExpensiveStuff();
  }
  return cache;
}

使用类(在 PHP 中),您可以:

class StuffDoer {
  function doStuff() {
    if ($this->cache == null) {
      $this->cache = $this->doExpensiveStuff();
    }
    return $this->cache;
  }
}

是的,基于类的 oop 比函数式编程更冗长,但在性能方面它们应该差不多。

除此之外,PHP 5.3 可能会获得词法作用域/闭包支持,因此当它出现时,您可以用更流畅的函数式编程风格编写代码。有关此功能的详细说明,请参阅 PHP rfc-wiki 。

于 2008-09-23T08:47:45.927 回答
0

使用局部静态变量怎么样?

function doStuff($param1) {
    static $called = false;
    if (!$called) {
        doExpensiveStuff($param1);
        $called = true;
    }
    // do the rest
}

如果您只需要为给定的参数值做一次昂贵的事情,您可以使用数组缓冲区:

function doStuff($param1) {
    static $buffer = array();
    if (!array_key_exists($param1, $buffer)) {
        doExpensiveStuff($param1);
        $buffer[$param1] = true;
    }
    // do the rest
}

局部静态变量在函数调用中是持久的。他们记住返回后的值。

于 2008-09-23T09:29:18.650 回答