Ich glaube, ich habe einige / viele / die meisten Grundkonzepte gelernt, die der funktionalen Programmierung in JavaScript zugrunde liegen. Ich habe jedoch Probleme beim spezifischen Lesen von Funktionscode, selbst von Code, den ich geschrieben habe, und frage mich, ob mir jemand Hinweise, Tipps, Best Practices, Terminologie usw. geben kann, die helfen können.
Nehmen Sie den folgenden Code. Ich habe diesen Code geschrieben. Ziel ist es, eine prozentuale Ähnlichkeit zwischen zwei Objekten zwischen say {a:1, b:2, c:3, d:3}
und zuzuweisen {a:1, b:1, e:2, f:2, g:3, h:5}
. Ich habe den Code als Antwort auf diese Frage zu Stack Overflow erstellt . Da ich nicht genau wusste, nach welcher prozentualen Ähnlichkeit das Poster fragte, stellte ich vier verschiedene Arten zur Verfügung:
- der Prozentsatz der Schlüssel im 1. Objekt, der im 2. gefunden werden kann,
- der Prozentsatz der Werte im 1. Objekt, der im 2. gefunden werden kann, einschließlich Duplikate,
- der Prozentsatz der Werte im 1. Objekt, der im 2. Objekt gefunden werden kann, ohne dass Duplikate zulässig sind, und
- Der Prozentsatz der {key: value} -Paare im 1. Objekt, der im 2. Objekt gefunden wird.
Ich begann mit einigermaßen zwingendem Code, erkannte jedoch schnell, dass dies ein Problem war, das für die funktionale Programmierung gut geeignet war. Insbesondere wurde mir klar, dass ich es sein könnte, wenn ich eine oder drei Funktionen für jede der oben genannten vier Strategien extrahieren könnte, die den Typ des Features definieren, das ich vergleichen wollte (z. B. die Schlüssel oder die Werte usw.) in der Lage, den Rest des Codes in wiederholbare Einheiten zu reduzieren (verzeihen Sie das Wortspiel). Sie wissen, es trocken zu halten. Also wechselte ich zur funktionalen Programmierung. Ich bin ziemlich stolz auf das Ergebnis, finde es einigermaßen elegant und verstehe, was ich ganz gut gemacht habe.
Selbst wenn ich den Code selbst geschrieben habe und jeden Teil davon während der Erstellung verstanden habe, bin ich, wenn ich jetzt darauf zurückblicke, mehr als ein wenig verblüfft darüber, wie man eine bestimmte halbe Zeile liest und wie man sie liest "grok", was eine bestimmte halbe Codezeile tatsächlich tut. Ich mache mentale Pfeile, um verschiedene Teile zu verbinden, die sich schnell in ein Durcheinander von Spaghetti verwandeln.
Kann mir jemand sagen, wie ich einige der komplizierteren Codebits auf eine Weise "lesen" kann, die sowohl präzise ist als auch zu meinem Verständnis dessen beiträgt, was ich lese? Ich denke, die Teile, die mich am meisten erreichen, sind diejenigen, die mehrere fette Pfeile hintereinander haben und / oder Teile, die mehrere Klammern hintereinander haben. Auch hier kann ich im Kern die Logik herausfinden, aber (ich hoffe) es gibt einen besseren Weg, um schnell und klar und direkt eine Reihe funktionaler JavaScript-Programmierung "aufzunehmen".
Sie können auch eine beliebige Codezeile von unten oder andere Beispiele verwenden. Wenn Sie jedoch erste Vorschläge von mir wünschen, sind hier einige. Beginnen Sie mit einem relativ einfachen. Gegen Ende des Codes wird dieser als Parameter an eine Funktion übergeben : obj => key => obj[key]
. Wie liest und versteht man das? Ein längeres Beispiel ist eine Vollfunktion von Anfang an : const getXs = (obj, getX) => Object.keys(obj).map(key => getX(obj)(key));
. Der letzte map
Teil bringt mich besonders.
Bitte beachten Sie , an diesem Punkt in der Zeit ist ich nicht die Suche nach Hinweisen auf Haskell oder symbolische abstrakte Notation oder die Grundlagen des currying usw. Was ich bin auf der Suche nach ist englische Sätze , dass ich still Mund kann , während bei einer Codezeile suchen. Wenn Sie Referenzen haben, die genau das ansprechen, großartig, aber ich suche auch nicht nach Antworten, die besagen, dass ich einige grundlegende Lehrbücher lesen sollte. Ich habe das getan und bekomme (zumindest einen erheblichen Teil) der Logik. Beachten Sie auch, dass ich keine erschöpfenden Antworten benötige (obwohl solche Versuche willkommen wären): Selbst kurze Antworten, die eine elegante Möglichkeit bieten, eine bestimmte Zeile ansonsten problematischen Codes zu lesen, wären willkommen.
Ich nehme an, ein Teil dieser Frage lautet: Kann ich Funktionscode sogar linear von links nach rechts und von oben nach unten lesen? Oder ist man ziemlich gezwungen, ein mentales Bild von spaghettiartigen Verkabelungen auf der Codeseite zu erstellen, das entschieden nicht linear ist? Und wenn man das tun muss , müssen wir den Code noch lesen. Wie nehmen wir also linearen Text und verdrahten die Spaghetti?
Alle Tipps wäre dankbar.
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
quelle
Ich habe nicht viel von hochfunktionalen Arbeit in Javascript gemacht (was ich sagen würde , das ist - die meisten Menschen über funktionale Javascript sprechen können unter Verwendung von Karten, Filter und reduziert, aber der Code seine eigenen geordneten Funktionen definiert , das ist etwas fortgeschrittener als das), aber ich habe es in Haskell getan, und ich denke, zumindest ein Teil der Erfahrung wird übersetzt. Ich gebe Ihnen ein paar Hinweise auf Dinge, die ich gelernt habe:
Die Angabe der Funktionstypen ist sehr wichtig. In Haskell müssen Sie nicht angeben, um welchen Funktionstyp es sich handelt. Die Aufnahme des Typs in die Definition erleichtert jedoch das Lesen erheblich. Während Javascript die explizite Eingabe nicht auf die gleiche Weise unterstützt, gibt es keinen Grund, die Typdefinition nicht in einen Kommentar aufzunehmen, z.
Mit ein wenig Übung im Umgang mit solchen Typdefinitionen machen sie die Bedeutung einer Funktion viel klarer.
Das Benennen ist wichtig, vielleicht sogar noch wichtiger als bei der prozeduralen Programmierung. Viele funktionale Programme sind in einem sehr knappen Stil geschrieben, der stark von Konventionen geprägt ist (z. B. ist die Konvention, dass 'xs' eine Liste / ein Array und 'x' ein Element darin ist, sehr verbreitet), aber es sei denn, Sie verstehen diesen Stil leicht würde ich eine ausführlichere Benennung vorschlagen. Wenn Sie sich bestimmte Namen ansehen, die Sie verwendet haben, ist "getX" irgendwie undurchsichtig, und daher hilft "getXs" auch nicht wirklich viel. Ich würde "getXs" so etwas wie "applyToProperties" nennen, und "getX" wäre wahrscheinlich "propertyMapper". "getPctSameXs" wäre dann "ProzentPropertiesSameWith" ("mit").
Eine andere wichtige Sache ist das Schreiben von idiomatischem Code . Ich stelle fest, dass Sie eine Syntax verwenden
a => b => some-expression-involving-a-and-b
, um Curry-Funktionen zu erzeugen. Dies ist interessant und kann in einigen Situationen nützlich sein, aber Sie tun hier nichts, was von Curry-Funktionen profitiert, und es wäre idiomatischer, Javascript zu verwenden, um stattdessen traditionelle Funktionen mit mehreren Argumenten zu verwenden. Auf diese Weise können Sie auf einen Blick leichter erkennen, was los ist. Sie verwenden auchconst name = lambda-expression
, um Funktionen zu definieren, bei denen die Verwendung idiomatischer wärefunction name (args) { ... }
. Ich weiß, dass sie sich semantisch geringfügig unterscheiden, aber wenn Sie sich nicht auf diese Unterschiede verlassen, würde ich vorschlagen, wenn möglich die häufigere Variante zu verwenden.quelle
obj => key => ...
vereinfacht werden,(obj, key) => ...
da spätergetX(obj)(key)
auch vereinfacht werden kannget(obj, key)
. Im Gegensatz dazu kann eine andere Curry-Funktion(getX, filter = vals => vals) => (objA, objB) => ...
zumindest im Zusammenhang mit dem Rest des geschriebenen Codes nicht einfach vereinfacht werden.