views:

150

answers:

4

How can I use PHP 5.3 Closures like We use Blocks in Ruby. I never used 'for' Loop in Ruby due to using Blocks with 'each' 'find_all' 'inject' Methods.

How can I use PHP 5.3 Closures like Ruby Blocks and say bye-bye to 'for' Loops :)

Like Between { and } is a Closure(or Block or Anonymous Function)

fruit = %w[apple banana orange]
fruit.each { |f| print "#{f}, " }

I do it in PHP this way,

$fruit = array('apple', 'banana', 'orange');
foreach ($fruit as $f) 
{
 print "$f, "; 
}

Is there a way to do this the Ruby way using PHP Closures as PHP 5.3 supports it.

A: 

I don't think an anonymous function is a substitute for a for loop, nor do I think it's necessary to replace for loops with them.

What it is useful for is a callback. Take this for example: (yes, it is a lame bubble sort, but it is an example)

<?php

function bubble_sort($sort_rule, $elements) {
    do {
        $swapped = false;
        for ($i = 0; $i < count($elements) - 1; $i++) {
            if ($sort_rule($elements[$i], $elements[$i + 1])) {
                $elements[$i] ^= $elements[$i + 1];
                $elements[$i + 1] ^= $elements[$i];
                $elements[$i] ^= $elements[$i + 1];
                $swapped = true;
            }
        }
    } while($swapped);
    return $elements;
}

print_r(bubble_sort(function ($a, $b) { if ($a > $b) return true; else return false; }
,array(1,6,3,7,42,-1,0,6)));
?>

Closures aren't a replacement for for loops in a procedural programming language like php. Sure if you're using lisp or scheme they are, but that's out of necessity.

You can write them that way, all you'll really be doing is creating an anonymous function with a for loop inside it. I think recursion would just be unnecessary if the task is just as easily accomplished with a for loop, and therefore you're not kissing for loops good bye.

Anonymous functions are very useful in event driven programming as well, when you want to just define a callback method really quick.

Tom Dignan
In Clojure Language (evolved from Scheme) it seems to be using closures in place of 'for' loops. I think in PHP its not possible :) Thanks very much
Rohit Chauhan
+1  A: 

Simple answer: you don't. Ruby is not devoid of for() loops, they just mask the word "for" and change the syntax a bit. If you wanted to use a closure, it'd just be a closure with a loop inside it, or an ugly (and less efficient) recursive closure.

And closures are NOT the same thing as blocks. Closures are comparable to functions in JavaScript-- that is, they can be stored in variables and sent as arguments.

Daniel
Thanks yes I think you are correct , Closures and Blocks are different in many ways
Rohit Chauhan
1. Ruby _is_ devoid of `for` loops (syntactically, `for` exists, but blocks entirely make it superfluous, not by "changing the syntax a bit", but being useful and ubiquitous language feature). 2. Ruby blocks _are_ closures. They can be stored in variables and sent as arguments.
Mladen Jablanović
+4  A: 

If you are looking at using lambdas to iterate over a PHP array, there are certain functions that you could use to accomplish that. Better illustrate it, I used a wrapper class enum:

class enum {
    public $arr;

    function __construct($array) {
        $this->arr = $array;
    }

    function each($lambda) {
        array_walk($this->arr, $lambda);
    }

    function find_all($lambda) {
        return array_filter($this->arr, $lambda);
    }

    function inject($lambda, $initial=null) {
        if ($initial == null) {
            $first = array_shift($this->arr);
            $result = array_reduce($this->arr, $lambda, $first);
            array_unshift($this->arr, $first);

            return $result;
        } else {
            return array_reduce($this->arr, $lambda, $initial);
        }
    }

}


$list = new enum(array(-1, 3, 4, 5, -7));
$list->each(function($a) { print $a . "\n";});

// in PHP you can also assign a closure to a variable 
$pos = function($a) { return ($a < 0) ? false : true;};
$positives = $list->find_all($pos);

// inject() examples
$list = new enum(range(5, 10));

$sum = $list->inject(function($sum, $n) { return $sum+$n; });
$product = $list->inject(function($acc, $n) { return $acc*$n; }, 1);

$list = new enum(array('cat', 'sheep', 'bear'));
$longest = $list->inject(function($memo, $word) {
        return (strlen($memo) > strlen($word)) ? $memo : $word; }
    );

That being said, closures in PHP aren't meant to replace the for loop nor do they behave like ruby blocks.

quantumSoup
Thanks , I think array_map() and array_walk() look better for this purpose
Rohit Chauhan
@Rohit Yes, I just wanted to make it easier for you to relate to ruby. By the way I added the `inject()` method.
quantumSoup
A: 

I think array_map() and array_walk() look better as RubyBlocks replacement.

Rohit Chauhan