Это третья статья из серии [Как функциональное программирование JS]. Ставьте лайк👍 подписывайтесь👀, следите😄
Первые два портала:
- «XDM, как функциональное программирование на JS? Смотрите, хватит! (один)"
- «XDM, как функциональное программирование на JS? Смотрите, хватит! (два)"
Во второй части мы говорили о фундаменте фундамента, важности важного -«Частичная функция», частичная функция инкапсулируется функцией, которая позволяет уменьшить количество передаваемых параметров и решает проблему ручного указания фактических параметров.
Важнее:
Когда функции имеют только один параметр, мы можем относительно легко их комбинировать. Эта единичная функция удобна для последующих комбинированных функций;
Да, эта статья о"композиционная функция". Это главный приоритет функционального программирования!

Комбинаторные функции
имея в виду
Функциональное программирование похоже на сборку Lego!
В Lego есть все виды деталей, которые мы собираем и соединяем, чтобы сделать большую сборку или модель.
Функциональное программирование также имеет функции различных функций, которые мы собираем и объединяем для достижения конкретной функции.
Давайте рассмотрим пример, например, мы хотим использовать эти две функции для анализа текстовых строк:
function words(str) {
return String( str )
.toLowerCase()
.split( /\s|\b/ )
.filter( function alpha(v){
return /^[\w]+$/.test( v );
} );
}
function unique(list) {
var uniqList = [];
for (let i = 0; i < list.length; i++) {
if (uniqList.indexOf( list[i] ) === -1 ) {
uniqList.push( list[i] );
}
}
return uniqList;
}
var text = "To compose two functions together";
var wordsFound = words( text );
var wordsUsed = unique( wordsFound );
wordsUsed;
// ["to", "compose", "two", "functions", "together"]
Не смотрите на это, просто знайте: мы сначала обработали текст с помощью функции words, а затем обработали результат предыдущей обработки wordsFound с помощью уникальной функции;
Такой процесс подобен обработке товаров на производственной линии и их обработке на сборочной линии.

Представьте, если бы вы были владельцем фабрики, как бы вы оптимизировали процессы и сократили расходы?
Здесь автор дает решение:Снимите конвейерную ленту!

То есть для уменьшения промежуточной переменной мы можем назвать ее так:
var wordsUsed = unique( words( text ) );
wordsUsed
Ведь без промежуточных переменных понятнее, можно опять оптимизировать?
Далее мы можем инкапсулировать весь поток обработки в функцию:
function uniqueWords(str) {
return unique( words( str ) );
}
uniqueWords(text)
Это похоже на черный ящик, вам не нужно заботиться о процессе внутри, просто знайте, что входит в этот ящик! Какой выход! Ввод и вывод четкие, функция понятная и очень "чистая"! Как показано на рисунке:

В то же время его можно перемещать или собирать заново.
Вернемся к внутренностям функции uniqueWords(), и ее поток данных также ясен:
uniqueWords <-- unique <-- words <-- text
упаковочная коробка
Приведенный выше инкапсулированный блок uniqueWords очень хорош.Если мы хотим постоянно инкапсулировать блоки, такие как uniqueWords, должны ли мы писать их один за другим?
function uniqueWords(str) {
return unique( words( str ) );
}
function uniqueWords_A(str) {
return unique_A( words_A( str ) );
}
function uniqueWords_B(str) {
return unique_B( words_B( str ) );
}
...
Итак, чтобы не лениться, мы можем написать более мощную функцию для автоматической инкапсуляции блока:
function compose2(fn2,fn1) {
return function composed(origValue){
return fn2( fn1( origValue ) );
};
}
// ES6 箭头函数形式写法
var compose2 =
(fn2,fn1) =>
origValue =>
fn2( fn1( origValue ) );

Затем вызов становится таким:
var uniqueWords = compose2( unique, words );
var uniqueWords_A = compose2( unique_A, words_A );
var uniqueWords_B = compose2( unique_B, words_B );
Так ясно!
случайная комбинация
Выше мы объединили две функции, на самом деле мы можем также объединить N функций;
finalValue <-- func1 <-- func2 <-- ... <-- funcN <-- origValue

Например, используйте функцию составления для достижения (постучите по клавише):
function compose(...fns) {
return function composed(result){
// 拷贝一份保存函数的数组
var list = fns.slice();
while (list.length > 0) {
// 将最后一个函数从列表尾部拿出
// 并执行它
result = list.pop()( result );
}
return result;
};
}
// ES6 箭头函数形式写法
var compose =
(...fns) =>
result => {
var list = fns.slice();
while (list.length > 0) {
// 将最后一个函数从列表尾部拿出
// 并执行它
result = list.pop()( result );
}
return result;
};
На основе предыдущего примера uniqueWords(..) мы дополнительно добавляем функцию для обработки (отфильтровывать строки с длиной меньше или равной 4):
function skipShortWords(list) {
var filteredList = [];
for (let i = 0; i < list.length; i++) {
if (list[i].length > 4) {
filteredList.push( list[i] );
}
}
return filteredList;
}
var text = "To compose two functions together";
var biggerWords = compose( skipShortWords, unique, words );
var wordsUsed = biggerWords( text );
wordsUsed;
// ["compose", "functions", "together"]
Таким образом, функция compose имеет три входных параметра, и все они являются функциями. Мы также можем использовать свойства частичных функций, чтобы добиться большего:
function skipLongWords(list) { /* .. */ }
var filterWords = partialRight( compose, unique, words ); // 固定 unique 函数 和 words 函数
var biggerWords = filterWords( skipShortWords );
var shorterWords = filterWords( skipLongWords );
biggerWords( text );
shorterWords( text );
Функция filterWords является более функциональным вариантом (фильтрует строки на основе того, что делает первая функция).
сочинить вариант
Функция compose(..) очень важна, но мы не можем использовать нашу собственную функцию compose(..) в продакшене и предпочитаем использовать решение, предоставляемое библиотекой. Знание того, как это работает внутри, также очень полезно для углубления нашего понимания функционального программирования.
Давайте разберемся с другим вариантом compose(..) - рекурсивно:
function compose(...fns) {
// 拿出最后两个参数
var [ fn1, fn2, ...rest ] = fns.reverse();
var composedFn = function composed(...args){
return fn2( fn1( ...args ) );
};
if (rest.length == 0) return composedFn;
return compose( ...rest.reverse(), composedFn );
}
// ES6 箭头函数形式写法
var compose =
(...fns) => {
// 拿出最后两个参数
var [ fn1, fn2, ...rest ] = fns.reverse();
var composedFn =
(...args) =>
fn2( fn1( ...args ) );
if (rest.length == 0) return composedFn;
return compose( ...rest.reverse(), composedFn );
};
Повторение действий посредством рекурсии проще для понимания, чем отслеживание результатов в цикле, для понимания которого может потребоваться больше времени;
Основываясь на предыдущем примере, если мы хотим поменять местами параметры:
var biggerWords = compose( skipShortWords, unique, words );
// 变成
var biggerWords = pipe( words, unique, skipShortWords );
Просто измените внутреннюю реализацию compose(..) на эту строку:
...
while (list.length > 0) {
// 从列表中取第一个函数并执行
result = list.shift()( result );
}
...
Хотя порядок параметров только обратный, между ними нет существенной разницы.
абстрактная способность
Вы задаетесь вопросом: при каких обстоятельствах его можно упаковать в вышеупомянутую «коробку»?
Это тест - умение абстрагироваться!
Фактически,Если две или более задач имеют общие части, мы можем их инкапсулировать.
Например:
function saveComment(txt) {
if (txt != "") {
comments[comments.length] = txt;
}
}
function trackEvent(evt) {
if (evt.name !== undefined) {
events[evt.name] = evt;
}
}
Его можно абстрактно инкапсулировать как:
function storeData(store,location,value) {
store[location] = value;
}
function saveComment(txt) {
if (txt != "") {
storeData( comments, comments.length, txt );
}
}
function trackEvent(evt) {
if (evt.name !== undefined) {
storeData( events, evt.name, evt );
}
}
При выполнении такого рода абстракции существует принцип, который часто называют СУХИМ (не повторяйтесь), даже если мы тратим время на выполнение второстепенной работы.
Абстракция может продвинуть ваш код дальше!Например, приведенный выше пример можно дополнительно обновить:
function conditionallyStoreData(store,location,value,checkFn) {
if (checkFn( value, store, location )) {
store[location] = value;
}
}
function notEmpty(val) { return val != ""; }
function isUndefined(val) { return val === undefined; }
function isPropUndefined(val,obj,prop) {
return isUndefined( obj[prop] );
}
function saveComment(txt) {
conditionallyStoreData( comments, comments.length, txt, notEmpty );
}
function trackEvent(evt) {
conditionallyStoreData( events, evt.name, evt, isPropUndefined );
}
Таким образом, оператор if также абстрактно инкапсулируется.
Абстракция — это процесс, с помощью которого программист связывает имя с потенциально сложной частью программы, чтобы имя можно было рассматривать как представляющее назначение функции, а не то, как эта функция реализована. Абстракция снижает концептуальную сложность, скрывая ненужные детали, позволяя программистам в любое время сосредоточиться на поддерживаемом подмножестве содержимого программы. -- "язык программирования"
Мы упоминали в начале этой серии: «Все дело в создании более читаемого и понятного кода».
С другой стороны, абстракция — это процесс превращения императивного кода в декларативный. От «как» к «что».
Императивный код в первую очередь связан с описанием того, что нужно сделать, чтобы точно выполнить задачу. Декларативный код описывает, каким должен быть вывод, и оставляет реализацию другим частям.
Например, синтаксис структуры, добавленный ES6:
function getData() {
return [1,2,3,4,5];
}
// 命令式
var tmp = getData();
var a = tmp[0];
var b = tmp[3];
// 声明式
var [ a ,,, b ] = getData();
Разработчики должны быть осторожны при использовании соответствующего уровня абстракции для каждой части своей программы, ни слишком много, ни недостаточно.
резюме этапа
Композиция функций предназначена для соответствия «декларативному стилю программирования», который фокусируется на «что», а не на «что».
Он может переходить с выходного маршрута одного вызова функции на другой вызов функции и т. д., и делает это с помощью compose(..) или его вариантов. .
Мы ожидаем, что функция в комбинации будет унарной (вход и выход должны быть максимально едиными), что также является очень важным моментом, упомянутым в предыдущей статье.
Композиция — декларативный поток данных — является одним из наиболее важных инструментов, лежащих в основе других особенностей функционального программирования!
над!
Я Наггетс Энтони, официальный аккаунт [Наггетс Энтони], вывод раскрывает ввод, а технологии видят жизнь.