Я считаю, что я изучил некоторые / многие / большинство основных концепций, лежащих в основе функционального программирования в JavaScript. Однако у меня возникают проблемы с чтением функционального кода, даже кода, который я написал, и мне интересно, кто-нибудь может дать мне какие-нибудь советы, советы, рекомендации, терминологию и т. Д., Которые могут помочь.
Возьми код ниже. Я написал этот код. Он призван установить процентное сходство между двумя объектами, скажем, {a:1, b:2, c:3, d:3}
и {a:1, b:1, e:2, f:2, g:3, h:5}
. Я создал код в ответ на этот вопрос о переполнении стека . Поскольку я точно не знал, о каком процентном сходстве спрашивал плакат, я предоставил четыре разных типа:
- процент ключей в первом объекте, который можно найти во втором,
- процент значений в первом объекте, который можно найти во втором, включая дубликаты,
- процент значений в 1-м объекте, который можно найти во 2-м, без дубликатов, и
- процент пар {ключ: значение} в 1-м объекте, который можно найти во 2-м объекте.
Я начал с достаточно императивного кода, но быстро понял, что эта проблема хорошо подходит для функционального программирования. В частности, я понял, что если бы я мог извлечь функцию или три для каждой из четырех вышеупомянутых стратегий, которые определяли тип функции, которую я хотел сравнить (например, ключи или значения и т. Д.), То я мог бы быть возможность сократить (простите за игру слов) остальную часть кода на повторяемые единицы. Вы знаете, сохраняя это СУХОЙ. Поэтому я переключился на функциональное программирование. Я очень горжусь результатом, я думаю, что он достаточно элегантный, и я понимаю, что хорошо справился.
Тем не менее, даже написав код сам и понимая каждую его часть во время конструирования, когда я теперь оглядываюсь на него, я по-прежнему несколько удивлен, как читать какую-то конкретную строку, так и как "Грок", что на самом деле делает какая-то конкретная половина строки кода. Я обнаружил, что делаю мысленные стрелки, чтобы соединить разные части, которые быстро разлагаются в беспорядок спагетти.
Итак, может ли кто-нибудь сказать мне, как «читать» некоторые из более запутанных фрагментов кода таким образом, который является одновременно и кратким, и это способствует моему пониманию того, что я читаю? Я думаю, что части, которые меня больше всего привлекают, это те, у которых есть несколько жирных стрелок подряд и / или части, у которых есть несколько скобок подряд. Опять же, по своей сути, я могу со временем разобраться в логике, но (я надеюсь) есть лучший способ быстро и четко и прямо «взять на себя» линию функционального программирования на JavaScript.
Не стесняйтесь использовать любую строку кода снизу или даже другие примеры. Однако, если вы хотите, чтобы некоторые первоначальные предложения от меня, вот несколько. Начните с достаточно простого. С ближе к концу кода, есть такой , который передается в качестве параметра функции: obj => key => obj[key]
. Как читать и понимать это? Более длинный пример одну полной функции от около начала: const getXs = (obj, getX) => Object.keys(obj).map(key => getX(obj)(key));
. Последняя map
часть меня особенно привлекает.
Пожалуйста, обратите внимание, что в данный момент я не ищу ссылки на Haskell или символьную абстрактную нотацию или основы каррирования и т. Д. То, что я ищу, это английские предложения, которые я могу молча произносить, глядя на строку кода. Если у вас есть ссылки, которые конкретно касаются именно этого, прекрасно, но я также не ищу ответы, в которых говорится, что я должен пойти и прочесть некоторые основные учебники. Я сделал это, и я получил (по крайней мере, значительное количество) логику. Также обратите внимание, что мне не нужны исчерпывающие ответы (хотя такие попытки приветствуются): приветствуются даже короткие ответы, которые обеспечивают элегантный способ чтения одной конкретной строки кода, который в противном случае был бы проблематичным.
Я предполагаю, что часть этого вопроса такова: могу ли я читать функциональный код линейно, знаете ли, слева направо и сверху вниз? Или кто-то в значительной степени вынужден создавать мысленную картину спагетти-подобной проводки на странице кода, которая определенно не линейна? И если это нужно сделать, нам все равно нужно прочитать код, так как же взять линейный текст и связать спагетти?
Любые советы будут оценены.
const obj1 = { a:1, b:2, c:3, d:3 };
const obj2 = { a:1, b:1, e:2, f:2, g:3, h:5 };
// x or X is key or value or key/value pair
const getXs = (obj, getX) =>
Object.keys(obj).map(key => getX(obj)(key));
const getPctSameXs = (getX, filter = vals => vals) =>
(objA, objB) =>
filter(getXs(objB, getX))
.reduce(
(numSame, x) =>
getXs(objA, getX).indexOf(x) > -1 ? numSame + 1 : numSame,
0
) / Object.keys(objA).length * 100;
const pctSameKeys = getPctSameXs(obj => key => key);
const pctSameValsDups = getPctSameXs(obj => key => obj[key]);
const pctSameValsNoDups = getPctSameXs(obj => key => obj[key], vals => [...new Set(vals)]);
const pctSameProps = getPctSameXs(obj => key => JSON.stringify( {[key]: obj[key]} ));
console.log('obj1:', JSON.stringify(obj1));
console.log('obj2:', JSON.stringify(obj2));
console.log('% same keys: ', pctSameKeys (obj1, obj2));
console.log('% same values, incl duplicates:', pctSameValsDups (obj1, obj2));
console.log('% same values, no duplicates: ', pctSameValsNoDups(obj1, obj2));
console.log('% same properties (k/v pairs): ', pctSameProps (obj1, obj2));
// output:
// obj1: {"a":1,"b":2,"c":3,"d":3}
// obj2: {"a":1,"b":1,"e":2,"f":2,"g":3,"h":5}
// % same keys: 50
// % same values, incl duplicates: 125
// % same values, no duplicates: 75
// % same properties (k/v pairs): 25
источник
Я не проделал много высокофункциональной работы в Javascript (я бы сказал, что это так - большинство людей, говорящих о функциональном Javascript, могут использовать карты, фильтры и сокращения, но ваш код определяет свои собственные функции более высокого уровня , несколько более продвинутый, чем это), но я сделал это в Haskell, и я думаю, что, по крайней мере, часть опыта переводится. Я дам вам несколько советов о том, что я узнал:
Указание типов функций действительно важно. Haskell не требует, чтобы вы указывали тип функции, но включение типа в определение значительно облегчает чтение. Хотя Javascript не поддерживает явную типизацию таким же образом, нет причин не включать определение типа в комментарий, например:
Немного попрактиковавшись в работе с такими определениями типов, они делают смысл функции намного более понятным.
Именование важно, возможно, даже больше, чем в процедурном программировании. Многие функциональные программы написаны в очень лаконичном стиле, сложном для соглашения (например, соглашение о том, что «xs» является списком / массивом, а «x» является элементом в нем, очень распространено), но если вы не понимаете этот стиль легко я бы предложил более подробное наименование. Глядя на конкретные имена, которые вы использовали, «getX» вроде бы непрозрачен, и поэтому «getXs» тоже не очень помогает. Я бы назвал «getXs» чем-то вроде «applyToProperties», а «getX», вероятно, будет «propertyMapper». «getPctSameXs» будет тогда «процентPropertiesSameWith» («с»).
Другой важной вещью является написание идиоматического кода . Я заметил, что вы используете синтаксис
a => b => some-expression-involving-a-and-b
для создания функций карри. Это интересно и может быть полезно в некоторых ситуациях, но вы не делаете здесь ничего, что выигрывает от карри-функций, и было бы более идиоматичным Javascript вместо этого использовать традиционные функции с несколькими аргументами. Это может помочь с первого взгляда увидеть, что происходит. Вы также используетеconst name = lambda-expression
для определения функций, гдеfunction name (args) { ... }
вместо этого было бы более идиоматичным . Я знаю, что они семантически немного отличаются, но если вы не полагаетесь на эти различия, я бы предложил использовать более общий вариант, когда это возможно.источник
obj => key => ...
можно упростить,(obj, key) => ...
потому что позжеgetX(obj)(key)
можно упростить и егоget(obj, key)
. Напротив, другая функция с карри,(getX, filter = vals => vals) => (objA, objB) => ...
не может быть легко упрощена, по крайней мере, в контексте остальной части кода, как написано.