quasi blocchi in php

08 December 2006 // php. stuff.

Every programmer knows that iterators are generally better than loops. Although php has a variety of iterator functions, their use is seriously limited, because there is no way to pass user code to the iterator.

 

The overly verbose create_function() is a very unequal replacement for lambdas or blocks, but in php we have no other choice. We can only try to reduce verbosity.

function qe($body) {
    static $cache = array();
    if(!isset($cache[$body])) {
        $argc = 0;
        preg_replace('/\$(\d)/e', '$argc = max($argc, $1)', $body);
        $args = '$_' . implode("='',\$_", range(0, $argc)) . "=''";
        $expr = preg_replace('/\$(\d)/', '$_$1', $body);
        $cache[$body] = create_function($args, "return $expr;");
    }
    return $cache[$body];
}

qe() (= "quoted expression") creates a dynamic function from an expression given in a string. $0...$9 act as function parameters (like in preg_replace). Examples

# find odd numbers < 30

$a = array_filter(range(1, 30), qe('$0 % 2'));
print_r($a);

# muliply two arrays

$a = array(3, 5, 7);
$b = array(4, 6, 8);

$product = array_map(qe('$0 * $1'), $a, $b);
print_r($product);

Unlike create_function, qe also caches its results, so that the identical functions are created only once. This is quite practical when calling qe in a loop.

qe requires its argument to be an expression, i.e. no operators are allowed. The slight variation called qf ("quoted function") takes the complete function body as an argument.

function qf($body) {
    static $cache = array();
    if(!isset($cache[$body])) {
        $argc = 0;
        preg_replace('/\$(\d)/e', '$argc = max($argc, $1)', $body);
        $args = '$_' . implode("='',\$_", range(0, $argc)) . "=''";
        $expr = preg_replace('/\$(\d)/', '$_$1', $body);
        $cache[$body] = create_function($args, "$expr;");
    }
    return $cache[$body];
}

Example

$apples = array(0, 1, 2, 3);

$messages = array_map(qf('
    if($0 == 0) return "no apples";
    if($0 == 1) return "one apple";
    else return "$0 apples"
'), $apples);

print_r($messages);

Of course, this kind of code only makes sense with very short function definitions.

 
If you think this comment is spam or otherwise completely irrelevant here, feel free to hide it. The comment disappears immediately, though it is not deleted, so I have an option to "unhide" it later.
 

comment on this