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

发布于 2024-12-20 05:15:27 字数 858 浏览 5 评论 0原文

假设我有一个标签数组

$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 中的映射和过滤器如何实际改变数据结构本身并返回布尔值有关,而不是使用函数式风格,在函数式风格中它们不会改变并返回新列表。

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

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.
I'd like the links to be:
'A' linking to "index.php?x[]=B&x[]=C"
'B' linking to "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)

I'd like to use array_filter() to solve this.
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.

I am also interested in other ways to make this code more succinct.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

萌辣 2024-12-27 05:15:27

这是一种替代方法:

// 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 将结果合并到查询字符串中。

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.

司马昭之心 2024-12-27 05:15:27

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

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

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.

BTW, you should never use 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.

If you have PHP 5.3 or greater, you can do this:

$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().

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. array_diff() is a better approach.

吹梦到西洲 2024-12-27 05:15:27

根据我在评论中给出的答案(此处显示):

<?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。

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.

潜移默化 2024-12-27 05:15:27

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

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

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. Now, it is defined in the creating functions scope(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. 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.

Also, as an aside, I'm guessing you might benefit from turning up php's error reporting level while developing. php would have thrown undefined variable notices at you, which helps debugging and understanding.

// ideally set these in php.ini instead of in the script
error_reporting(E_ALL);
ini_set('display_errors', 1);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文