2016-05-16 13 views
2

Wie kann ich nur map, reduce oder filter oder eine beliebige andere Möglichkeit verwenden, um eine benutzerdefinierte Iteration für ein Array zu erstellen?Funktionelle Methode für benutzerdefinierte Iteration

Sagen wir, ich Karte möchte ein Array zu einem anderen Array, das die Summe von jeweils drei benachbarten Elementen in der Quell Array enthält:

var source = [1, 2, 3, 4, 6, 7, 8] // to [6, 17, 8] 

Oder einen Eimer mit zwei Elementen machen:

var source = [1, 2, 3, 4, 5, 6, 7] // to [[1, 2], [3, 4], [5, 6], [7]] 

Für die zweite habe ich die folgende, aber das sieht nicht sehr funktionell aus, da ich auf das Array nach Index zugreife:

function* pairMap(data) { 
    yield* data.map((item, index) => { 
     if (index > 0) { 
      return [data[index - 1], item]; 
     } 
    }); 
} 

Ich interessiere mich für die funktionale Art, dies zu tun.

+6

Es gibt eine Reihe von Möglichkeiten und das sind zwei sehr verschiedene Probleme. Was hast du probiert? –

+3

Funktional bedeutet nicht nur 'map',' reduce' und 'filter'. Funktional bedeutet, dass es deklarativ ist und wo Funktionen erstklassige Bürger afaik sind. –

+0

@ WillemVanOnsem, das ist meine Frage. Wie macht man das "deklarativ"? – norbertpy

Antwort

11

Lassen Sie sich sagt, dass ich zu einem anderen Array ein Array Karte will, die die Summe von jeweils drei benachbarten Elementen in dem Quell Array enthält:

var source = [1, 2, 3, 4, 6, 7, 8] // to [6, 17, 8] 

Karten erstellen 1: 1-Beziehungen, so dass dieser wouldn‘ t geeignete Verwendung von map. Stattdessen wäre eine reduce oder ("fold") hier besser.

const comp = f=> g=> x=> f (g (x)); 
const len = xs=> xs.length; 
const isEmpty = xs=> len(xs) === 0; 
const concat = xs=> ys=> ys.concat(xs); 

const chunk= n=> xs=> 
    isEmpty (xs) 
    ? [] 
    : concat (chunk (n) (xs.slice(n))) ([xs.slice(0,n)]); 

const reduce = f=> y=> xs=> xs.reduce((y,x)=> f(y)(x), y); 
const map = f=> xs=> xs.map(x=> f(x)); 
const add = x=> y=> y + x; 
const sum = reduce (add) (0); 

var source = [1, 2, 3, 4, 6, 7, 8]; 
comp (map (sum)) (chunk (3)) (source); 
//=> [ 6, 17, 8 ] 

So wie Sie sehen können, wir zuerst die source in Stücke von 3 verwandeln, dann map wir die sum Funktion über jeden Brocken.

Wenn Sie Leute über "deklarativen" Code sprechen hören, ist die letzte Zeile ziemlich klar und macht sich wenig Gedanken über die Implementierung. Wir sagen dem Computer nicht, wie seine Arbeit zu machen. Es gibt keine for/while Schleifen, keine Fremdvariablen oder Iteratoren, keine Logik usw.

"idgaf how, brechen nur source in Gruppen von 3, und dann jeden Teil sum"

// very declaration, wow 
comp (map (sum)) (chunk (3)) (source); 

Oder einen Eimer mit zwei Elementen machen:

var source = [1, 2, 3, 4, 5, 6, 7] // to [[1, 2], [3, 4], [5, 6], [7]] 

Mit dem gleichen Code oben

var source = [1, 2, 3, 4, 5, 6, 7]; 
chunk (2) (source); 
// => [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7 ] ] 

Für die zweite ich folgendes haben, aber das sieht nicht sehr funktional wie ich Array von Index Zugriff bin:

function* pairMap(data) { 
     yield* data.map((item, index) => { 
     if (index > 0) { 
       return [data[index - 1], item]; 
      } 
     }); 
    } 

Mit dem obigen Code können Sie einfach pairMap

const pairMap = f=> comp (map (f)) (chunk (2)); 

var source = [1, 2, 3, 4, 5, 6, 7]; 
pairMap (pair => console.log(pair)) (source); 
// [ 1, 2 ] 
// [ 3, 4 ] 
// [ 5, 6 ] 
// [ 7 ] 
implementieren

Lernen Sie alle Dinge

Die Frage ist "funktional für benutzerdefinierte Iteration". Sie werden bemerken, dass mein Code sorta mit Array.prototype.reduce und Array.prototype.map überbrückt. Lernen, wie man diese auf eigene Faust zu bauen, wurde für mich ein gutes Lerninstrument zu verstehen, dass es funktioniert Aufbau Funktionsschleifen/Iteratoren/Steuerung ist einfach und macht Spaß

const isEmpty = xs=> xs.length === 0 
const head = xs=> xs[0]; 
const tail = xs=> xs.slice(1); 

const reduce = f=> y=> xs=> 
    isEmpty (xs) 
    ? y 
    : reduce (f) (f (y) (head (xs))) (tail (xs)); 

const add = x=> y=> y + x; 
reduce (add) (0) ([1,2,3]); 
//=> 6 

!.

OK, lassen Sie uns sehen, wie wir Karte

const concat = xs=> ys=> ys.concat(xs); 
const append = x=> concat ([x]); 

const map = f=> 
    reduce (ys=> x=> append (f (x)) (ys)) ([]); 

const sq = x => x * x; 
map (sq) ([1,2,3]) 
//=> [ 1, 4, 9 ] 

Quiz 1 tun würde: Sie filter schreiben, some und everyreduce mit?

Troll Warnung: Es gibt viele verschiedene Möglichkeiten, diese Funktionen zu implementieren. Wenn Sie anfangen, rekursive Funktionen zu schreiben, sollten Sie zuerst wissen, was ein tail call ist. ES6 erhält eine Tail-Call-Optimierung, wird aber für eine Weile nicht mehr verbreitet sein. Für eine Weile konnte Babel es mit einer while-Schleife transpilieren, aber es ist in Version 6 vorübergehend deaktiviert und wird zurückkehren, sobald sie es behoben haben.

Quiz 2: Wie können Sie meine reduce mit einem richtigen Tail Call umschreiben?

+0

Das ist JavaScript-Code, ja? Ich habe es gestarrt und versucht zu entziffern, was vor sich geht! Ich bin immer noch nicht an die neue "Pfeilfunktion" -Syntax gewöhnt oder an viele neue ES6-Sachen ... –

+1

Ja, es sind nur ES6-Pfeilfunktionen. Kein anderes ES6-Zeug wird verwendet. Wenn es hilft, können Sie den Code in [http://babeljs.io/repl/](http://babeljs.io/repl/) einfügen, um es in ES5-kompatiblem Code zu sehen. – naomik

+0

Mann, du hast es genagelt. Ich brauche etwas Zeit, um das zu verdauen. Vielen Dank. – norbertpy

2

Zum ersten Teil der Frage kann man reduce und slice zur Gruppe map und reduce alle 3 Elemente von Array verwenden und dann können Sie Summe aus jeder Gruppe zu erhalten verwenden.alle 2 Elemente

var source = [1, 2, 3, 4, 6, 7, 8]; 
 

 
var result = source.reduce((r, elem, i) => { 
 
    if(i % 3 == 0) r.push(source.slice(i, i+3)); 
 
    return r; 
 
}, []); 
 

 
result = result.map(e => {return e.reduce((a, el) => { return a + el })}); 
 
console.log(result)

Und für zweiten Teil der Frage, können Sie wieder reduce mit slice zu Gruppe verwenden.

var source = [1, 2, 3, 4, 5, 6, 7] 
 

 
source = source.reduce((r, e, i) => { 
 
    if(i % 2 == 0) r.push(source.slice(i, i+2)); 
 
    return r; 
 
}, []) 
 

 
console.log(source)

1

erste Aufgabe mit einem reduce

var source = [1, 2, 3, 4, 5, 6, 7]; 
 

 
var r1 = source.reduce((p, c, i, a) => { 
 
    if (i % 2) p[p.length - 1].push(c); 
 
    else p.push([a[i]]); 
 
    return p; 
 
}, []); 
 

 
console.log(JSON.stringify(r1, 0, 2));

Zweite Aufgabe mit reduce und map

var source = [1, 2, 3, 4, 5, 6, 7]; 
 

 
var r2 = source.reduce((p, c, i, a) => { 
 
    if (p[p.length - 1].length < 3) p[p.length - 1].push(c); 
 
    else p.push([a[i]]); 
 
    return p; 
 
}, [[]]).map(e => e.reduce((a, b) => a + b)); 
 

 
console.log(JSON.stringify(r2, 0, 2));

Verwandte Themen