Интересный способ представить функциональное программирование -
# main.php
require 'comparison.php';
function sortByPage ($a, $b) {
return $a['page'] <=> $b['page'];
}
function sortByVolume ($a, $b) {
return $a['volume'] <=> $b['volume'];
}
// first sort by volume, then sort by page
usort($my_array, \comparison\concat (sortByVolume, sortByPage));
Выше concat
может быть не сразу интуитивно понятным.Конечно, у вас есть concat
две строки или два массива, и вы даже можете подумать о добавлении чисел, таких как 1 + 2, как concat (1, 2)
.Но как это выглядит для concat
двух функций?
Когда вы concat
две строки, результатом является строка.Когда вы concat
два массива, результатом является массив.Когда вы concat
две функции сравнения, результатом будет другая функция сравнения.
# comparison.php
namespace comparison;
require 'ordered.php';
function concat ($c1, $c2) {
return function ($a, $b) use ($c1, $c2) {
return \ordered\concat
( $c1 ($a, $b)
, $c2 ($a, $b)
);
};
}
Функция сравнения принимает два аргумента и должна возвращать упорядоченный результат, (-1, 0или 1).Но так же, как мы можем реализовать concat
для функций сравнения, мы можем также реализовать concat
для упорядоченных результатов.Таким образом, мы передаем $a
и $b
каждой функции сравнения, $c1
и $c1
, каждая из которых дает упорядоченное значение .Окончательный результат - concat
из двух упорядоченных значений.
Так что же происходит, когда вы concat
два упорядоченных значения?Вы получаете другое заказанное значение!
// ordered.php
namespace ordered;
function concat ($a, $b) {
return $a === 0 ? $b : $a;
}
Выше всего необходимо все для достижения желаемого результата, но некоторые решения могут показаться произвольными.Возможно, вы также сочли утомительным определять sortByPage
и sortByVolume
как отдельные функции.Возможно, вам также понадобится возможность перевернуть сортировку, и это будет означать определение даже больше функций.Или, может быть, вам нужно отсортировать, используя больше , чем две функции сортировки.Расширение наших модулей comparison
и ordered
поможет.
order.php
PHP традиционно использует -1, 0 и 1 для кодирования упорядоченного результата.Мы можем быть более явными об этом в нашем модуле.Обратите внимание, что каждая функция проста, как и одна или две вещи.
namespace ordered;
const eq = 0;
const gt = 1;
const lt = -1;
function concat ($a, $b) {
return $a === eq ? $b : $a;
}
function sum ($os) {
return array_reduce ($os, '\ordered\concat', eq);
}
function reverse ($a) {
return $a * -1;
}
сравнение.php
В этом модуле мы реализуем <=>
как функцию compare
вместо оператора.Обратите внимание, что мы также вернули заказанный lt
, gt
или eq
вместо -1, 1 или 0. Это учитывает барьер абстракции и позволяет модулю order изменять свое представление, не затрагивая модуль сравнения .Еще раз подчеркнем, что каждая функция выполняет только одну или две вещи.
namespace comparison;
require 'ordered.php';
function compare ($a, $b) {
if ($a < $b)
return \ordered\lt; // respect abstraction barrier
if ($a > $b)
return \ordered\gt; // respect abstraction barrier
else
return \ordered\eq; // respect abstraction barrier
}
function concat ($c1, $c2) {
return function ($a, $b) use ($c1, $c2) {
return \ordered\concat // respect abstraction barrier
( $c1 ($a, $b)
, $c2 ($a, $b)
);
};
}
function sum ($cs) {
return array_reduce ($cs, '\comparison\concat', '\comparison\compare');
}
function reverse ($c) {
return function ($a, $b) use ($c) {
return \ordered\reverse ($c ($a, $b)); // respect abstraction barrier
};
}
function contramap ($f, $g) {
return function ($a, $b) use ($f, $g) {
return $f
( $g ($a)
, $g ($b)
);
};
}
Эти простые модули подходят практически для любой задачи сортировки.
1. Объедините любое числосортировщиков
// possibly based on form submission data
$sorters =
[ 'sortByVolume' // pre-defined function
, 'sortByPage' // pre-defined function
, 'sortByName' // pre-defined function
];
usort ($my_array, \comparison\sum ($sorters));
2. обратная сортировка
usort ($my_array, \comparison\reverse ('sortByVolume'));
3. анонимные сортировщики - не нужно определять sortByVolume
, sortByPage
и т. Д.
function sortBy ($key) {
return \comparison\contramap
( '\comparison\compare'
, function ($x) use ($key) { return $x[$key]; }
);
}
usort
( $my_array
, \comparison\concat
( sortBy ('volume') // sorter created ad hoc
, sortBy ('page') // same here
)
);
4. Возможность смешивать и сопоставлять эти эффекты и получать надежный результат - неоспоримое преимущество функционального программирования.
$sorters =
[ 'sortByVolume', // pre-defined function
, sortBy ('page') // anonymous sorter
, reverse (sortBy ('name')) // reverse sorter
];
usort
( $my_array
, \comparison\sum ($sorters) // combine all sorters
);
В итоге мы не использовали оператор космического корабля <=>
в наших модулях.Функциональное программирование - это программирование с помощью функций, поэтому такие вещи, как операторы, ключевые слова и операторы, избегают использования простых функций.Причина этого в том, что функции могут быть легко объединены с использованием композиции, тогда как операторы и операторы не всегда могут быть объединены.