【问题标题】:Lazy Function Definition in PHP - is it possible?PHP中的延迟函数定义 - 可能吗?
【发布时间】:2016-01-20 14:13:22
【问题描述】:

在 JavaScript 中,您可以使用 Lazy Function Definitions 优化对函数的第 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() 作为函数中的第一行来只执行一次外部代码,但我听说这些也很昂贵。

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

【问题讨论】:

    标签: php design-patterns optimization


    【解决方案1】:

    使用本地静态变量:

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 代码正在记忆中。 bool 测试(虽然可能更容易写成 if( !$call ) )决定是否执行慢代码,因此它只运行一次。
      • @Kent:阅读 scunliffe 的链接。他试图在第一次运行后重新定义该函数,以便跳过if($called) 测试。
      【解决方案3】:

      你可以做条件函数定义。

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

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

      你可以使用create_function,但我建议你不要,因为它很慢,占用大量内存,在 php 退出之前不会获得 free()'d,并且是安全的与 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 
      }
      

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

      【讨论】:

      • 这是一个复杂的、令人费解的解决方案,甚至还不能使用。本地静态变量是简单的解决方案。将其缓存在脚本之外也相对容易,具体取决于最初需要完成多少工作来处理它。
      【解决方案4】:

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

      【讨论】:

      • 不完全正确;尽管我能想到的唯一情况是非常具体的:如果您使用 DOMPDF 和它的
      【解决方案5】:

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

      $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();
      }
      

      试一试

      【讨论】:

      • 嗯,这看起来可能可行,绝对值得一试,谢谢。
      • 但它仍然是一个函数调用,它本身可能相当昂贵。
      【解决方案6】:

      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 以获取 detailed description of this feature

      【讨论】:

        【解决方案7】:

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

        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
        }
        

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

        【讨论】:

          【解决方案8】:

          您有什么理由致力于功能样式模式?尽管有匿名函数和关闭计划,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
              }
          } 
          

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

          【讨论】:

          • PHP 真的不是一种功能语言。 FWIW,类对于php来说是一个相对“新”的东西。在 php5 之前,它们几乎都不是类。类也有开销,并且您的代码在逻辑上与原始代码相同。
          • 是的,没错,但是 PHP 在过去两三年中的发展方向是面向 OOP 的经典概念,而 PHP 中的功能抽象有其自身的开销水平。只是另一个需要考虑的选择。
          猜你喜欢
          • 1970-01-01
          • 2013-02-07
          • 2017-08-07
          • 2011-11-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-04-09
          相关资源
          最近更新 更多