Как я могу использовать array_filter () для функционального программирования в PHP? - PullRequest
3 голосов
/ 07 декабря 2011

Скажем, у меня есть массив тегов

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

И я хочу создать набор URL-адресов с переменными $ _GET.
Я бы хотел, чтобы ссылки были:
'A' ссылка на "index.php?x[]=B&x[]=C"
'B' ссылка на "index.php?x[]=A&x[]=C"
и т. д. ($ _GET - это массив со всеми элементами, кроме «текущего») (я знаю, что есть более простой способ реализовать это: IНа самом деле я упрощаю более сложную ситуацию)

Я бы хотел использовать 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 фактически изменяют структуру данных сами и возвращают логическое значение вместо использования функционального стиля, где они не изменяются и возвращают новый список.

Мне также интересны другие способы сделать этот код более лаконичным.

Ответы [ 4 ]

2 голосов
/ 07 декабря 2011

Вот альтернативный подход:

// 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 голос
/ 07 декабря 2011

Нечто, приближающееся к реальной поддержке стилей функционального программирования в PHP, очень, очень новое - только в PHP 5.3 функции стали первоклассными и стали возможными анонимные функции.

Кстати, вы никогда не должны использовать create_function().Что он на самом деле делает, так это определяет новую функцию в глобальном пространстве имен (которая никогда никогда не будет собирать мусор!) И использует eval() за кулисами.

Если у вас есть PHP5.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, вы должны использовать объект class + для замыканий вместо <code>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() не очень идиоматично.array_diff() - лучший подход.

1 голос
/ 07 декабря 2011

Я просто собираюсь ответить на часть «почему это не работает».

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

Переменная tag_name не определена в области действия вашей лямбда-функции.Теперь он определен в области создания функций (tag_to_url).Вы также не можете точно использовать глобальное ключевое слово здесь, потому что $ 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);
1 голос
/ 07 декабря 2011

Основываясь на ответе, который я дал в комментариях ( показано здесь ):

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

...