2011-12-21 10 views
6

Desde hace un tiempo, he estado usando una función recursiva "tradicional" para aplanar matrices multidimensionales comoel paso por referencia no se trabaja con parámetros adicionales para array_walk_recursive, a menos que sea el desuso de llamadas en tiempo pasan por referencia

$baseArray = array(array('alpha'), 
        array('beta','gamma'), 
        array(), 
        array(array('delta','epsilon'), 
         array('zeta',array('eta', 
              'theta' 
              ), 
          ), 
         ), 
        array('iota'), 
       ); 

a una simple matriz de 1-d.

Ayer por la noche, pensé que me gustaría echar un vistazo a array_walk_recursive() usando para ver si podía hacerlo más eficiente y más limpio.

Mi primer intento no tuvo mucho éxito:

function flattenArray($arrayValue, $arrayKey, &$flatArray) { 
    $flatArray[] = $arrayValue; 
} 


$flattenedArray = array(); 
array_walk_recursive($baseArray,'flattenArray',$flattenedArray); 

pensé que debería funcionar, pero todo lo que tengo una serie de errores:

Warning: Cannot use a scalar value as an array in C:\xampp\htdocs\arrayTest.php on line 16 

y un resultado de:

array(0) { } 

mecanografía de tipo en mi función flattenArray() me dio

Catchable fatal error: Argument 3 passed to flattenArray() must be an array, integer given in C:\xampp\htdocs\arrayTest.php on line 16 

Usando un cierre dio resultados idénticos

La única manera de que pudiera hacerlo funcionar (sin recurrir al uso de un mundial o una estática para mi flattenedArray) fue el uso de llamadas en tiempo pasar por referencia:

function flattenArray($arrayValue, $arrayKey, $flatArray) { 
    $flatArray[] = $arrayValue; 
} 


$flattenedArray = array(); 
array_walk_recursive($baseArray,'flattenArray',&$flattenedArray); 

que produce el resultado correcto

array(9) { [0]=> string(5) "alpha" [1]=> string(4) "beta" [2]=> string(5) "gamma" [3]=> string(5) "delta" [4]=> string(7) "epsilon" [5]=> string(4) "zeta" [6]=> string(3) "eta" [7]=> string(5) "theta" [8]=> string(4) "iota" } 

pero me da una advertencia de no-inesperada

Deprecated: Call-time pass-by-reference has been deprecated in C:\xampp\htdocs\arrayTest.php on line 22 

Sé que PHP es un lenguaje peculiar, pero parece un poco extremo. La documentación muestra claramente que el primer parámetro para array_walk_recursive es pass-by-reference, pero parece que los argumentos adicionales solo pueden pasarse por referencia en el tiempo de llamada. ¡Extraño!

versión de PHP 5.3.8 es

Cualquier sugerencia en cuanto a cómo puedo usar array_walk_recursive para aplanar mi matriz correctamente, sin obtener errores en desuso (además de la presentación de un informe de error).

EDITAR

P. S.

Soy consciente de que puedo pasar por alto este problema usando un cierre:

$flattenedArray = array(); 
array_walk_recursive($baseArray, function($arrayValue, $arrayKey) use(&$flattenedArray){ $flattenedArray[] = $arrayValue; }); 
var_dump($flattenedArray); 

pero como este es necesario para una biblioteca que actualmente permite el uso con PHP 5.2.0, no es una opción práctica para utilizar una función que requiere una versión bastante más tarde de PHP

Respuesta

6

pensar de esta manera: Se pasa $flatArray en array_walk_recursivepor valor. array_walk_recursive luego pasará el argumento por referencia a su función. Pero como se pasó a array_walk_recursive por valor, la referencia ya indicará un valor diferente.

Lo sé, esto podría parecer una limitación extraña, pero cuando lo piensas, es bastante lógico también.

Por cierto, creo que accidentalmente también encontraste otro problema con esto, en realidad parece una corrupción de memoria grave (mira los terceros elementos de la matriz impresa @http://codepad.viper-7.com/ZYNrNd). Voy a mirar en esto.

En una nota lateral, una forma fácil de aplanar está utilizando un RecursiveArrayIterator: versión específica

$flattenedArray = array(); 
foreach (new RecursiveIteratorIterator(
      new RecursiveArrayIterator($baseArray), 
      RecursiveIteratorIterator::LEAVES_ONLY 
     ) as $value) { 
    $flattenedArray[] = $value; 
} 
+0

Puedo ver la lógica, aunque no estoy seguro de estar de acuerdo ... el argumento arrayValue se pasa a la función de devolución de llamada por referencia, lo que permite cambiar los valores de la matriz - cambiador de funciones (& $ arrayValue, $ arrayKey) { \t $ arrayValue. = 'TEST'; } array_walk_recursive ($ baseArray, 'changer'); var_dump ($ baseArray); –

+0

El problema de la corrupción de la memoria me parece potencialmente grave ... no había visto ninguna evidencia de ello hasta que lo destacó –

+0

Ciertamente creo que debería haber una nota formal que indique que no se puede usar la llamada por referencia con argumentos adicionales en la función de devolución de llamada como call_user_func(), no solo cualquier comentario que pueda agregar –

1
No

particularmente útil en esta etapa.

Lectura a través de la documentación de PHP, he encontrado que call_user_func() tiene una nota contra su descripción de los argumentos de los parámetros:

Tenga en cuenta que los parámetros para call_user_func() no se pasan por referencia.

pero array_walk_recursive() no tiene tal aviso.

Esto puede ser puramente un problema de documentación ... si hubiera visto un aviso similar en la documentación de array_walk_recursive(), probablemente no lo hubiera intentado (aunque los curiosos podrían haber intentado independientemente, solo para ver qué sucedió).

Sin embargo, no entiendo por qué ninguno de los casos debe aceptar los argumentos de paso por referencia en la definición de la función de devolución de llamada. Esto se siente un poco como un paso atrás ... una función de idioma que sí funcionaba (aunque usando la referencia de paso a paso de la llamada) ya no lo hace, sin depender de esa característica obsoleta.

0

Pasar por referencia al tiempo de la llamada está en desuso:

http://uk3.php.net/manual/en/ini.core.php#ini.allow-call-time-pass-reference

Esto no se observó en la documentación array_walk_recursive porque no es específico para esa función.

Una cosa que podría hacer es pasar un nombre de objeto y método, en lugar de un nombre de función, como la devolución de llamada, y mantener el estado de la matriz aplanada como miembro de ese objeto.

p. Ej.

class ArrayFlattener 
{ 
    //implementation and array state 
} 

$flattener = new ArrayFlattener(); 
array_walk_recursive($baseArray, array($flattener, 'flatten')); 

print_r($flattener->getFlattenedArray()); 
+1

aprecio __call-time__ pasar por referencia está en desuso, pero estaba tratando un pase por referencia recta que no está en desuso, pero no parece funcionar cuando se utiliza para __additional__ argumentos de una función de devolución de llamada. –

+0

Es posible que tenga que usar un atributo de clase como método alternativo ... esto se usará dentro de una clase una vez que pueda hacerlo funcionar ... pero tendré que asegurarme de inicializar el atributo cada vez que use el aplanar método, que es una sobrecarga adicional –

1

Debido a que sus soluciones son (según la documentación, PHP 5.2 no debería quejarse de llamadas en tiempo pass-by-reference), una opción es crear diferentes scripts que contengan cada implementación, y luego tener otra secuencia de comandos condicionalmente incluir la secuencia de comandos que define la función de aplanamiento (y cualquier otro código específico de la versión) dependiendo de la versión de PHP.

if (! defined('PHP_VERSION_ID')) { 
    $version = explode('.', PHP_VERSION); 
    define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2])); 
    if (PHP_VERSION_ID < 50207) { 
     define('PHP_MAJOR_VERSION', $version[0]); 
     define('PHP_MINOR_VERSION', $version[1]); 
     define('PHP_RELEASE_VERSION', $version[2]); 
    } 
} 
if (PHP_VERSION_ID < 50300) { 
    include_once('php-5.2-.php'); 
} else { 
    include_once('php-5.3+.php'); 
} 
+0

Tendría que ver cómo funcionaría eso, no es una solución particularmente limpia, pero podría darme algunos de los beneficios de rendimiento Lo necesito sin interrumpir la funcionalidad en todas las versiones de PHP ... if (version_compare (PHP_VERSION, '5.3.0', '<')) {etc –

Cuestiones relacionadas