简体   繁体   English

如何在PHP中使用array_filter()进行函数式编程?

[英]How do I use array_filter() for functional programming in PHP?

Say I have an array of tags 说我有一系列标签

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

And I want to create a set of URLs with $_GET variables. 我想用$ _GET变量创建一组URL。
I'd like the links to be: 我希望链接是:
'A' linking to "index.php?x[]=B&x[]=C" 'A'链接到"index.php?x[]=B&x[]=C"
'B' linking to "index.php?x[]=A&x[]=C" 'B'链接到"index.php?x[]=A&x[]=C"
etc. ($_GET is an array with all elements except for "current" element) (I know there's an easier way to implement this: I'm actually simplifying a more complex situation) 等等($ _GET是一个包含除“current”元素之外的所有元素的数组)(我知道有一种更简单的方法来实现它:我实际上是在简化更复杂的情况)

I'd like to use array_filter() to solve this. 我想用array_filter()来解决这个问题。
Here's my attempt: 这是我的尝试:

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

But it doesn't work. 但它不起作用。 I have a suspicion that maybe it has to do with how maps and filters in PHP actually mutate the data structure themselves, and return a boolean, instead of using a functional style, where they don't mutate and return a new list. 我怀疑它可能与PHP中的地图和过滤器实际上如何改变数据结构本身有关,并返回一个布尔值,而不是使用功能样式,它们不会变异并返回一个新列表。

I am also interested in other ways to make this code more succinct. 我也对其他方法感兴趣,使这段代码更简洁。

Here's an alternative approach: 这是另一种方法:

// 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";

It's simply one call to array_reduce , and then an implode to pull the results together into a query string. 它只是对array_reduce的一次调用,然后是将结果一起拉入查询字符串的内implode

Based on an answer I gave in the comments ( 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

Basically, use array_diff to remove the entry from the array (instead of filtering), then pass it off to the http_build_query to come up with a valid URL. 基本上,使用array_diff从数组中删除条目(而不是过滤),然后将其传递给http_build_query以提供有效的URL。

I'm just going to answer the "why it doesnt work" part. 我只想回答“为什么它不起作用”的部分。

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

The tag_name variable is undefined in your lambda function's scope. 在lambda函数的作用域中未定义tag_name变量。 Now, it is defined in the creating functions scope(tag_to_url). 现在,它在创建函数范围(tag_to_url) 定义。 You can't exactly use the global keyword here either, because $tag_name isn't in the global scope, it is in the local tag_to_url scope. 您也不能在此处使用global关键字,因为$ tag_name不在全局范围内,而是在本地tag_to_url范围内。 You could declare the variable in the global scope and then it could work, but considering you're fond of functional approaches, I doubt you like global variables :) 你可以在全局范围内声明变量然后它可以工作,但考虑到你喜欢功能方法,我怀疑你喜欢全局变量:)

You could do trickery with string concatenation and var_export($tag_name) and pass that to create_function() if you wanted, but there's other better ways to achieve your goals. 您可以使用字符串连接和var_export($ tag_name)进行欺骗,并根据需要将其传递给create_function(),但还有其他更好的方法可以实现您的目标。

Also, as an aside, I'm guessing you might benefit from turning up php's error reporting level while developing. 另外,另外,我猜你可能会在开发时提高php的错误报告水平。 php would have thrown undefined variable notices at you, which helps debugging and understanding. php会向你抛出未定义的变量通知,这有助于调试和理解。

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

Something approaching real support for functional programming styles in PHP is very, very new--it was only in PHP 5.3 that functions became first class and anonymous functions were possible. 接近真正支持PHP中的函数式编程风格的东西非常非常新 - 只有在PHP 5.3中,函数才能成为一流的,并且匿名函数是可能的。

BTW, you should never use create_function() . 顺便说一句,你永远不应该使用create_function() What it really does is define a new function in the global namespace (which will never be garbage-collected!), and it uses eval() behind the scenes. 它真正做的是在全局命名空间中定义一个新函数( 永远不会被垃圾收集!),它在幕后使用eval()

If you have PHP 5.3 or greater, you can do this: 如果你有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));

If you have a PHP < 5.3, you should use a class+object for your closures instead of create_function() . 如果PHP <5.3,则应该使用类+对象作为闭包而不是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')));
}

In general, however, PHP is not very well suited to a functional style, and for your particular task using array_filter() is not very idiomatic PHP. 但是,一般情况下,PHP并不是很适合功能样式,对于使用array_filter()特定任务而言,PHP并不是非常惯用的。 array_diff() is a better approach. array_diff()是一种更好的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM