将额外参数传递给 usort 回调

Posted

技术标签:

【中文标题】将额外参数传递给 usort 回调【英文标题】:Pass extra parameters to usort callback 【发布时间】:2012-01-04 01:22:32 【问题描述】:

我有以下功能。 WordPress 功能,但这确实是一个 php 问题。他们根据每个对象元数据中的artist_lastname 属性对我的$term 对象进行排序。

我想在第一个函数中将字符串传递给$meta。这可以让我重用此代码,因为我可以将其应用于各种元数据属性。

但我不明白如何将 extra 参数传递给 usort 回调。我试图制作一个 JS 风格的匿名函数,但服务器上的 PHP 版本太旧(v. 5.2.17)并引发了语法错误。

任何帮助 - 或推到手册的右角 - 非常感谢。谢谢!

function sort_by_term_meta($terms, $meta) 

  usort($terms,"term_meta_cmp");


function term_meta_cmp( $a, $b ) 

    $name_a = get_term_meta($a->term_id, 'artist_lastname', true);
    $name_b = get_term_meta($b->term_id, 'artist_lastname', true);
    return strcmp($name_a, $name_b); 

PHP 版本:5.2.17

【问题讨论】:

【参考方案1】:

我认为这个问题值得更新。我知道最初的问题是针对 PHP 5.2 版的,但我来这里是为了寻找解决方案,并为较新版本的 PHP 找到了一个解决方案,并认为这对其他人也可能有用。

对于 PHP 5.3 及更高版本,您可以使用 'use' 关键字将局部变量引入匿名函数的局部范围。所以以下应该工作:

function sort_by_term_meta(&$terms, $meta) 
    usort($terms, function($a, $b) use ($meta) 
        $name_a = get_term_meta($a->term_id, 'artist_lastname', true);
        $name_b = get_term_meta($b->term_id, 'artist_lastname', true);
        return strcmp($name_a, $name_b);  
    );

一些更通用的代码

如果您只想对数组进行一次排序并且需要一个额外的参数,您可以使用这样的匿名函数:

usort($arrayToSort, function($a, $b) use ($myExtraArgument) 
    //$myExtraArgument is available in this scope
    //perform sorting, return -1, 0, 1
    return strcmp($a, $b);
);

如果您需要一个可重用函数来对需要额外参数的数组进行排序,您可以始终包装匿名函数,就像原始问题一样:

function mySortFunction(&$arrayToSort, $myExtraArgument1, $myExtraArgument2) 
    usort($arrayToSort, function($a, $b) use ($myExtraArgument1, $myExtraArgument2) 
        //$myExtraArgument1 and 2 are available in this scope
        //perform sorting, return -1, 0, 1
        return strcmp($a, $b);
    );

【讨论】:

太棒了!我是否可以建议修改您的第一个示例以实际使用 $meta 参数,以便更清晰。 超级有用,谢谢;意识到你也可以通过这种方式传递多个参数($extraArgument1, $extraArgument2) 您将如何将比较函数定义为不是闭包而是单独定义(以便您可以重用它)。 usort($terms, function($a, $b) use ($meta) - 当然,谢谢!【参考方案2】:

在 PHP 中,callback 的一个选项是传递一个包含对象句柄和方法名称的二元素数组以调用该对象。例如,如果$obj 是类MyCallable 的一个实例,并且您想在$obj 上调用MyCallablemethod1 方法,那么您可以将array($obj, "method1") 作为回调传递。

使用这种受支持的回调类型的一种解决方案是定义一个本质上类似于闭包类型的一次性类:

function sort_by_term_meta( $terms, $meta ) 

    usort($terms, array(new TermMetaCmpClosure($meta), "call"));


function term_meta_cmp( $a, $b, $meta )

    $name_a = get_term_meta($a->term_id, $meta, true);
    $name_b = get_term_meta($b->term_id, $meta, true);
    return strcmp($name_a, $name_b); 
 

class TermMetaCmpClosure

    private $meta;

    function __construct( $meta ) 
        $this->meta = $meta;
    

    function call( $a, $b ) 
        return term_meta_cmp($a, $b, $this->meta);
    

【讨论】:

我喜欢这个,但我认为我更喜欢@Kato,因为它有点像“分块”到一台小机器里——每次实例化一个新对象有什么好处吗? @djb:Kato 的解决方案本质上引入了一个全局变量,其中保存了$meta 字符串。 PHP 是单线程的,所以这不是一个交易破坏者。但是,我认为封装$meta 字符串更简洁,这样代码就不会在执行排序时意外更改静态变量的内容。 啊,现在我明白为什么了。我可以看到它的概念清洁度......被接受了。谢谢。 @Daniel - 我同意封装并且没有考虑将实际对象作为第一个参数而不是类名传递;很好的解决方案!也就是说,我认为静态方法更容易理解和理解,正如 djb 最初的 cmets 所指出的那样;权衡取舍;)【参考方案3】:

假设您可以访问对象和静态(PHP 5 或更高版本),您可以创建一个对象并直接在那里传递参数,如下所示:

<?php
class SortWithMeta 
    private static $meta;

    static function sort(&$terms, $meta) 
       self::$meta = $meta;
       usort($terms, array("SortWithMeta", "cmp_method"));
    

    static function cmp_method($a, $b) 
       $meta = self::$meta; //access meta data
       // do comparison here
    



// then call it
SortWithMeta::sort($terms, array('hello'));

假设您无权访问对象/静态;你可以做一个全局:

$meta = array('hello'); //define meta in global

function term_meta_cmp($a, $b) 
   global $meta; //access meta data
   // do comparison here


usort($terms, 'term_meta_cmp');

【讨论】:

谢谢,这很有趣。我想我更喜欢这个而不是每次都创建一个新对象?还是@Daniel 的方法更干净? 在静态方法SortWithMeta::sort中,我认为参数$terms需要通过引用传递。 @Daniel - 是的,我认为它需要通过引用传递;现在更新 @djb - Daniel's 在技术上将使用更少的资源,因为 $meta 可以收集垃圾(你总是可以在 cmp_method() 中添加self::$meta = null; 以使它们等效!),但它不会除非 $meta 包含大量数据,否则很重要;选择最容易理解和操作的东西;两个我都喜欢;)【参考方案4】:

警告 自 PHP 7.2.0 起,该函数已被弃用。强烈建议不要依赖此函数。

文档说create_function() 应该适用于 PHP >= 4.0.1。这行得通吗?

function term_meta_cmp( $a, $b, $meta )  
    echo "$a, $b, $meta<hr>"; // Debugging output

$terms = array("d","c","b","a");
usort($terms, create_function('$a, $b', 'return term_meta_cmp($a, $b, "some-meta");'));

【讨论】:

嗯,这确实有效,但并不能真正解决问题。您仍在将元硬编码传递给比较函数,需要一个变量。 太棒了!这是我发现调用递归闭包而不弄乱索引参数的唯一方法!遗憾的是,这个函数在 PHP 7.2 中被弃用了,应该被一个原生匿名函数(闭包)替换,因为它使用了 是邪恶的eval()【参考方案5】:

这对usort() 完全没有帮助,但可能会有所帮助。您可以使用其他排序函数之一array_multisort() 对数组进行排序。

我们的想法是构建一个您要排序的值的数组(来自get_term_meta() 的返回值),并针对您的主$terms 数组对其进行多重排序。

function sort_by_term_meta(&$terms, $meta) 

    $sort_on = array();
    foreach ($terms as $term) 
        $sort_on[] = get_term_meta($term->term_id, $meta, true);
    
    array_multisort($sort_on, SORT_ASC, SORT_STRING, $terms);

【讨论】:

【参考方案6】:

将 Args 传递给 usort() 的最简单解决方案是什么?

我喜欢这里的许多答案,但我希望有一个可以尽可能简单地完成的解决方案,但也可以演示!当调用 usort 时,提供额外的参数,例如这……

usort($sortable, [$arg1, $arg2, ... $argn, compareFunction]);

但请务必在之前定义这些参数,因此,您最终会得到类似...

$arg1 = 'something';
$arg2 = 'something else';
$argn = 'yet another thing';

usort($sortable, [$arg1, $arg2, ... $argn, compareFunction]);

然后$arg1$arg2$argn 将可用于compareFunction()

演示一下!

为了演示,这里有一个usort(),它只考虑被比较元素的前三个字母...

function cmp ($a, $b) 
     return strcmp(substr($a, 0, $num), substr($a, 0, $num));


$terms = ['123a', '123z', '123b',];
$num = 3;
$thing = 4;
usort($terms, [$num, $thing, cmp]);

print_r($terms);

Full Working Demo Online

【讨论】:

以上是关于将额外参数传递给 usort 回调的主要内容,如果未能解决你的问题,请参考以下文章

将额外的参数传递给回调函数

如何在 Javascript .filter() 方法中将额外参数传递给回调函数?

将额外参数传递给事件处理程序?

将参数传递给 UIcontrol 回调函数

如何将额外的额外参数传递给批处理文件?

将额外参数传递给异步映射