2008-09-23 12 views
5

En JavaScript, puede usar Lazy Function Definitions para optimizar la segunda y la enésima llamada a una función realizando operaciones costosas de un solo uso solo en la primera llamada a la función.Definición de función diferida en PHP: ¿es posible?

Me gustaría hacer el mismo tipo de cosas en PHP 5, pero la redefinición de una función no está permitida, ni está sobrecargando una función.

Efectivamente, lo que me gustaría hacer es como lo siguiente, solo optimizado, por lo que las llamadas 2ª - Nth (por ejemplo, 25-100) no necesitan volver a verificar si son la primera llamada.

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

PS he pensado en utilizar un include_once() o require() como la primera línea en la función para ejecutar el código externo sólo una vez, pero he oído que estos también son caros.

alguna idea? o hay una mejor manera de abordar esto?

Respuesta

13

Use un var estática local:

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

Evitar el uso de un mundial para esto. Agrupa el espacio de nombres global y hace que la función esté menos encapsulada. Si otros lugares además de las entrañas de la función necesitan saber si se ha llamado, entonces valdría la pena colocar esta función dentro de una clase como lo indicó Alan Storm.

6

¿Realmente ha perfilado este código? Dudo que una prueba booleana adicional tenga un impacto medible en el tiempo de renderizado de la página.

+0

El código se memoizing. la prueba bool (aunque probablemente sea más fácil escribir como si (! $ llamado)) decide si se ejecuta o no el código lento, por lo que solo se ejecuta una vez. –

+1

@Kent: lee el enlace de scunliffe. Está tratando de redefinir la función después de la primera vez que se ejecuta para que pueda omitir la prueba 'if ($ called)'. –

4

puede hacer condicional definición de función.

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

Pero en la actualidad, una función se convierte en un ladrillo cuando se define.

Puede utilizar create_function, pero lo que sugiere NO porque es lento, utiliza mucha memoria, no liberarse() 'd salidas php untill, y es un agujero de seguridad tan grande como eval ()

Espera a PHP5.3, en el que tenemos "cierres" http://wiki.php.net/rfc/closures

continuación, se le permitirá hacer

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

$baz('hello'); 

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

Tras la lectura adicional, este es el efecto que desea.

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

Hará lo que quiera, pero la llamada podría ser un poco más costosa que una llamada de función normal.

En PHP5.3 Esto debería ser válido también:

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

(personalmente, creo que por supuesto que todos estos truquitos van a ser epically más lento que su comparación anterior de 2 bits positivos;).)

Si usted está realmente preocupado por conseguir la velocidad mejor todas partes

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

es posible que no abl Sin embargo, para hacer eso, no has dado suficiente alcance para aclarar.Sin embargo, si puede hacer eso, entonces, las respuestas simples suelen ser las mejores :)

+0

Es una solución complicada y complicada que aún no está disponible para su uso. Una var estática local es la solución simple. También es relativamente fácil guardarlo en caché fuera de la secuencia de comandos, dependiendo de cuánto trabajo haya que hacer para procesarlo inicialmente. –

0

Si termina encontrando que una prueba booleana extra va a ser muy costosa, puede establecer una variable con el nombre de un función y llamarla:

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

Dale que un tiro

+0

Hmm, parece que podría funcionar, definitivamente uno para darle un perfil, gracias. – scunliffe

+0

Pero sigue siendo una llamada a la función, que probablemente sea bastante costosa. –

0

algún motivo que está comprometida con un patrón de estilo funcional? A pesar de tener funciones anónimas y planes para el cierre, PHP realmente no es un lenguaje funcional. Parece que una clase y un objeto serían la mejor solución aquí.

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 
    } 
} 

Si quieres ser elegante puedes utilizar los métodos mágicos para evitar tener que predefinir los booleanos llamados. Si no quiere crear una instancia de un objeto, vaya estático.

+0

PHP realmente no es un lenguaje funcional. FWIW, las clases son algo relativamente "nuevo" para php. Hasta php5, apenas eran clases. Las clases también tienen gastos generales, y tu código es lógicamente idéntico al original. –

+0

Sí, todo es cierto, pero la dirección hacia la que se dirigió PHP en los últimos dos o tres años es hacia un concepto clásico de OOP, y las abstracciones funcionales en PHP vienen con su propio nivel de sobrecarga. Solo otra opción a considerar. –

1

No utilice include() o include_once(), a menos que no le importe si el include() falla. Si incluyes el código, entonces te importa. Siempre use require_once().

+0

No del todo cierto; aunque el único caso en el que puedo pensar es bastante específico: si estás usando DOMPDF con su

0

¿Cómo sobre el uso de variables estáticas locales?

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

Si lo que necesita hacer cosas caras sólo una vez por valor de parámetro dado, podría utilizar un buffer de matriz:

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

variables estáticas locales son persistentes entre llamadas a funciones. Recuerdan el valor después del regreso.

Cuestiones relacionadas