【问题标题】:How do I use array_filter() for functional programming in PHP?如何在 PHP 中使用 array_filter() 进行函数式编程?
【发布时间】:2011-12-07 18:18:38
【问题描述】:

假设我有一个标签数组

$all_tags = array('A', 'B', 'C');

我想创建一组带有 $_GET 变量的 URL。
我希望链接是:
'A' 链接到 "index.php?x[]=B&x[]=C"
'B' 链接到 "index.php?x[]=A&x[]=C"
等等($_GET 是一个包含除“当前”元素之外的所有元素的数组) (我知道有一种更简单的方法来实现这一点:我实际上是在简化更复杂的情况)

我想使用array_filter() 来解决这个问题。
这是我的尝试:

function make_get ($tag) { return 'x[]=' . $tag; }
function tag_to_url ($tag_name) {
   global $all_tags;

   $filta = create_function('$x', 'global $all_tags; return ($x != $tag_name);'); 
   return 'index.php?' . implode('&', array_map("make_get", array_filter($all_tags, "filta")));
}
print_r(array_map("", $all_tags));

但它不起作用。我怀疑这可能与 PHP 中的映射和过滤器如何实际改变数据结构本身并返回一个布尔值有关,而不是使用函数式样式,它们不会改变并返回一个新列表。

我也对其他使代码更简洁的方法感兴趣。

【问题讨论】:

  • 所以...无论是什么链接,从链接集合中删除该特定条目,然后使用剩余值构建 URL?
  • 可能是这样的:ideone.com/vVnWe?
  • @BradChristie 评论#2:这不像我想要的那样“实用”,但它工作得很好而且简洁。你为什么不把它作为答案呢?
  • 我不确定您的方向是否正确,或者我是否正确理解了这个问题。
  • 顺便说一句,在您的create_function 中,$tag_name 未定义(这可能是问题所在)。我不认为 PHP 将作用域转移到create_function,就像C#(或类似的)对lambda 所做的那样。但我可能错了。

标签: php filter functional-programming higher-order-functions


【解决方案1】:

这是另一种方法:

// The meat of the matter
function get_link($array, $tag) {
    $parts = array_reduce($array, function($result, $item) use($tag)
                          {
                              if($item != $tag) $result[] = 'x[]='.$tag;
                              return $result;
                          });
    return implode('&', $parts);
}

// Test driver

$all_tags = array('A', 'B', 'C');

echo get_link($all_tags, 'A');
echo "\n";
echo get_link($all_tags, 'B');
echo "\n";
echo get_link($all_tags, 'C');
echo "\n";

只需调用array_reduce,然后调用implode 即可将结果汇总到一个查询字符串中。

【讨论】:

  • +1 表示没有副作用。仍然渴望得到像map ("x[]=" ++) (filter (/= tag_name) all_tags) tho 这样简短的答案:)
【解决方案2】:

在 PHP 中接近真正支持函数式编程风格的东西是非常非常新的——只有在 PHP 5.3 中,函数才成为第一类并且匿名函数成为可能。

顺便说一句,你永远不应该使用create_function()。它真正做的是在全局命名空间中定义一个新函数(永远不会被垃圾回收!),它在幕后使用eval()

如果你有 PHP 5.3 或更高版本,你可以这样做:

$all_tags = array('A', 'B', 'C');

function is_not_equal($a, $b) {
    return $a != $b;
}

function array_filter_tagname($alltags, $name) {
    $isNotEqualName = function($item) use ($name){
        return is_not_equal($item, $name);
    };
    // array_merge() is ONLY to rekey integer keys sequentially.
    // array_filter() preserves keys.
    return array_merge(array_filter($alltags, $isNotEqualName));
}

function make_url($arr) {
    return 'input.php?'.http_build_query(array('x'=>$arr));
}
$res = array_filter_tagname($all_tags, 'B');
print_r($res);
print_r(make_url($res));

如果你的 PHP create_function()。

class NotEqualName {
    protected $otheritem;
    function __construct($otheritem) { // with PHP 4, use "function NotEqualName($otheritem) {"
        $this->otheritem = $otheritem;
    }
    function compare($item) {
        return $item != $this->otheritem;
    }
}

function array_filter_tagname_objectcallback($alltags, $name) {
    $isNotEqualName = new NotEqualName($name);
    return array_merge(array_filter($alltags, array($isNotEqualName,'compare')));
}

然而,一般来说,PHP 不太适合函数式风格,并且对于您的特定任务,使用 array_filter() 不是非常惯用的 PHP。 array_diff() 是更好的方法。

【讨论】:

    【解决方案3】:

    根据我在 cmets (shown here) 中给出的答案:

    <?php
    
      $all_tags = array('A', 'B', 'C');
    
      function tag_to_url($tag_name)
      {
        global $all_tags;
    
        $remaining_tags = array_diff($all_tags, array($tag_name));
        return sprintf('index.php?%s', 
                 http_build_query(array('x'=>array_values($remaining_tags))));
      }
    
      echo tag_to_url('B'); // index.php?x%5B0%5D=A&x%5B1%5D=C
                            // basically: index.php?x[0]=A&x[1]=C
    

    基本上,使用 array_diff 从数组中删除条目(而不是过滤),然后将其传递给 http_build_query 以提供有效的 URL。

    【讨论】:

    • +1 是一个非常好的方法。我真诚地希望你没有在那里使用全局。 :)
    • @Jon:使用 OP 给我的东西。 ;-)
    【解决方案4】:

    我只是要回答“为什么它不起作用”部分。

    $filta = create_function('$x', 'global $all_tags; return ($x != $tag_name);'); 
    

    tag_name 变量在您的 lambda 函数范围内未定义。现在,它 在创建函数范围(tag_to_url)中定义。您也不能在这里完全使用 global 关键字,因为 $tag_name 不在全局范围内,而是在本地 tag_to_url 范围内。您可以在全局范围内声明变量,然后它就可以工作,但考虑到您喜欢函数式方法,我怀疑您是否喜欢全局变量 :)

    如果你愿意,你可以使用字符串连接和 var_export($tag_name) 进行欺骗并将其传递给 create_function(),但还有其他更好的方法来实现你的目标。

    另外,顺便说一句,我猜你可能会受益于在开发过程中调高 php 的错误报告级别。 php 会向你抛出未定义的变量通知,这有助于调试和理解。

    // ideally set these in php.ini instead of in the script
    error_reporting(E_ALL);
    ini_set('display_errors', 1);
    

    【讨论】:

      猜你喜欢
      • 2018-05-04
      • 2014-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多