Я вважаю, що я вивчив деякі / багато / більшість основних понять, що лежать в основі функціонального програмування в JavaScript. Однак у мене виникають проблеми з читанням функціонального коду, навіть кодом, який я написав, і цікаво, чи може хтось дати мені якісь вказівки, поради, найкращі практики, термінологія тощо.
Візьміть код нижче. Я написав цей код. Він спрямований на визначення відсоткової схожості між двома об'єктами, між сказати {a:1, b:2, c:3, d:3}і {a:1, b:1, e:2, f:2, g:3, h:5}. Я створив код у відповідь на це запитання на переповнення стека . Оскільки я не був точно впевнений, про яку відсоткову схожість запитував плакат, я надав чотири різні види:
- відсоток ключів у 1-му об'єкті, який можна знайти у другому,
- відсоток значень першого об'єкта, який можна знайти у другому, включаючи дублікати,
- відсоток значень у 1-му об'єкті, який можна знайти у 2-му, без дублікатів, і
- відсотків пар {key: value} пар у 1-му об'єкті, який можна знайти у 2-му об’єкті.
Я почав з досить імперативного коду, але швидко зрозумів, що це проблема, яка добре підходить для функціонального програмування. Зокрема, я зрозумів, що якщо я можу витягнути функцію або три для кожної з перерахованих вище чотирьох стратегій, які визначали тип функції, яку я прагну порівняти (наприклад, ключі або значення тощо), то я можу бути в змозі скоротити (помилуйте гру на словах) решту коду на повторювані одиниці. Ви знаєте, зберігаючи це СУХО. Тому я перейшов на функціональне програмування. Я дуже пишаюся результатом, думаю, що це досить елегантно, і я думаю, що розумію, що зробив досить добре.
Однак, навіть написавши код сам і розуміючи кожну його частину під час будівництва, коли я зараз оглядаюсь на нього, я продовжую бути трохи збентежений як читати будь-який конкретний пів рядка, так і як "grok", що насправді робить будь-який конкретний піврядковий код. Мені здається, що я створюю психічні стрілки для з'єднання різних частин, які швидко розпадаються на безлад спагетті.
Отже, чи може хто-небудь підказати мені, як "прочитати" деякі більш складні біти коду таким чином, щоб бути і стислим, і це сприяє моєму розумінню того, що я читаю? Я здогадуюсь, що частин, які мене найбільше отримують, - це ті, у яких кілька жирових стрілок поспіль та / або частини, які мають кілька дужок підряд. Знову ж таки, в їх основі, я можу врешті-решт з'ясувати логіку, але (сподіваюсь), є кращий спосіб швидко та зрозуміло, і безпосередньо "перейняти" ряд функціональних програм програмування 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