Wierzę, że nauczyłem się niektórych / wielu / większości podstawowych pojęć leżących u podstaw programowania funkcjonalnego w JavaScript. Mam jednak problemy z odczytaniem kodu funkcjonalnego, nawet kodu, który napisałem, i zastanawiam się, czy ktoś może dać mi jakieś wskazówki, porady, najlepsze praktyki, terminologię itp., Które mogą pomóc.
Weź poniższy kod. Napisałem ten kod. Ma na celu przypisanie procentowego podobieństwa między dwoma obiektami, między powiedzmy {a:1, b:2, c:3, d:3}
i {a:1, b:1, e:2, f:2, g:3, h:5}
. Kod powstał w odpowiedzi na to pytanie dotyczące przepełnienia stosu . Ponieważ nie byłem pewien, o jaki procent podobieństwa pytał plakat, podałem cztery różne rodzaje:
- procent kluczy w 1. obiekcie, który można znaleźć w 2.,
- procent wartości w pierwszym obiekcie, który można znaleźć w drugim, w tym duplikaty,
- procent wartości w pierwszym obiekcie, który można znaleźć w drugim, bez dozwolonych duplikatów, oraz
- procent par {klucz: wartość} w 1. obiekcie, który można znaleźć w 2. obiekcie.
Zacząłem od rozsądnego kodu, ale szybko zdałem sobie sprawę, że jest to problem dobrze dostosowany do programowania funkcjonalnego. W szczególności zdałem sobie sprawę, że jeśli uda mi się wyodrębnić funkcję lub trzy dla każdej z czterech powyższych strategii, które określają rodzaj funkcji, którą chciałem porównać (np. Klucze lub wartości itp.), To mogę być w stanie zredukować (ułaskawić grę słów) resztę kodu do powtarzalnych jednostek. Wiesz, utrzymując to na sucho. Więc przeszedłem do programowania funkcjonalnego. Jestem bardzo dumny z wyniku, myślę, że jest dość elegancki i myślę, że rozumiem, co zrobiłem całkiem dobrze.
Jednak nawet po napisaniu kodu i zrozumieniu każdej jego części podczas budowy, kiedy teraz na niego patrzę, nadal jestem nieco zaskoczony zarówno tym, jak czytać poszczególne półtony, jak i jak „grok”, co właściwie robi konkretna półwiersz kodu. Robię mentalne strzały, by łączyć różne części, które szybko rozkładają się w bałagan spaghetti.
Czy ktoś może mi więc powiedzieć, jak „odczytać” bardziej skomplikowane fragmenty kodu w sposób zwięzły i przyczyniający się do zrozumienia tego, co czytam? Sądzę, że części, które mnie najbardziej doceniają, to te, które mają kilka grubych strzałek z rzędu i / lub części, które mają kilka nawiasów z rzędu. Ponownie, u ich podstaw, w końcu mogę zrozumieć logikę, ale (mam nadzieję) istnieje lepszy sposób na szybkie i jasne i bezpośrednie „przyjęcie” szeregu funkcjonalnych programów JavaScript.
Możesz użyć dowolnej linii kodu od dołu, a nawet innych przykładów. Jeśli jednak chcesz ode mnie kilku wstępnych sugestii, oto kilka. Zacznij od dość prostej. Od blisko końca kodu, jest to, że jest przekazywana jako parametr do funkcji: obj => key => obj[key]
. Jak to przeczytać i zrozumieć? Dłuższy przykładem jest jeden pełny funkcji od blisko początku: const getXs = (obj, getX) => Object.keys(obj).map(key => getX(obj)(key));
. Ostatnia map
część mnie szczególnie.
Należy pamiętać, że w tym momencie ja nie szukają odniesień do Haskell lub symbolicznej abstrakcyjnej notacji lub podstaw zmiękczania itp Co ja jestem szukasz jest zdania po angielsku, że mogę cicho usta patrząc na linię kodu. Jeśli masz referencje, które dokładnie to dotyczą, świetnie, ale nie szukam również odpowiedzi, które mówią, że powinienem przeczytać kilka podstawowych podręczników. Zrobiłem to i dostaję (przynajmniej znaczną część) logikę. Zauważ też, że nie potrzebuję wyczerpujących odpowiedzi (chociaż takie próby byłyby mile widziane): mile widziane byłyby nawet krótkie odpowiedzi, które zapewniają elegancki sposób odczytu pojedynczej linii w innym przypadku kłopotliwego kodu.
Przypuszczam, że część tego pytania brzmi: czy mogę nawet czytać kod funkcjonalny liniowo, od lewej do prawej i od góry do dołu? A może ktoś jest zmuszony stworzyć na stronie kodu obraz przypominający spaghetti, który nie jest liniowy? A jeśli trzeba to zrobić, wciąż musimy przeczytać kod, więc jak wziąć liniowy tekst i połączyć spaghetti?
Wszelkie wskazówki będą mile widziane.
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