Следующая статья "Фэнтезийный дрейф обещаний в функциональном программировании"
Вы когда-нибудь слышали о функциональном программировании (ФП) в процессе работы и учебы? Но изучение функторов всегда сбивает с толку. Эта статья является кратким изложением моих заметок в процессе изучения функционального программирования, и я также делюсь ею со студентами, которые хотят изучать функциональное программирование.
Прежде чем учиться, вы должны задать себе несколько вопросов или использовать это как интервью.Посмотрите на следующие вопросы.Как вы должны ответить на них?
- Можете ли вы назвать две парадигмы программирования, наиболее важные для разработчиков JavaScript?
- Что такое функциональное программирование?
- Каковы преимущества и недостатки функционального программирования и объектной ориентации?
- Вы понимаете замыкания? Где вы его часто используете? Какое отношение замыкания имеют к каррированию?
- Что, если мы хотим инкапсулировать функцию защиты от сотрясений, такую как underscorede?
- Как вы понимаете понятие функторов? Что делает монадный функтор?
- Каков результат выполнения следующего кода?
var Container = function(x) { this.__value = x; }
Container.of = x => new Container(x);
Container.prototype.map = function(f){
console.log(f)
return Container.of(f(this.__value))
}
Container.of(3).map(x=>x+1).map(x => 'Result is ' + x);
console.log(Container.of(3).map(x=>x+1).map(x => 'Result is ' + x))
Теперь давайте учиться с вопросом. В конце статьи мы еще раз резюмируем ответы на эти вопросы.
1.1 Идея функционального программирования (FP)
Объектно-ориентированный (ООП) можно понимать как абстракцию данных, например, человека как абстрактный объект, связанный с данными. Функциональное программирование — это процесс абстрактного мышления, текущее действие должно быть абстрактным, связанным с действием.
举个例子:如果一个数a=1 ,我们希望执行+3(f函数),然后再*5(g函数),最后得到结果result是20
数据抽象,我们关注的是这个数据:a=1 经过f处理得到 a=4 , 再经过g处理得到 a = 20
过程抽象,我们关注的是过程:a要执行两个f,g两操作,先将fg合并成一个K操作,然后a直接执行K,得到 a=20
Вопрос: f и g объединяются в K, то какие условия должны выполняться для функций, которые можно объединять? Понятие чистой функции обсуждается ниже.
1.2 Чистые функции
Определение: Функция, входные параметры которой заданы, а выходные данныетолько уверен, то он чистая функция.
Особенности: Без состояния, без побочных эффектов, независимо от времени, мощности и т. д. (независимо от того, сколько раз звонить, тот же результат)
Какие функции являются чистыми?
let arr = [1,2,3];
arr.slice(0,3); //是纯函数
arr.splice(0,3); //不是纯函数,对外有影响
function add(x,y){ // 是纯函数
return x + y // 无状态,无副作用,无关时序,幂等
} // 输入参数确定,输出结果是唯一确定
let count = 0; //不是纯函数
function addCount(){ //输出不确定
count++ // 有副作用
}
function random(min,max){ // 不是纯函数
return Math.floor(Math.radom() * ( max - min)) + min // 输出不确定
} // 但注意它没有副作用
function setColor(el,color){ //不是纯函数
el.style.color = color ; //直接操作了DOM,对外有副作用
}
Это очень просто, давайте добавим требование дальше?
Если последняя функция, вы хотите работать с группой li в пакетном режиме, и есть много таких, которые нужно изменить, напишите публичную функцию?
function change (fn , els , color){
Array.from(els).map((item)=>(fn(item,color)))
}
change(setColor,oLi,"blue")
Итак, вопрос в том, является ли эта функция чистой функцией?
Прежде всего, независимо от того, что на входе, выход не определен.Далее мы анализируем, влияет ли это на внешнее.Мы обнаружили, что прямого воздействия в функции нет, но вызываемый setColor оказывает влияние на внешнее. . Так является ли изменение чистой функцией?
Ответ, конечно, нет.Здесь мы подчеркиваем, что зависимости чистых функций не должны быть затронуты, то есть функции, на которые есть внутренние ссылки, не могут влиять на внешний мир.
Вопрос: Можно ли как-нибудь очистить эту функцию?
1.3 Карри
Определение: вызовите функцию, передав ей всего несколько аргументов, и позвольте ей вернуть функцию для обработки остальных аргументов.
javascript
function add(x, y) {
return x + y;
}
add(1, 2)
******* 柯里化之后 *************
function addX(y) {
return function (x) {
return x + y;
};
}
var newAdd = addX(2)
newAdd (1)
Теперь вернемся к вопросу из предыдущего раздела?
Если мы не позволим setColor выполняться в функции изменения, разве изменение не будет чистой функцией?
javascript
function change (fn , els , color){
Array.from(els).map((item)=>(fn(item,color)))
}
change(setColor,oLi,"blue")
****** 柯里化之后 *************
function change(fn){
return function(els,color){
Array.from(els).map((item)=>(fn(item,color)))
}
}
var newSetColor = change(setColor);
newSetColor(oLi,"blue")
- Сначала мы проанализируем процесс каррирования. В предыдущей функции изменения есть три параметра: fn, els и цвет.Мы надеемся, что значение параметра fn равно setColor каждый раз, когда мы его вызываем, потому что мы хотим придать разные цвета разным DOM. Наш самый внешний параметр выбирает fn, поэтому возвращаемой функции не нужно вводить значение fn.
- Далее разбираем процесс очистки.После перезаписи,независимо от ввода fn,возвращается уникальная функция,а в функции изменения выполняется только оператор return,а функция setColor не выполняется при изменении,поэтому изменение Нет внешнего воздействия. Очевидно, что в настоящее время изменение является чистой функцией.
javascript
function change (fn , els , color){
Array.from(els).map((item)=>(fn(item,color)))
}
change(setColor,oLi,"blue")
//******* 通过一个curry函数*************
var changeCurry = curry(change);
var newSetColor = changeCurry(setColor);
newSetColor(oLi,"blue")
Очевидно, что функция изменения, переданная в fn в предыдущем разделе, является функцией более высокого порядка, очевидно, что это чистая функция, не имеющая внешних побочных эффектов. Возможно, это не позволяет вам по-настоящему понять функции высшего порядка, поэтому позвольте мне привести несколько примеров!
1.4.1 Эквивалентные функции
Определение: Эквивалентная функция может использоваться везде, где вызывается сама функция;
javascript
function __equal__(fn){
return function(...args){
return fn.apply(this,args);
}
}
//第一种
function add(x,y){
return x + y
}
var addnew1 = __equal__(add);
console.log(add(1,2));
console.log(addnew1(1,2));
//第二种
let obj = {
x : 1,
y : 2,
add : function (){
console.log(this)
return this.x + this.y
}
}
var addnew2 = __equal__(obj.add);
console.log( obj.add() ) ; //3
console.log( addnew2.call(obj)); //3
Первый не считает это
- equal(добавить): пусть эквивалентность (equal) функция передает исходную функцию для формирования замыкания и возвращает новую функцию addnew1
- addnew1(1,2): параметры, переданные в addnew1, вызываются в fn, а переменная fn указывает на исходную функцию.
Вторые считают это
- addnew2.call(obj): Пусть функция addnew2, возвращаемая функцией __equal__, выполняется в среде obj, то есть this в родительской функции в fn.apply(this, args); указывает на obj
- В fn.apply(this, args) это переменная, которая наследует родителя, а родитель указывает на obj, поэтому вызовите fn в окружении obj
- fn — указатель, формирующий замыкание, на obj.add
Итак, прочитав код, мы обнаружили, что это ничем не отличается от прямого присвоения функции переменной, так в чем же преимущество эквивалентной функции?
Перехват и мониторинг эквивалентных функций:
javascript
function __watch__(fn){
//偷偷干点啥
return function(...args){
//偷偷干点啥
let ret = fn.apply(this,args);
//偷偷干点啥
return ret
}
}
Мы знаем, что приведенная выше функция, по сути, является эквивалентной, и результат выполнения fn не имеет проблем с задачами. Но вы можете сделать что-то тайно до и после выполнения, например, console.log("Я выполнил это").
Вопрос: Для перехвата и мониторинга можно использовать эквивалентные функции, есть конкретные примеры?
1.4.2 Функция дросселирования
Front-end разработка будет сталкиваться с некоторыми частыми триггерами событий.Чтобы решить эту проблему, обычно есть два решения:
- дроссельная заслонка
- опровергать
javascript
function throttle(fn,wait){
var timer;
return function(...args){
if(!timer){
timer = setTimeout(()=>timer=null , wait);
console.log(timer)
return fn.apply(this,args)
}
}
}
const fn = function(){ console.log("btn clicked")}
const btn = document.getElementById('btn');
btn.onclick = throttle(fn , 5000);
Анализ кода
- Сначала мы определяем таймер
- Когда таймер не существует, выполните функцию в решении if
- setTimeout присваивает таймеру значение id, а fn также выполняет
- Если вы продолжите нажать, существует таймер, и функция в суждении IF не выполняется.
- Когда пришло время, функция обратного вызова SetTimeout очистила таймер, а затем выполнила функцию оценки IF.
Поэтому мы хорошо реализуем функцию дросселя, отслеживая и перехватывая эквивалентную функцию. Это не влияет на результат выполнения функции fn. Вот домашнее задание для всех: раз уж мы реализовали функцию троттлинга, можете ли вы написать функцию антитряски, основанную на том же принципе?
Вопрос: Ой, подобную троттлинговую функцию можно написать прямо в моем обычном проекте, инкапсулировать такую функцию вам кажется хлопотным?
1.5 Императив и декларативный
В обычное время, если мы не используем функции методов для реализации функции регулирования, мы можем напрямую реализовать функцию регулирования таким образом.
var timer;
btn.onclick = function(){
if(!timer){
timer = setTimeout(()=>timer=null , 5000);
console.log("btn clicked")
}
}
Так в чем же разница с предыдущими функциями высшего порядка?
很显然,在下面的这例子中,我们每次在需要做节流的时候,我们每次都需要这样重新写一次代码。 Рассказывать 程序如何执行。而上面的高阶函数的例子,我们定义好了一个功能函数之后,我们只需要告诉程序,你要做 Что вы можете.
- Императив: приведенный выше пример обязателен
- Декларативная: Примером функции высшего порядка является декларативная
Тогда давайте посмотрим, если вы пройдете по массиву и распечатаете элементы в каждом массиве, как реализовать это двумя способами?
//命令式
var array = [1,2,3];
for (i=0; i<array.length;i++){
console.log(array[i])
}
//声明式
array.forEach((i) => console.log(i))
Видеть forEach не очень привычно, оказывается, мы уже давно много используем функциональное программирование.
Здесь мы можем остановиться и подытожить функциональное программирование.
- Функциональное программирование больше связано с действиями, такими как определяемая нами функция регулирования, которая абстрагирует действие регулирования.
- Поэтому такая функция должна иметь определенные входы и выходы и не иметь доступа к внешнему миру.Такую функцию мы называем чистой функцией.
- Для очищения нечистых функций используется метод каррирования.
- В процессе каррирования параметр, который мы передаем, является именно функцией, и возвращаемое значение также является функцией, которая называется функцией высшего порядка.
- Функции более высокого порядка часто могут абстрагироваться от таких функций, как регулирование.
- Декларатив использует эти функции
Вопрос: Сейчас у нас есть предварительное представление о функциональном программировании, но мы еще не ощутили его мощь.Помните те чистые функции, о которых мы говорили ранее, которые можно комбинировать? В следующем разделе мы реализуем его
1.6 Композиция (compose)
function double(x) {
return x * 2
}
function add5(x) {
return x + 5
}
double(add5(1))
В приведенном выше коде мы выполнили два действия, но нам кажется не очень удобным писать вот так double(add5(x)) . Подумайте об этом с другой стороны, можем ли мы объединить функции вместе. Мы определяем функцию составления
var compose = function(f, g) {
return function(x) {
return f(g(x));
};
};
Очевидно, что с помощью функции compose мы можем комбинировать double и add5 вместе.
var numDeal = compose(double,add5)
numDeal(1)
- Во-первых, мы знаем, что двойное слияние по compose, add5 выполняется справа налево.
- Таким образом, 1 сначала выполняет сложение 5, а затем завершает умножение на 2.
Тогда есть несколько вопросов,
- Это используется только с одним параметром, что, если параметров несколько? Некоторые студенты уже думали об использовании каррирования
- Кроме того, это всего две функции, а если их несколько. Студенты, знакомые с использованием сокращения, возможно, уже имеют представление об этом.
- Compose выполняется справа налево, я хочу запустить слева направо? Конечно, у него также есть специальное название, называемое функцией конвейера.
Мы оставляем эти три вопроса в качестве вопросов для размышления. Мы реализуем это в углубленной теме.
Вопрос: Теперь, когда мы хотим завершить некоторые функции, нам нужно объединить функции, и объединенные функции будут иметь определенный порядок.Можем ли мы обрабатывать данные, как цепочка вызовов JQ?
1.7 Функторы
Говоря о функторах, мы сначала возвращаемся к нашей проблеме. Прежде чем мы выполним функцию обычно следующим образом.
function double(x) {
return x * 2
}
function add5(x) {
return x + 5
}
double(add5(1))
//或者
var a = add5(5)
double(a)
Теперь мы хотим взять данные за основу и выполнять одно действие за другим.
(5).add5().double()
Очевидно, что если мы сможем выполнить эту функцию, то она намного более привлекательной комфортной. Итак, мы знаем, что этот звонок будет выполнен
- (5) Должен быть ссылочным типом, поскольку требуется метод монтирования.
- Должен быть вызываемый метод для ссылочного типа
Поэтому мы пытаемся создать для него ссылочный тип
class Num{
constructor (value) {
this.value = value ;
}
add5(){
return this.value + 5
}
double(){
return this.value * 2
}
}
var num = new Num(5);
num.add5()
Мы обнаружили, что в это время есть проблема, то есть после того, как мы ее вызвали, возвращаемое значение является значением, и у нас нет возможности перейти к следующему шагу. Итак, нам нужно вернуть объект.
class Num{
constructor (value) {
this.value = value ;
}
add5 () {
return new Num( this.value + 5)
}
double () {
return new Num( this.value * 2)
}
}
var num = new Num(2);
num.add5 ().double ()
- Мы создаем экземпляр того же типа, что и num через новый Num
- Обработанное значение передается в качестве параметра для изменения значения this.value.
- Мы вернули этот объект и можем продолжать вызывать методы для обработки функций
Мы обнаружили, что обработка this.value в новом Num(this.value + 5) может быть полностью обработана путем передачи функции
А в реальном случае мы не можем создавать такие конструкторы с разными методами для каждого экземпляра, им нужен унифицированный метод.
class Num{
constructor (value) {
this.value = value ;
}
map (fn) {
return new Num(fn(this.value))
}
}
var num = new Num(2);
num.map(add5).map(double)
Мы создали метод карты и передали в него функцию обработки fn. Таким образом, мы можем идеально реализовать задуманную нами функцию.
Наконец, давайте разберемся с этой функцией.
class Functor{
constructor (value) {
this.value = value ;
}
map (fn) {
return Functor.of(fn(this.value))
}
}
Functor.of = function (val) {
return new Functor(val);
}
Functor.of(5).map(add5).map(double)
- Мы изменили имя исходного конструктора Num на Functor.
- Мы запечатываем метод Functor.of в новый Functor(val);
Теперь функтор. (5) .map (Add5) .map (double) идет, чтобы вызвать функцию. Вы чувствуете себя очень круто?
Ха-ха, что еще лучше, так это то, что вы изучили концепцию функторов до того, как узнали об этом. Общий функтор в приведенном выше примере является функтором. Теперь давайте подытожим, у него есть те характеристики.
- Functor — это контейнер, он содержит значение this.value (подумайте о своем новом Num(5) в начале).
- У Functor есть метод карты. Этот метод сопоставляет каждое значение в контейнере с другим контейнером. (Подумайте, есть ли у вас новый Num(fn(this.value) в нем)
- Все операции в функциональном программировании выполняются через функторы, то есть операции направлены не на значение, а на контейнер значения, функтор. (Подумайте, не манипулируете ли вы значением напрямую)
- Сам функтор имеет внешний интерфейс (метод карты), а различные функции являются операторами, которые обращаются к контейнеру через интерфейс и вызывают деформацию значения в контейнере. (Это означает, что функция, которую вы передаете, будет обрабатывать this.value)
- По общему соглашению в функциональном программировании функторы имеют метод of, который используется для создания новых контейнеров. (То есть в конце разобрались с функциями)
Ах, под которым я понимаю, что это за функтор. У новичка в функциональном программировании это не должно быть слишком запутанной концепцией. Мы видим многое, говоря об учебнике Полночь в письме полностью не упоминает синтаксис JavaScript. Чтобы объяснить математические понятия с тупым.
Я лично считаю, что прочитать книгу сто раз — это корыстно. То же самое верно и для концептуального понимания парадигм программирования, вы сначала знаете, что это такое. как использовать. Чем больше вы пишете и чем больше практикуетесь, тем больше вы будете понимать смысл. Трудно понять, когда всегда смотришь на кучу понятий.
Выше, объяснение процесса функтора (Functor), личное понимание. Поправки также приветствуются.
Вопрос: Мы реализовали один из самых общих функторов, не задавайте сейчас вопросов, давайте ковать железо, пока горячо, и учить другой функтор
1.7.1 Возможно функторы
Мы знаем, что при обработке строк, если строка имеет значение null, toUpperCase(); сообщит об ошибке.
Functor.of(null).map(function (s) {
return s.toUpperCase();
});
Затем мы вызываем функтор Functor, и также сообщается об ошибке.
Так есть ли способ отфильтровать нули в функторе?
class Maybe{
constructor (value) {
this.value = value ;
}
map (fn) {
return this.value ? Maybe.of(fn(this.value)) : Maybe.of(null);
}
}
Maybe.of = function (val) {
return new Maybe(val);
}
var a = Maybe.of(null).map(function (s) {
return s.toUpperCase();
});
Мы видим, что такой функтор Maybe можно завершить, только установив фильтр нулевых значений в .
Следовательно, разные типы функторов будут выполнять разные функции. Узнав об этом, мы обнаружили, что каждый функтор не оперирует непосредственно данными, которые необходимо обработать, и не участвует в функции, обрабатывающей данные.
Вместо этого в середине выполняется некоторый перехват и фильтрация. Это похоже на нашу функцию высшего порядка? Итак, теперь вы лучше понимаете функциональное программирование.
Теперь давайте сделаем небольшое упражнение с функциональным программированием: У нас есть строка «li», которую мы хотим преобразовать в верхний регистр и загрузить в div с текстом идентификатора.
var str = 'li';
Maybe.of(str).map(toUpperCase).map(html('text'))
Если у нас есть написанный функтор Maybe и две функциональные функции, для этого нам понадобится всего одна строка кода.
Итак, давайте посмотрим на нашу функцию зависимости.
let ? = id => Maybe.of(document.getElementById(id));
class Maybe{
constructor(value){
this.__value = value;
}
map(fn){
return this.__value ? Maybe.of(fn(this.__value)) : Maybe.of(null);
}
static of(value){
return new Maybe(value);
}
}
let toUpperCase = str => str.toUpperCase();
let html = id => html => {
?(id).map(dom => {
dom.innerHTML = html;
});
};
Давайте проанализируем код
- Так как Maybe.of(document.getElementById(id)) мы будем часто использовать его, поэтому мы инкапсулируем его двойным $
- Затем есть очень знакомый функтор Maybe, статический метод класса, используемый здесь
- toUpperCase — обычная чистая функция (если es6 плохо учится, можно использоватьbabel ) скомпилирован в es5
- html — это функция высокого уровня, мы сначала передаем идентификатор целевого домена, а затем возвращаем функцию, чтобы повесить строку на целевой дом.
var html = function(id) {
return function (html) {
?(id).map(function (dom) {
dom.innerHTML = html;
});
};
};
Давайте подумаем над другим вопросом: каково окончательное значение Maybe.of(str).map(toUpperCase).map(html('text'))?
Мы обнаружили, что функция, которая не была обработана в конце, не имеет возвращаемого значения, поэтому окончательный результат должен быть Maybe {__value: undefined}; Вот вопрос к вам, что нам делать, если мы хотим продолжить работу со строкой после печати строки в div?
Вопрос: Разобравшись с концепцией функторов, давайте изучим последний раздел этой статьи. ты счастлив
1.8 Функторы монад
Монадный функтор — это тоже функтор, на самом деле он очень прост в принципе, но его функция важнее. Итак, давайте посмотрим на это и другие Какая разница.
Давайте сначала посмотрим на такой пример и выведем его на консоль.
var a = Maybe.of( Maybe.of( Maybe.of('str') ) )
console.log(a);
console.log(a.map(fn));
console.log(a.map(fn).map(fn));
function fn(e){ return e.value }
- Иногда мы сталкиваемся с ситуацией, когда обрабатываемые данные представляют собой «Может быть» {значение: «Может быть}».
- Очевидно, нам нужно распаковывать слой за слоем.
- Это очень хлопотно, так есть ли способ получить значение внутри?
class Maybe{
constructor (value) {
this.value = value ;
}
map (fn) {
return this.value ? Maybe.of(fn(this.value)) : Maybe.of(null);
}
join ( ) {
return this.value;
}
}
Maybe.of = function (val) {
return new Maybe(val);
}
Если мы хотим получить значение внутри, просто используйте метод соединения, чтобы вернуть его. Поэтому я добавил к нему метод соединения
var a = Maybe.of( Maybe.of('str') )
console.log(a.join().map(toUpperCase))
Итак, теперь мы можем получать данные в нем с помощью метода соединения слой за слоем и обрабатывать их в верхнем регистре.
Теперь вам должно быть интересно, почему генерируется структура Maybe.of(Maybe.of('str'))?
Помните функцию html? Ранее мы оставили вопрос, что мне делать, если я хочу продолжать манипулировать строкой после того, как строка будет напечатана в div?
Очевидно, нам нужно, чтобы эта функция имела возвращаемое значение.
let html = id => html => {
return ?(id).map(dom => {
dom.innerHTML = html;
return html
});
};
Проанализируйте код.
- Если вы добавите только return html внутри, внешняя функция не вернет значение
- Если вы добавите возврат только снаружи, вы не сможете получить html
- Таким образом, вы можете добавить только внутри и снаружи
- А вот и Maybe.of( Maybe.of('LI'))
Итак, в это время мы подумали, что, поскольку мы знаем, что когда мы его выполняем, это окажет влияние, могу ли я указать, что это должно быть при его выполнении? Чтобы устранить это.
class Maybe{
constructor (value) {
this.value = value ;
}
map (fn) {
return this.value ? Maybe.of(fn(this.value)) : Maybe.of(null);
}
join ( ){
return this.value;
}
chain(fn) {
return this.map(fn).join();
}
}
Мы написали цепную функцию. Сначала он вызывает метод карты, а после окончания выполнения удаляет слой вложенных функторов
Итак, при выполнении мы можем написать так.
Maybe.of(str).map(toUpperCase).chain(html('text'))
Функция, возвращаемая таким образом, является функтором только с одним уровнем вложенности.
К настоящему времени мы изучили все концепции функционального программирования. Теперь, если интервьюер задает такой вопрос, каков ответ? Не слишком ли это просто.
var Container = function(x) { this.__value = x; }
Container.of = x => new Container(x);
Container.prototype.map = function(f){
console.log(f)
return Container.of(f(this.__value))
}
Container.of(3).map(x=>x+1).map(x => 'Result is ' + x);
console.log(Container.of(3).map(x=>x+1).map(x => 'Result is ' + x))
Но вы обнаружите, что мы не запутались в каждой концепции, а больше воплотились в достижимом коде, который вам не чужд.
Ха-ха, тогда вы можете спросить, изучал ли я фальшивое функциональное программирование, а я нет. Потому что я думаю, что функциональное программирование — это тоже программирование, и в конечном счете все дело в возвращении к практике повседневных проектов. И чтобы иметь дело с проектами разной сложности, знания, конечно, используются разные, как и в кораблестроении, у малых лодок есть метод строительства лодок, у круизных кораблей есть метод строительства нефтяных танкеров, а у авианосцев есть метод строительства авианосцев. . вы не Перед началом работы необходимо изучить все пункты знаний кораблестроения один за другим. В повседневной жизни и на работе у вас может не быть реальных возможностей строить авианосцы (например, писать фреймворки). Вместо того, чтобы тратить много времени на изучение этих концепций, лучше начать строить маленькую лодку. Поэтому понятия, которые не нужно изучать немедленно, в этой статье в значительной степени преуменьшаются.
Теперь, когда вы находитесь в море функционального программирования, разве это не непривычно, когда вы видите поднимающуюся листву лодку?
Нарисована ли красивая кривая на мысе и на горизонте?
Затем мы будем практиковать библиотеку Underscore.js. Углубитесь в каждую деталь, чтобы узнать о функциональном программировании. Узнайте больше трюков.
Наконец, эта статья — мои заметки по изучению функционального программирования, когда я ее пишу, я часто разговариваю сам с собой и иногда утешаю себя. Если есть ошибки, прошу покритиковать и исправить.
Приведенные выше ответы, обобщенные в конце статьи, - да, но они все еще в моем сердце, и я напишу их, когда у меня будет время. . . .