在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()作为函数的第一行来执行外部代码一次,但我听说这些也很昂贵。
任何想法?还是有更好的方法来解决这个问题?
答案 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
}
本地静态变量在函数调用中是持久的。他们记得回归后的价值。