2017-06-10 2 views
1

Ich habe ein Array von Arrays eine Ebene tief und müssen die Summe der Längen der verschachtelten Arrays, d.h. Länge tief berechnen.
Der Versuch, eine gute idiomatische Art und Weise zu tun, um es mit Ramda zu tun.
Die aktuelle Lösung, die ich habe, fühlt sich nicht knapp genug. Vermutlich vermisse ich etwas.
Können Sie bitte besser vorschlagen?Ramda Länge tief

const arr = [[1], [2, 3], [4, 5, 6]] 
 

 
const lengthDeep = R.pipe(
 
    R.map(R.prop('length')), 
 
    R.sum 
 
) 
 
console.log(lengthDeep(arr)) // 6
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>

PS: Ich bin Ramda lernen, indem Sie versuchen, es zu täglichen Codierung anzuwenden.

+0

Sie sollten 'map' nicht verwenden, wenn Sie eine Datenstruktur" falten ". – ftor

Antwort

2

Als Erstes können Sie R.length anstelle von R.prop('length') verwenden. Sie könnten auch erwägen, das Array Abflachung, wonach das gewünschte Ergebnis die Länge das heißt:

R.pipe(R.flatten, R.length) 
+0

Das sieht gut aus! Vielen Dank. Der einzige Nachteil könnte die Leistung auf großen Arrays sein. Ich meine, das ist zu viel, um flach zu werden, nur um Länge zu bekommen. –

1

Obwohl dies eine sehr einfache und einfache Frage ist, ich denke, es in der Erleichterung einen interessanten Punkt bringt.

Es gibt drei Vorschläge bisher. Ich überspringe @ ftor's Antwort, da es deinen Kommentar darüber ignoriert, dass dies Teil des Lernens von Ramda ist. Aber ich füge seinen Kommentar zum Falten hinzu.

Hier sind die Lösungen:

const lengthDeep = R.compose(R.sum, R.map(R.length)); 
const lengthDeep = R.compose(R.length, R.flatten); 
const lengthDeep = R.reduce((total, xs) => total + R.length(xs), 0); 

(Beachten Sie, dass ich von pipe zu compose wechselte ich in der Regel compose verwenden, wenn die Funktion auf eine einzelne Zeile passt, aber ich glaube nicht, von pipe und compose als grundlegend. verschiedene Lösungen.)

Diese entsprechen unterschiedlichen Verständnis des Problems.

Version A (R.compose(R.sum, R.map(R.length))) ist die einfachste, und ich glaube, es bildet am ehesten auf die ursprüngliche Darstellung des Problems: Wir wollen „die Summe der Längen der verschachtelten Arrays“ finden. Diese Version ist die einfachste: Sie findet diese Längen und fügt sie dann zusammen. (Dies ist Ihre Version, die mit der Beobachtung von @ trincot erweitert wurde, dass R.length anstelle von R.prop('length') funktioniert.) Dies ist die, die ich wählen würde. Es ist ziemlich einfach und es ist offensichtlich, was es tut.

Version B (R.compose(R.length, R.flatten)) entspricht einer ganz anderen Auffassung des Problems. Es beantwortet die Frage: "Wenn ich all diese Arrays zu einem zusammenfassen würde, wie lange würde es sein?" Soweit ich das beurteilen kann, ist der einzige Vorteil, dass es der einfachste Code ist. Auf der anderen Seite dauert es wahrscheinlich länger, und es erfordert definitiv viel mehr Platz.

Version C (R.reduce((total, xs) => total + R.length(xs), 0)) beinhaltet noch eine andere Vorstellung. Der beste Weg, diese Lösung zu beschreiben, ist eine rekursive Beschreibung. Die tiefe Länge eines leeren Arrays von Arrays ist Null. Die tiefe Länge eines Arrays von Arrays, deren erstes Element die Länge n hat, ist n plus die tiefe Länge des Rests des Array-Arrays. Wenn Sie so über das Problem nachdenken, könnte diese Version für Sie sein. Es gibt ein anderes Mal, dass Sie es verwenden möchten: Obwohl ich nicht getestet habe, würde ich erwarten, dass es leistungsfähiger ist, da es nur einmal über die äußere Liste läuft.Wenn Sie also feststellen, dass diese Funktion ein Engpass in Ihrem Code war (Sie testen die Leistung, bevor Sie eine Leistungsoptimierung einführen, richtig?), Könnten Sie zu diesem Code wechseln, obwohl der Code wesentlich komplexer ist. (Ich weiß nicht, ob es eine vernünftige punktefreie Version davon gibt. Ich kann kein einfaches sehen, und das ist bereits ausreichend lesbar.)

Wieder würde ich Version A wählen, es sei denn etwas wichtiges veranlasste mich, zu Version C zu wechseln. Das scheint jedoch nicht wahrscheinlich.


All dies ist vielleicht ein sehr langwieriger Weg mit @ ftor Kommentar von anderer Meinung: „Man kann nicht map, wenn Sie tatsächlich fold eine Datenstruktur verwendet werden soll.“ Ich würde stattdessen sagen, dass Sie den einfachsten Code verwenden sollten, der Ihrem mentalen Modell des Problems entspricht. Dies muss durch andere Überlegungen wie die Leistung ausgeglichen werden, sollte aber der Standardwert sein. Meine Vorstellung von diesem Problem entspricht absolut dem Modell "nimm alle Längen und füge sie zusammen" hinzu.

+0

Hey Scott, mein Kommentar ist nur meinungsbezogen und keine richtige Antwort wert. Natürlich kann das OP seine eigene Faltung mit einem Zwischenarray zusammenstellen. Alles was ich sagen wollte ist, dass der "beste" Code nicht unbedingt der beste ist. – ftor

+1

@ftor, ich habe nicht angefangen zu denken, um auf Ihren Kommentar zu antworten. Erst als ich den Großteil meiner Antwort geschrieben hatte, wurde mir klar, dass das Ihrem Kommentar widersprach. Ich denke nicht, dass es insgesamt eine schlechte Idee ist, und ich mag Ihre Aufschlüsselung des Problems. Ich stimme absolut zu, dass Knappheit nicht die primäre Tugend für Code ist. –

+0

Vielen Dank für die ausführliche Erklärung. Ich habe nicht so viel Aktivität für diese Frage erwartet. –

1

Hier ist eine andere Art und Weise Sie es mit einem kleinen Helfer mapReduce genannt tun können - wir haben es mit Ramda des umsetzen können curry, so dass es eine magische Curry-Schnittstelle wie andere Rambda Bibliotheksmitglieder teilen.

mapReduce nimmt effektiv eine Zuordnungsfunktion m und eine reduzierende Funktion r und erstellt eine neue reduzierende Funktion. Dies ist eine nützliche generische Funktion, weil es überall eingesetzt werden können Sie wünschen Reduzierungen

Als zusätzlichen Bonus zu generieren, wird diese Lösung iterieren nur durch die Eingabe-Array einmal (die Mindestanforderung, die Antwort zu berechnen)

// mapReduce :: (a -> b) -> ((c, b) -> c) -> ((c, a) -> c) 
const mapReduce = curry ((m, r) => 
    (x, y) => r (x, m (y))) 

// deepLength :: [[a]] -> Integer 
const deepLength = xs => 
    reduce (mapReduce (length, add), 0, xs) 

// arr :: [[Integer]] 
const arr = [[1], [2, 3], [4, 5, 6]] 

console.log (deepLength (arr)) 
// 6 

Um den vielfältigen Nutzen von mapReduce zu demonstrieren, werde ich Ihnen zeigen, wie sie die Dinge handhaben kann, wenn sie etwas komplizierter sind - während noch ein lesbares Programm Aufrechterhaltung

// mapReduce :: (a -> b) -> ((c, b) -> c) -> ((c, a) -> c) 
const mapReduce = curry ((m, r) => 
    (x, y) => r (x, m (y))) 

// omap :: (a -> b) -> {k : a} -> {k : b} 
const omap = curry ((f, o) => 
    reduce (mapReduce (k => ({ [k]: f(o[k]) }), Object.assign), {}, keys(o))) 

console.log (omap (add(10), {a: 1, b: 2, c: 3})) 
// {"a": 11, "b": 12, "c": 13} 
+0

Der Kartenwandler ist hier das richtige Werkzeug. Da dies im zweiten Argument im Wesentlichen eine Funktionszusammensetzung ist, frage ich mich, ob es einen allgemeineren Namen als "mapReduce" oder "mapper" gibt. – ftor

+0

Ich bin auch neugierig, wenn wir den Code mit einer rechten Falte vereinfachen können, wobei der Akkumulator das zweite Argument des Reduzierers ist und wir einfach über den ersten kompilieren können. Dies würde natürlich eine Curry-Lösung erfordern. Ich versuche es mal. – ftor

+0

@ftor, ich denke das ist vielleicht was du beschreibst: [https://repl.it/IgNi/1](https://repl.it/IgNi/1) - ja? – naomik