如何像在Ruby中使用块一样使用PHP 5.3闭包

5

我如何像在Ruby中使用块一样使用PHP 5.3闭包。由于使用“each”、“find_all”、“inject”方法,我从未在Ruby中使用过'for'循环。

我如何像Ruby块一样使用PHP 5.3闭包并告别“for”循环 :)

在 {和}之间的代码是一个闭包(或块或匿名函数)

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

我用PHP实现如下:

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

有没有一种使用 PHP 闭包的 Ruby 方式来完成这个任务,因为 PHP 5.3 支持它。


你无法教会老狗新把戏。PHP随着时间的推移获得了所有“花哨”的功能(面向对象,命名空间,闭包),但它一开始设计得不够好-这意味着现在所有的语言特性都存在了,但并不总是很有用和方便使用。从答案中可以看出,仍然最容易和最优雅地使用预定义的数组函数。 - Mladen Jablanović
哦,另一个重要的事情:闭包不同于匿名函数。闭包需要保留它们定义时所在的环境,以便调用它们。而匿名函数则不需要。从技术上讲,Ruby块不是函数,而是一种语言结构。但是它们可以很容易地转换为函数,例如通过在前面加上“lambda”。 - Mladen Jablanović
Rohit,你能解释一下为什么 Ruby 程序员不喜欢“for”循环吗?在我看来,他们大多数时候使用块的目的都是和“for”循环(或 PHP 中的“foreach”)做完全相同的事情,但他们似乎真的很讨厌它们。我以前问过,但从未得到一个好的答案。 - James
块对我来说看起来很自然。在 PHP 中使用 foreach 对我来说也很自然,直到我跳上 Ruby 的车。但是了解不同的编程风格是好的。现在有很多重构工作,因为遗留应用程序必须转换为 PHP 5 和 Ruby 1.9。还有一种绝对的函数式语言叫做 Clojure(源自 LISP),我发现在那里使用闭包可以使代码非常简短和可读。但那是另一回事! - Rohit Chauhan
4个回答

5

如果你想使用lambda表达式来迭代PHP数组,有一些函数可以用来实现它。为了更好地说明,我使用了一个包装器类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; }
    );

话虽如此,PHP 中的闭包并不是为了替代 for 循环,也不像 Ruby 的块那样行事。


谢谢,我认为array_map()和array_walk()更适合这个目的。 - Rohit Chauhan
@Rohit 是的,我只是想让你更容易理解 Ruby。顺便说一下,我添加了 inject() 方法。 - quantumSoup

2
我认为array_map()和array_walk()更适合作为RubyBlocks的替代品。

0

我认为匿名函数不能替代for循环,也不必用它们来替换for循环。

它有用的地方在于回调函数。以这个为例: (是的,这是一个糟糕的冒泡排序,但这只是一个例子)

<?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)));
?>

在像PHP这样的过程式编程语言中,闭包并不能替代for循环。当然,如果你使用Lisp或Scheme,那么它们是必需的。

你可以这样编写它们,但实际上你只是创建了一个带有for循环的匿名函数。如果任务可以轻松地通过for循环完成,那么我认为递归就是不必要的,因此你并没有放弃for循环。

在事件驱动编程中,匿名函数也非常有用,当你想快速定义回调方法时。


在Clojure语言(从Scheme演变而来)中,似乎使用闭包代替'for'循环。我认为在PHP中这是不可能的 :) 非常感谢 - Rohit Chauhan

0
简单的回答:你不需要这样做。Ruby并没有摒弃for()循环,只是将其隐藏在其他语法中。如果你想使用闭包,它只是一个内部带有循环的闭包,或者是一个丑陋(而且效率较低)的递归闭包。
而闭包和块不是同一件事情。闭包类似于JavaScript中的函数——也就是说,它们可以存储在变量中并作为参数发送。

谢谢,我认为你是正确的,闭包和块在许多方面都是不同的。 - Rohit Chauhan
1
  1. Ruby 缺乏 for 循环(语法上,for 存在,但块完全使其变得多余,不是通过“稍微改变语法”,而是成为有用和普遍的语言特性)。
  2. Ruby 块 闭包。它们可以存储在变量中并作为参数发送。
- Mladen Jablanović

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接