【问题标题】:PHP modify code to avoid anonymous functionsPHP修改代码以避免匿名函数
【发布时间】:2012-11-27 17:26:55
【问题描述】:

我找到了一些解决我一直遇到的排序问题的方法,但是代码使用 PHP 中的匿名函数。我使用的是 5.2.17 版本,我认为不支持匿名函数。

如何更改以下代码块以便在 PHP 5.2.17 中使用它们

$keys = array_flip($order);

usort($items, function($a, $b) use($keys)
{
    return $keys[$a['id']] - $keys[$b['id']];
});

来自PHP sort multidimensional array by other array

$sorted = array_map(function($v) use ($data) {
    return $data[$v - 1];
}, $order);

来自PHP - Sort multi-dimensional array by another array

更新: 问题之一是我不确定如何使用变量 $a、$b 和 $v。所以我不确定如何创建普通函数,从而绕过匿名函数。

【问题讨论】:

  • 匿名函数是在 5.3 中添加的,所以是的,你是对的......不支持:php.net/manual/en/functions.anonymous.php。通过使用常规的通常定义的函数来解决它。 usort($items, "your_func_here").
  • 谢谢 - 问题是我真的不知道匿名函数是如何工作的。例如,我不知道如何使用变量 $a、$b 和 $v。他们来自哪里?
  • 将您的函数声明为常规函数,并将它们作为字符串传递。
  • $a 和 $b 只是 usort 传递给您的函数的值,并且是 $item 中考虑的两个数组元素。您可以简单地将回调更改为反向排序,只需将function($b, $a) 作为其参数,而无需考虑实际的比较代码。
  • 谢谢马克。我仍然不是 100% 理解你。你能为我构建一个答案中的代码吗?

标签: php anonymous-function


【解决方案1】:

这两个匿名函数都使用use 子句将变量传递到本地范围。

您可以使用对象方法实现相同的效果,其中对象将这些变量作为属性。

在对象方法中,您可以访问这些。

$sorted = array_map(function($v) use ($data) {
    return $data[$v - 1];
}, $order);

一个示例性的映射对象可能如下所示:

class MapObj
{
    private $data;
    public function __construct($data) {
        $this->data = $data;
    }

    public function callback($v) {
        return $this->data[$v - 1];
    }
}

如您所见,它具有相同的功能,但只是用 PHP 5.2 语法编写的。

它的用法:

$map = new MapObj($data);
$callback = array($map, 'callback');
$sorted = array_map($callback, $order);

这就是它的工作原理。对象方法的回调总是写成array的形式,有两个成员,第一个是对象实例,第二个是对象方法的名称。

当然,您可以扩展它并将映射函数放入映射对象中,这样更直接:

class MapObj
{
    ...

    public function map(array $order) {
        $callback = array($this, 'callback');
        return array_map($callback, $order);
    }
}

新用法:

$map = new MapObj($data);
$sorted = $map->map($order);

如您所见,这可能会使用法更直接。我必须承认,我的方法命名在这里并不是很出色,所以我为您的改进留出了一些空间。

另一个好处是,您可以将回调方法的可见性设为私有。


在回调中将要使用的数据作为参数传递给映射函数的情况。那是因为你写了你已经有一个你想使用的类,但是你不能触摸构造函数。所以给定的例子有点短。

这是另一个没有使用构造函数的例子,我把它去掉了:

class MyObj
{
    private $data;

    private function callback($v) {
        return $this->data[$v - 1];
    }

    public function map($order, $data) {
        $callback = array($this, 'callback');
        $this->data = $data;
        return array_map($callback, $order);
    }
}

如您所见,不再需要构造函数来传递$data,而是将其作为附加参数传递给map() 方法。用法:

$myObj = new MyObj(....); // somewhere.

// later on:

$myObj->map($order, $data);

// could be also:

$this->map($order, $data);

如您所见,如何设置私有成员变量取决于您。做适合工作的事情。

【讨论】:

  • 哇,非常详细 - 谢谢。我必须在自己的类中声明该函数吗?或者这一切都可以被包装到我从中调用它的类中。
  • 当然,如果您已经有一个类定义,您可以添加回调函数。然后你应该将它们设为私有并在你的类中调用映射和排序函数,然后它真的很闪亮!
  • 啊抱歉,我的速度有点慢。让我失望的是公共函数 __construct($data)。我真的无法将它添加到我目前的课程中。对不起,如果这很明显......这是漫长的一天......
  • 当然不能在那里添加。但是,您可以通过其他一些公共函数传递它,将其分配给私有成员变量,进行回调并完成。它不得通过ctor函数传递。
  • 嗯好的,现在完全迷路了。谢谢你的帮助。我会看看我能拼凑出什么......
【解决方案2】:

你这里有一个 闭包$data 上——没有匿名函数就不可能 100% 重写它。这是最接近的近似值:

function _array_sort_callback($a, $b) {
    global $_array_sort_callback__keys;
    return $_array_sort_callback__keys[$a['id']] - $_array_sort_callback__keys[$b['id']];
}

... {
    $keys = array_flip($order);
    global $_array_sort_callback__keys;
    $_array_sort_callback__keys = $keys;
    usort($items, "_array_sort_callback");
}

请注意,我已经为全局名称添加了前缀,以避免发生冲突。函数名称和全局名称在您的应用程序中都必须是唯一的。

另外,请记住 PHP 5.2.17 已过时且不受支持。您应该尽快迁移出去。

【讨论】:

    【解决方案3】:

    如果您想模仿闭包,在特定时间对变量进行快照,您可以使用一个简单的基类作为值的容器,然后只需定义子类来实现比较逻辑。

    未经测试

    // base class exists purely to capture the value of some variables at instantiation time
    // kinda like a closure
    class VarCapture {
        protected $vars;
        function __construct($vars) {
            $this->vars = $vars;
        }
    }
    class ItemComparer extends VarCapture {
        function compare($a, $b) {
           $keys = $this->vars['keys'];
           return $keys[$a['id']] - $keys[$b['id']];
        }
    }
    
    class PluckMapper extends VarCapture {
        function pluck($v) {
            $data = $this->vars['data'];
            return $data[$v - 1];
        }
    }
    $keys = array_flip($order);
    $ic = new ItemComparer(compact('keys'));
    $callable = array($ic, 'compare');
    usort($items, $callable);
    
    $pm = new PluckMapper(compact('data'));
    $callable = array($mp, 'pluck');
    $sorted = array_map($callable, $order);
    

    请注意,我使用了 php 的回调伪类型 http://php.net/manual/en/language.types.callable.php

    【讨论】:

      【解决方案4】:

      您还可以将其重写为 5.3 之前的匿名函数,例如 create_function()。尽管create_function() 函数通常不充当闭包,但您可以使用一些技巧(不使用全局变量)使它们在某些有限的情况下充当闭包。您将封闭变量直接编码到函数的源代码中。限制是数据只能单向 - 在;封闭变量只能是“简单”数据类型,如数字、字符串和数组;并且使用create_function 创建的函数永远不会被释放,导致内存泄漏;加上它不是很有效。但我认为这对你的例子来说已经足够了(假设你只使用数组和字符串等)。

      $keys = array_flip($order);
      
      usort($items, create_function('$a,$b', '$keys = '.var_export($keys,true).';
          return $keys[$a["id"]] - $keys[$b["id"]];
      '));
      

      $sorted = array_map(create_function('$v', '$data = '.var_export($data,true).';
          return $data[$v - 1];
      '), $order);
      

      不过,更通用的 5.3 之前的解决方案是使用对象和方法作为闭包,就像 hakra 的回答一样。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-06
        • 2018-11-14
        • 2021-02-02
        • 2011-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多