2011-07-03 9 views
15

¿Hay alguna forma de hacer array_map pero como iterador?PHP mapeo de matriz perezosa

Por ejemplo:

foreach (new MapIterator($array, $function) as $value) 
{ 
    if ($value == $required) 
     break; 
} 

La razón para hacer esto es que la función $ es difícil de calcular y $ array tiene demasiados elementos, sólo tiene que asignar hasta que encuentre un valor específico. array_map calculará todos los valores antes de que pueda buscar el que yo quiera.

Podría implementar el iterador yo mismo, pero quiero saber si hay una manera nativa de hacerlo. No pude encontrar nada buscando en la documentación de PHP.

Respuesta

6

En resumen: Nº

No hay ninguna asignación iterador perezoso integrada de PHP. Hay una función no perezosa iterator_apply(), pero nada como lo que está buscando.

Puede escribir uno usted mismo, como ha dicho. Le sugiero que extienda IteratorIterator y simplemente anule el método current().

Si existiera tal cosa, estaría documentado here o here.

+0

Es de hecho una buena idea usar 'IteratorIterator' en lugar de escribir un decorador completa implementación de la interfaz' Iterator'. – Harmen

1

Estoy pensando en una implementación de clase de mapa simple que utiliza una matriz de claves y una matriz de valores. La ejecución podría ser utilizado como la clase de iterador de Java, mientras que le iterar a través de él como:

while ($map->hasNext()) { 
    $value = $map->next(); 
    ... 
} 
proceso
0
foreach ($array as $key => $value) { 
    if ($value === $required) { 
     break; 
    } else { 
     $array[$key] = call_back_function($value); 
    } 
} 

e iterar hasta que se encuentre el valor requerido.

0

no se moleste con un iterador, es la respuesta:

foreach ($array as $origValue) 
{ 
    $value = $function($origValue); 
    if ($value == $required) 
     break; 
} 
+0

Aaaand esto fue downvoted porque? Nadie más dio un ejemplo de "cómo" hacer lo que pidió el OP, sin efectos secundarios. – Izkata

+0

No lo rechacé, pero esa no es una solución perezosa, solo una solución de cortocircuito. Solo cubre el ejemplo, no toda la gama de situaciones que el PO probablemente intenta. (Aunque la respuesta aceptada no proporciona ningún código, apunta en la dirección correcta.) – Brilliand

0

escribí esta clase para utilizar una devolución de llamada para ese propósito. Uso:

$array = new ArrayIterator(array(1,2,3,4,5)); 
$doubles = new ModifyIterator($array, function($x) { return $x * 2; }); 

Definición (no dude en modificar para sus necesidades):

class ModifyIterator implements Iterator { 
    /** 
    * @var Iterator 
    */ 
    protected $iterator; 

    /** 
    * @var callable Modifies the current item in iterator 
    */ 
    protected $callable; 

    /** 
    * @param $iterator Iterator|array 
    * @param $callable callable This can have two parameters 
    * @throws Exception 
    */ 
    public function __construct($iterator, $callable) { 
     if (is_array($iterator)) { 
      $this->iterator = new ArrayIterator($iterator); 
     } 
     elseif (!($iterator instanceof Iterator)) 
     { 
      throw new Exception("iterator must be instance of Iterator"); 
     } 
     else 
     { 
      $this->iterator = $iterator; 
     } 

     if (!is_callable($callable)) { 
      throw new Exception("callable must be a closure"); 
     } 

     if ($callable instanceof Closure) { 
      // make sure there's one argument 
      $reflection = new ReflectionObject($callable); 
      if ($reflection->hasMethod('__invoke')) { 
       $method = $reflection->getMethod('__invoke'); 
       if ($method->getNumberOfParameters() !== 1) { 
        throw new Exception("callable must have only one parameter"); 
       } 
      } 
     } 

     $this->callable = $callable; 
    } 

    /** 
    * Alters the current item with $this->callable and returns a new item. 
    * Be careful with your types as we can't do static type checking here! 
    * @return mixed 
    */ 
    public function current() 
    { 
     $callable = $this->callable; 
     return $callable($this->iterator->current()); 
    } 

    public function next() 
    { 
     $this->iterator->next(); 
    } 

    public function key() 
    { 
     return $this->iterator->key(); 
    } 

    public function valid() 
    { 
     return $this->iterator->valid(); 
    } 

    public function rewind() 
    { 
     $this->iterator->rewind(); 
    } 
} 
0

iteradores de PHP son bastante difíciles de utilizar, especialmente si se requiere anidación profunda. LINQ, que implementa consultas tipo SQL para matrices y objetos, es más adecuado para esto, ya que permite un fácil encadenamiento de métodos y es muy vago. Una de las bibliotecas que lo implementa es YaLinqo *. Con él, puede realizar la asignación y el filtrado de esta manera:

// $array can be an array or \Traversible. If it's an iterator, it is traversed lazily. 
$is_value_in_array = from($array)->contains(2); 

// where is like array_filter, but lazy. It'll be called only until the value is found. 
$is_value_in_filtered_array = from($array)->where($slow_filter_function)->contains(2); 

// select is like array_map, but lazy. 
$is_value_in_mapped_array = from($array)->select($slow_map_function)->contains(2); 

// first function returns the first value which satisfies a condition. 
$first_matching_value = from($array)->first($slow_filter_function); 
// equivalent code 
$first_matching_value = from($array)->where($slow_filter_function)->first(); 

Hay muchas más funciones, más de 70 en total.

* desarrollado por mí

5

Ésta es una función de mapa colección perezosa que le devuelve una Iterator:

/** 
* @param array|Iterator $collection 
* @param callable $function 
* @return Iterator 
*/ 
function collection_map($collection, callable $function) { 
    foreach($collection as $element) { 
     yield $function($element); 
    } 
} 
+0

Buena idea para hacer de esto una función gratuita en lugar de una función de miembro de la clase 'Colección' – Harmen

0

Tenga una mirada en Non-standard PHP library.Tiene una función lazy map:

use function \nspl\a\lazy\map; 

$heavyComputation = function($value) { /* ... */ }; 
$iterator = map($heavyComputation, $list);