PHP中的懒函数定义 - 可能吗?

时间:2008-09-23 01:49:19

标签: php design-patterns optimization

在JavaScript中,您可以使用Lazy Function Definitions来优化对函数的第2个N次调用,方法是仅在第一次调用函数时执行昂贵的一次性操作。

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

实际上我想做的就像下面一样,只是优化了所以第二次 - 第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()作为函数的第一行来执行外部代码一次,但我听说这些也很昂贵。

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

8 个答案:

答案 0 :(得分:13)

使用本地静态var:

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

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

答案 1 :(得分:6)

您是否真的对此代码进行了分析?我怀疑额外的布尔测试会对页面渲染时间产生任何可测量的影响。

答案 2 :(得分:4)

你可以做条件功能定义。

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

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

你可以使用create_function,但我会建议你不要,因为它很慢,使用大量内存,没有得到免费()'直到php退出,并且是一个像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 
}

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

答案 3 :(得分:1)

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

答案 4 :(得分: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();
}

试一试

答案 5 :(得分: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
    }
} 

如果你想获得幻想,你可以使用魔术方法来避免必须预定义被叫的布尔值。如果您不想实例化对象,请转到静态。

答案 6 :(得分: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可能会获得词法范围/闭包支持,因此当出现这种情况时,您可以编写更流畅的函数编程风格。有关detailed description of this feature的信息,请参阅PHP rfc-wiki。

答案 7 :(得分: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
}

本地静态变量在函数调用中是持久的。他们记得回归后的价值。