оригинал:medium.com/better-pro Страна…
Переводчик: Front-end Xiaozhi
Ставь лайк и смотри, поиск в WeChat【Переезд в мир】Обратите внимание на этого человека, который не имеет большого фабричного прошлого, но имеет восходящий и позитивный настрой. эта статья
GitHub
GitHub.com/QQ449245884…Он был включен, статьи были классифицированы, и многие мои документы и учебные материалы были систематизированы.
Все говорили, что нет проекта для написания резюме, поэтому я помог вам найти проект, и это было с бонусом.【Учебник по строительству】.
После длительного изучения и использования объектно-ориентированного программирования давайте сделаем шаг назад и рассмотрим сложность системы.
Проведя небольшое исследование, я обнаружил такие концепции функционального программирования, как неизменяемость и чистые функции. Эти концепции позволяют создавать функции без побочных эффектов и, таким образом, упрощают обслуживание систем с другими преимуществами.
В этом посте функциональное программирование и некоторые связанные с ним важные концепции будут подробно представлены на многочисленных примерах кода.
Что такое функциональное программирование
Функциональное программирование — это парадигма программирования, стиль построения структуры и элементов компьютерных программ, который рассматривает вычисления как оценку математических функций, избегая изменений состояния и изменяемых данных.
чистая функция
Когда мы хотим понять функциональное программирование, первая базовая концепция, которую нам нужно знать, — это чистые функции, но что, черт возьми, такое чистые функции?
Как узнать, является ли функция чистой? Вот очень строгое определение:
-
Возвращает тот же результат, если заданы одни и те же аргументы (также известные какуверенность).
-
Он не вызывает никаких побочных эффектов.
Я получаю тот же результат, если заданы те же параметры
Он возвращает тот же результат, если заданы одни и те же аргументы. Представьте, что мы хотим реализовать функцию, которая вычисляет площадь круга.
Не чистая функция будет это делать, получаяradius
в качестве параметра, затем вычислитьradius * radius * PI
:
let PI = 3.14;
const calculateArea = (radius) => radius * radius * PI;
calculateArea(10); // returns 314.0
Почему это нечистая функция?Причина проста, потому что она использует глобальный объект, который не был передан функции в качестве аргумента.
А теперь представьте, что некоторые математики думают, что число пи на самом деле42
И значение глобального объекта изменено.
Нечистая функция получает10 * 10 * 42 = 4200
. по тем же параметрам (radius = 10
), мы получаем разные результаты.
починить это:
let PI = 3.14;
const calculateArea = (radius, pi) => radius * radius * pi;
calculateArea(10, PI); // returns 314.0
теперь поставьPI
Значение передается в качестве параметра функции, так что внешние объекты не вводятся.
- для параметров
radius = 10
а такжеPI = 3.14
, всегда получайте один и тот же результат:314.0
. - для
radius = 10
а такжеPI = 42
, всегда получайте один и тот же результат:4200
прочитать файл
Следующая функция читает внешний файл, это не чистая функция, и содержимое файла может быть другим в любое время.
const charactersCounter = (text) => `Character count: ${text.length}`;
function analyzeFile(filename) {
let fileContent = open(filename);
return charactersCounter(fileContent);
}
генерация случайных чисел
Любая функция, зависящая от генератора случайных чисел, не может быть чистой.
function yearEndEvaluation() {
if (Math.random() > 0.5) {
return "You get a raise!";
} else {
return "Better luck next year!";
}
}
Нет очевидных побочных эффектов
Чистые функции не вызывают никаких наблюдаемых побочных эффектов. Примеры видимых побочных эффектов включают изменение глобального объекта или параметров, передаваемых по ссылке.
Теперь мы хотим реализовать функцию, которая принимает целое число и возвращает это целое число.1
работать и возвращаться.
let counter = 1;
function increaseCounter(value) {
counter = value + 1;
}
increaseCounter(counter);
console.log(counter); // 2
Нечистая функция получает значение и переназначает егоcounter
, его значение увеличивается1
.
Функциональное программирование препятствует изменчивости. Мы модифицируем глобальный объект, но как сделать его чистой функцией? просто вернуть приращение1
ценность .
let counter = 1;
const increaseCounter = (value) => value + 1;
increaseCounter(counter); // 2
console.log(counter); // 1
чистая функцияincreaseCounter
вернуть2
,ноcounter
Значение остается прежним. Функция возвращает увеличенное значение без изменения значения переменной.
Нам будет легче понять нашу программу, если мы будем следовать этим двум простым правилам. Теперь каждая функция изолирована и не может влиять на остальную часть системы.
Чистые функции стабильны, непротиворечивы и предсказуемы. При одних и тех же аргументах чистая функция всегда возвращает один и тот же результат.
Нам не нужно рассматривать случай, когда один и тот же параметр дает разные результаты, потому что этого никогда не бывает.
Преимущества чистых функций
Чистый функциональный код определенно легче тестировать и не требует моков, поэтому мы можем проводить модульное тестирование чистых функций с другим контекстом:
- задан параметр
A
, ожидайте, что функция вернет значениеB
- задан параметр
C
, ожидайте, что функция вернет значениеD
Простой пример — взять набор чисел и добавить каждое число1
Эта операция скульптуры из песка.
let list = [1, 2, 3, 4, 5];
const incrementNumbers = (list) => list.map(number => number + 1);
перениматьnumbers
массив, использоватьmap
Увеличивайте каждое число и возвращайте новый список увеличенных чисел.
incrementNumbers(list); // [2, 3, 4, 5, 6]
для ввода[1,2,3,4,5]
, ожидаемый результат[2,3,4,5,6]
.
неизменность
Несмотря на изменение времени или то же самое, чистые функции тяжеловесов одинаковы.
Когда данные неизменяемы, их состояние не может быть изменено после их создания.
Мы не можем изменять неизменяемые объекты, если нам нужно сделать печатную копию, нам просто нужно сделать глубокую копию, а затем работать с этой копией.
В JS мы обычно используемfor
цикл,for
каждое пересечениеi
является переменной переменной.
var values = [1, 2, 3, 4, 5];
var sumOfValues = 0;
for (var i = 0; i < values.length; i++) {
sumOfValues += values[i];
}
sumOfValues // 15
При каждом обходе меняетсяi
а такжеsumOfValue
состояние, но как нам справиться с изменчивостью при обходе?рекурсия.
let list = [1, 2, 3, 4, 5];
let accumulator = 0;
function sum(list, accumulator) {
if (list.length == 0) {
return accumulator;
}
return sum(list.slice(1), accumulator + list[0]);
}
sum(list, accumulator); // 15
list; // [1, 2, 3, 4, 5]
accumulator; // 0
Приведенный выше код имеетsum
функция, которая принимает числовой вектор. функция вызывает себя до тех пор, покаlist
Пустая выходная рекурсия. За каждую «прогулку» мы будем прибавлять к общей стоимостиaccumulator
середина.
С рекурсией мы сохраняем переменную постоянной. не изменитсяlist
а такжеaccumulator
Переменная. Он сохраняет то же значение.
Наблюдение: мы можем использоватьreduce
для достижения этой функции. Это обсуждается в следующем разделе о функциях высшего порядка.
Строительство конечного состояния объекта также очень распространено. Предположим, у нас есть строка, пытаясь преобразовать строкуurl slug
.
В объектно-ориентированном программировании на Ruby мы можем создать классUrlSlugify
, этот класс имеетslugify
метод преобразования строкового ввода вurl slug
.
class UrlSlugify
attr_reader :text
def initialize(text)
@text = text
end
def slugify!
text.downcase!
text.strip!
text.gsub!(' ', '-')
end
end
UrlSlugify.new(' I will be a url slug ').slugify! # "i-will-be-a-url-slug"
Императивный метод программирования, использованный выше, сначала использует строчные буквы, чтобы указать, что мы хотимslugify
что в процессе, затем удалите ненужные пробелы и, наконец, замените оставшиеся пробелы дефисами.
Таким образом, изменяется входное состояние во всем процессе, что явно не соответствует концепции чистой функции.
Это можно оптимизировать с помощью композиции функций или объединения функций. Другими словами, результат функции будет использоваться в качестве входных данных для следующей функции без изменения исходной входной строки.
const string = " I will be a url slug ";
const slugify = string =>
string
.toLowerCase()
.trim()
.split(" ")
.join("-");
slugify(string); // i-will-be-a-url-slug
Приведенный выше код в основном выполняет следующие действия:
-
toLowerCase
: преобразовать строку во все строчные буквы. -
обрезка: удаляет пробелы с обоих концов строки.
-
split
а такжеjoin
: заменить все совпадающие экземпляры заменой в данной строке
ссылочная прозрачность
Затем реализуйтеsquare
функция:
const square = (n) => n * n;
При одних и тех же входных данных эта чистая функция всегда имеет одинаковые выходные данные.
square(2); // 4
square(2); // 4
square(2); // 4
// ...
Буду2
Передача в качестве аргумента квадратной функции всегда возвращает4
. Таким образом, мы можем поставитьsquare(2)
заменить4
, наша функция ссылочно прозрачна.
По сути, функцию можно считать прозрачной, если она всегда дает один и тот же результат для одних и тех же входных данных.
С этой концепцией мы можем сделать одну классную вещь — запомнить функцию. Предположим, что есть такая функция
const sum = (a, b) => a + b;
вызовите его с этими параметрами
sum(3, sum(5, 8));
sum(5, 8)
всегда равно13
, так что вы можете сделать некоторые трюки:
sum(3, 13);
Это выражение всегда получает16
, мы можем заменить все выражение числовой константой и записать ее.
Функции — это первоклассные граждане в JS
Как первоклассник в JS, функции очень кокетливы, а функции тоже можно рассматривать как значения и использовать как данные.
- Ссылайтесь на него из констант и переменных.
- Передайте его в качестве аргумента другим функциям.
- Верните его как результат других функций.
Идея состоит в том, чтобы рассматривать функции как значения и передавать функции как данные. Таким образом, мы можем комбинировать различные функции для создания новых функций с новым поведением.
Предположим, у нас есть функция, которая суммирует два значения, а затем удваивает значение следующим образом:
const doubleSum = (a, b) => (a + b) * 2;
Возьмите разницу между двумя значениями, затем удвойте значения:
const doubleSubtraction = (a, b) => (a - b) * 2;
Эти функции имеют схожую логику, но разница заключается в том, что делают операторы. Если мы можем рассматривать функции как значения и передавать их как аргументы, мы можем построить функцию, которая получает операторную функцию и использует ее внутри функции.
const sum = (a, b) => a + b;
const subtraction = (a, b) => a - b;
const doubleOperator = (f, a, b) => f(a, b) * 2;
doubleOperator(sum, 3, 1); // 8
doubleOperator(subtraction, 3, 1); // 4
f
параметр и использовать его для обработкиa
а такжеb
, который передается здесьsum
функция иsubtraction
и использоватьdoubleOperator
функции для объединения и создания новых моделей поведения.
Функции высшего порядка
Когда мы обсуждаем функции более высокого порядка, мы обычно включаем следующее:
-
Возьмите одну или несколько функций в качестве аргументов
-
возвращает функцию в результате
реализовано вышеdoubleOperator
Функция — это функция высшего порядка, потому что она принимает операторную функцию в качестве аргумента и использует ее.
мы часто используемfilter
,map
а такжеreduce
Все функции высшего порядка, смотри, смотри, смотри.
Filter
Для данной коллекции мы хотим фильтровать на основе атрибутов.filter
функция ожидаетtrue
илиfalse
значение, чтобы определить, должен ли элемент быть включен в набор результатов.
Если выражение обратного вызова истинно, функция фильтра включит элемент в результирующий набор, в противном случае — нет.
Простой пример: у нас есть набор целых чисел, и нам нужны только четные числа.
императив
Чтобы получить все четные числа в массиве с помощью императивного подхода, это обычно делается так:
-
Создать пустой массив
evenNumbers
-
перебирать массив
numbers
-
подтолкнуть четные числа к
evenNumbers
в массивеvar numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var evenNumbers = [];
for (var i = 0; i < numbers.length; i++) { if (numbers[i] % 2 == 0) { evenNumbers.push(numbers[i]); } }
console.log(evenNumbers); // (6) [0, 2, 4, 6, 8, 10]
мы также можем использоватьfilter
Функции высшего порядка для получения четных функций и возврата списка четных чисел:
const even = n => n % 2 == 0; const listOfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; listOfNumbers.filter(even); // [0, 2, 4, 6, 8, 10]
я здесьHacker Rank FPИнтересная задача решенаПроблема с массивом фильтров. Проблема состоит в том, чтобы отфильтровать заданный массив целых чисел и вывести только меньше указанного значения.X
эти значения.
Императивный подход обычно выглядит так:
var filterArray = function(x, coll) {
var resultArray = [];
for (var i = 0; i < coll.length; i++) {
if (coll[i] < x) {
resultArray.push(coll[i]);
}
}
return resultArray;
}
console.log(filterArray(3, [10, 9, 8, 2, 7, 5, 1, 3, 0])); // (3) [2, 1, 0]
Декларативный способ
Для всегда выше мы бы предпочли более декларативный подход к этой проблеме, например:
function smaller(number) {
return number < this;
}
function filterArray(x, listOfNumbers) {
return listOfNumbers.filter(smaller, x);
}
let numbers = [10, 9, 8, 2, 7, 5, 1, 3, 0];
filterArray(3, numbers); // [2, 1, 0]
существуетsmaller
используется в функцииthis
, сначала выглядит немного странно, но это легко понять.
filter
Второй параметр в функции означает вышеуказанноеthis
, то есть,x
стоимость.
Мы также можем использоватьmap
способ сделать это. Представьте, что у вас есть набор информации
let people = [
{ name: "TK", age: 26 },
{ name: "Kaio", age: 10 },
{ name: "Kazumi", age: 30 }
]
мы хотим фильтроватьage
Для лиц старше 21 года используйтеfilter
Способ
const olderThan21 = person => person.age > 21;
const overAge = people => people.filter(olderThan21);
overAge(people); // [{ name: 'TK', age: 26 }, { name: 'Kazumi', age: 30 }]
map
map
Основная идея функций — преобразовывать коллекции.
map
Метод преобразует коллекцию, применяя функцию ко всем ее элементам и создавая новую коллекцию из возвращенных значений.
Если мы не хотим фильтровать людей старше 21 года, мы хотим отобразить что-то вроде этого:TK is 26 years old.
Используя императивный стиль, мы обычно делаем:
var people = [
{ name: "TK", age: 26 },
{ name: "Kaio", age: 10 },
{ name: "Kazumi", age: 30 }
];
var peopleSentences = [];
for (var i = 0; i < people.length; i++) {
var sentence = people[i].name + " is " + people[i].age + " years old";
peopleSentences.push(sentence);
}
console.log(peopleSentences); // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old']
Декларатив сделает это:
const makeSentence = (person) => `${person.name} is ${person.age} years old`;
const peopleSentences = (people) => people.map(makeSentence);
peopleSentences(people);
// ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old']
Вся идея состоит в том, чтобы преобразовать данный массив в новый массив.
Еще одна интересная проблема HackerRank:проблема со списком обновлений. Мы хотим обновить значение массива его абсолютным значением.
Например, введите[1,2,3,- 4,5]
Выход должен быть[1,2,3,4,5]
,-4
Абсолютное значение4
.
Простое решение — обновление значений в каждой коллекции на месте, опасная практика
var values = [1, 2, 3, -4, 5];
for (var i = 0; i < values.length; i++) {
values[i] = Math.abs(values[i]);
}
console.log(values); // [1, 2, 3, 4, 5]
Мы используем функцию Math.abs, чтобы преобразовать значение в его абсолютное значение и выполнить обновление на месте.
Этот путь не лучшее решение.
Во-первых, во внешнем интерфейсе мы узнали об инвариантности, зная, что неизменность делает функцию более последовательной и предсказуемой, идея состоит в том, чтобы построить новый набор со всеми абсолютными значениями.
Во-вторых, почему бы не использовать здесьmap
"преобразовать" все данные
Первая мысль была проверитьMath.abs
Функция работает только с одним значением.
Math.abs(-1); // 1
Math.abs(1); // 1
Math.abs(-2); // 2
Math.abs(2); // 2
Мы хотим преобразовать каждое значение в положительное значение (абсолютное значение).
Теперь, когда вы знаете, как выполнять над значением операции с абсолютным значением, вы можете использовать эту функцию в качестве аргумента дляmap
функция.
Помните, что функции высшего порядка могут принимать функцию в качестве параметра и использовать ее?map
функция может сделать это
let values = [1, 2, 3, -4, 5];
const updateListMap = (values) => values.map(Math.abs);
updateListMap(values); // [1, 2, 3, 4, 5]
Reduce
reduce
Идея функции состоит в том, чтобы взять функцию и коллекцию и вернуть значение, созданное путем объединения этих элементов.
Типичным примером является получение общей суммы заказа.
Предположим, вы находитесь на сайте магазина и добавили в корзину (заказ) Товар 1, Товар 2, Товар 3 и Товар 4. Теперь мы хотим рассчитать общее количество тележек:
Императивным способом, то есть облегчить список заказов и добавить количество каждого продукта к общей сумме.
var orders = [
{ productTitle: "Product 1", amount: 10 },
{ productTitle: "Product 2", amount: 30 },
{ productTitle: "Product 3", amount: 20 },
{ productTitle: "Product 4", amount: 60 }
];
var totalAmount = 0;
for (var i = 0; i < orders.length; i++) {
totalAmount += orders[i].amount;
}
console.log(totalAmount); // 120
использоватьreduce
, мы можем создать функцию для вычисления объемаsum
и передать его в качестве параметраreduce
функция.
let shoppingCart = [
{ productTitle: "Product 1", amount: 10 },
{ productTitle: "Product 2", amount: 30 },
{ productTitle: "Product 3", amount: 20 },
{ productTitle: "Product 4", amount: 60 }
];
const sumAmount = (currentTotalAmount, order) => currentTotalAmount + order.amount;
const getTotalAmount = (shoppingCart) => shoppingCart.reduce(sumAmount, 0);
getTotalAmount(shoppingCart); // 120
здесь естьshoppingCart
, чтобы получить текущийcurrentTotalAmount
ФункцияsumAmount
, и суммируя ихorder
объект.
мы также можем использоватьmap
БудуshoppingCart
преобразовать вamount
сбор, а затем использоватьreduce
функция иsumAmount
функция.
const getAmount = (order) => order.amount; const sumAmount = (acc, amount) => acc + amount;
function getTotalAmount(shoppingCart) {
return shoppingCart
.map(getAmount)
.reduce(sumAmount, 0);
}
getTotalAmount(shoppingCart); // 120
getAmount
перениматьproduct
объект и просто вернутьсяamount
ценность, то есть[10,30,20,60]
,Потом,reduce
Объедините все термины, сложив их вместе.
Пример трех функций
Посмотрите, как работает каждая функция высшего порядка. Вот вам пример того, как совместить эти три функции на простом примере.
Говоря о корзинах, допустим, у нас есть этот список продуктов в нашем заказе.
let shoppingCart = [
{ productTitle: "Functional Programming", type: "books", amount: 10 },
{ productTitle: "Kindle", type: "eletronics", amount: 30 },
{ productTitle: "Shoes", type: "fashion", amount: 20 },
{ productTitle: "Clean Code", type: "books", amount: 60 }
]
Если вы хотите делать покупки с типом автомобиляbooks
от суммы, что обычно делается так:
-
Отфильтровать тип, тип которого — книги
-
использовать
map
Преобразовать корзину вamount
собирать. -
использовать
reduce
Сложите все предметы.
let shoppingCart = [
{ productTitle: "Functional Programming", type: "books", amount: 10 },
{ productTitle: "Kindle", type: "eletronics", amount: 30 },
{ productTitle: "Shoes", type: "fashion", amount: 20 },
{ productTitle: "Clean Code", type: "books", amount: 60 }
]
const byBooks = (order) => order.type == "books";
const getAmount = (order) => order.amount;
const sumAmount = (acc, amount) => acc + amount;
function getTotalAmount(shoppingCart) {
return shoppingCart
.filter(byBooks)
.map(getAmount)
.reduce(sumAmount, 0);
}
getTotalAmount(shoppingCart); // 70
Ошибки, которые могут существовать после развертывания кода, не могут быть известны в режиме реального времени.Чтобы решить эти ошибки впоследствии, много времени тратится на отладку журнала.Кстати, я рекомендую всем полезный инструмент мониторинга ошибок.Fundebug
общаться с
Статья постоянно обновляется каждую неделю. Вы можете выполнить поиск «Big Move to the World» в WeChat, чтобы прочитать и обновить ее как можно скорее (на одну или две статьи раньше, чем в блоге). Эта статья находится на GitHub.GitHub.com/QQ449245884…Он был включен, и многие мои документы были разобраны. Добро пожаловать в Звезду и совершенство. Вы можете обратиться в тестовый центр для ознакомления во время собеседования. Кроме того, обратите внимание на паблик-аккаунт и ответьте в фоновом режиме.Благосостояние, вы можете увидеть преимущества, вы знаете.