Heim > Web-Frontend > js-Tutorial > Wandler: Ein leistungsstarkes Funktionskompositionsmuster

Wandler: Ein leistungsstarkes Funktionskompositionsmuster

Barbara Streisand
Freigeben: 2025-01-13 14:28:12
Original
674 Leute haben es durchsucht

Transducer: A powerful function composition pattern

alias:: Transducer: Ein leistungsstarkes Funktionskompositionsmuster
Notizbuch:: Wandler: 一种强大的函数组合模式

Karte & Filter

Die Semantik von Map ist „Mapping“, was bedeutet, dass eine Transformation für alle Elemente in einer Menge einmal durchgeführt wird.

  const list = [1, 2, 3, 4, 5]

  list.map(x => x + 1)
  // [ 2, 3, 4, 5, 6 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  function map(f, xs) {
    const ret = []
    for (let i = 0; i < xs.length; i++) {
      ret.push(f(xs[i]))
    }
    return ret
  }
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  map(x => x + 1, [1, 2, 3, 4, 5])
  // [ 2, 3, 4, 5, 6 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Oben wird absichtlich eine for-Anweisung verwendet, um klar zum Ausdruck zu bringen, dass die Implementierung von Map vom Sammlungstyp abhängt.
Sequentielle Ausführung;
Sofortige Bewertung, nicht faul.
Schauen wir uns den Filter an:

  function filter(f, xs) {
    const ret = []
    for (let i = 0; i < xs.length; i++) {
      if (f(xs[i])) {
        ret.push(xs[i])
      }
    }
    return ret
  }
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  var range = n => [...Array(n).keys()]
Nach dem Login kopieren
Nach dem Login kopieren
  filter(x => x % 2 === 1, range(10))
  // [ 1, 3, 5, 7, 9 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

In ähnlicher Weise hängt die Implementierung von Filter auch vom spezifischen Sammlungstyp ab, und die aktuelle Implementierung erfordert, dass xs ein Array ist.
Wie kann die Karte verschiedene Datentypen unterstützen? Zum Beispiel „Set“, „Map“ und benutzerdefinierte Datentypen.
Es gibt einen herkömmlichen Weg: Er basiert auf der Schnittstelle (Protokoll) der Sammlung.
Unterschiedliche Sprachen haben unterschiedliche Implementierungen. JS bietet in dieser Hinsicht relativ schwache native Unterstützung, aber es ist auch machbar:
Iterieren Sie mit Symbol.iterator.
Verwenden Sie „Object#contractor“, um den Konstruktor zu erhalten.
Wie unterstützen wir also abstrakt verschiedene Datentypen in Push?
Sie imitiert die ramdajs-Bibliothek und kann auf die benutzerdefinierte @@transducer/step-Funktion zurückgreifen.

  function map(f, xs) {
    const ret = new xs.constructor()  // 1. construction
    for (const x of xs) { // 2. iteration
      ret['@@transducer/step'](f(x))  // 3. collection
    }
    return ret
  }
Nach dem Login kopieren
Nach dem Login kopieren
  Array.prototype['@@transducer/step'] = Array.prototype.push
  // [Function: push]
Nach dem Login kopieren
Nach dem Login kopieren
  map(x => x + 1, [1, 2, 3, 4, 5])
  // [ 2, 3, 4, 5, 6 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  Set.prototype['@@transducer/step'] = Set.prototype.add
  // [Function: add]
Nach dem Login kopieren
Nach dem Login kopieren
  map(x => x + 1, new Set([1, 2, 3, 4, 5]))
  // Set (5) {2, 3, 4, 5, 6}
Nach dem Login kopieren
Nach dem Login kopieren

Mit dieser Methode können wir Funktionen wie Map , Filter usw. implementieren, die eher axial sind.
Der Schlüssel besteht darin, Vorgänge wie Konstruktion, Iteration und Sammlung an bestimmte Sammlungsklassen zu delegieren, da nur die Sammlung selbst weiß, wie diese Vorgänge ausgeführt werden.

  function filter(f, xs) {
    const ret = new xs.constructor()
    for (const x of xs) {
      if (f(x)) {
        ret['@@transducer/step'](x)
      }
    }
    return ret
  }
Nach dem Login kopieren
Nach dem Login kopieren
  filter(x => x % 2 === 1, range(10))
  // [ 1, 3, 5, 7, 9 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  filter(x => x > 3, new Set(range(10)))
  // Set (6) {4, 5, 6, 7, 8, 9}
Nach dem Login kopieren
Nach dem Login kopieren

komponieren

Es treten einige Probleme auf, wenn die obige Karte und der obige Filter in Kombination verwendet werden.

  range(10)
    .map(x => x + 1)
    .filter(x => x % 2 === 1)
    .slice(0, 3)
  // [ 1, 3, 5 ]
Nach dem Login kopieren
Nach dem Login kopieren

Obwohl nur 5 Elemente verwendet werden, werden alle Elemente in der Sammlung durchlaufen.
Bei jedem Schritt wird ein Zwischensammlungsobjekt generiert.
Wir verwenden Compose, um diese Logik erneut zu implementieren

  function compose(...fns) {
    return fns.reduceRight((acc, fn) => x => fn(acc(x)), x => x)
  }
Nach dem Login kopieren
Nach dem Login kopieren

Um die Komposition zu unterstützen, implementieren wir Funktionen wie „Karte“ und „Filter“ in Form von „Curry“.

  function curry(f) {
    return (...args) => data => f(...args, data)
  }
Nach dem Login kopieren
Nach dem Login kopieren
  var rmap = curry(map)
  var rfilter = curry(filter)

  function take(n, xs) {
    const ret = new xs.constructor()
    for (const x of xs) {
      if (n <= 0) {
        break
      }
      n--
      ret['@@transducer/step'](x)
    }
    return ret
  }
  var rtake = curry(take)
Nach dem Login kopieren
Nach dem Login kopieren
  take(3, range(10))
  // [ 0, 1, 2 ]
Nach dem Login kopieren
Nach dem Login kopieren
  take(4, new Set(range(10)))
  // Set (4) {0, 1, 2, 3}
Nach dem Login kopieren
Nach dem Login kopieren
  const takeFirst3Odd = compose(
    rtake(3),
    rfilter(x => x % 2 === 1),
    rmap(x => x + 1)
  )

  takeFirst3Odd(range(10))
  // [ 1, 3, 5 ]
Nach dem Login kopieren
Nach dem Login kopieren

Bisher ist unsere Implementierung klar und prägnant im Ausdruck, aber verschwenderisch in der Laufzeit.

Die Form der Funktion

Transformator

Die Kartenfunktion in der Version Curry sieht folgendermaßen aus:

  const map = f => xs => ...
Nach dem Login kopieren
Nach dem Login kopieren

Das heißt, map(x => ...) gibt eine Einzelparameterfunktion zurück.

  const list = [1, 2, 3, 4, 5]

  list.map(x => x + 1)
  // [ 2, 3, 4, 5, 6 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Funktionen mit einem einzigen Parameter können einfach zusammengestellt werden.
Insbesondere sind die Eingaben dieser Funktionen „Daten“, die Ausgabe sind die verarbeiteten Daten und die Funktion ist ein Datentransformator (Transformer).

  function map(f, xs) {
    const ret = []
    for (let i = 0; i < xs.length; i++) {
      ret.push(f(xs[i]))
    }
    return ret
  }
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  map(x => x + 1, [1, 2, 3, 4, 5])
  // [ 2, 3, 4, 5, 6 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  function filter(f, xs) {
    const ret = []
    for (let i = 0; i < xs.length; i++) {
      if (f(xs[i])) {
        ret.push(xs[i])
      }
    }
    return ret
  }
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Transformer ist eine Einzelparameterfunktion, die sich für die Funktionskomposition eignet.

  var range = n => [...Array(n).keys()]
Nach dem Login kopieren
Nach dem Login kopieren

Reduzierer

Ein Reduzierer ist eine Zwei-Parameter-Funktion, die verwendet werden kann, um komplexere Logik auszudrücken.

  filter(x => x % 2 === 1, range(10))
  // [ 1, 3, 5, 7, 9 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Summe

  function map(f, xs) {
    const ret = new xs.constructor()  // 1. construction
    for (const x of xs) { // 2. iteration
      ret['@@transducer/step'](f(x))  // 3. collection
    }
    return ret
  }
Nach dem Login kopieren
Nach dem Login kopieren

Karte

  Array.prototype['@@transducer/step'] = Array.prototype.push
  // [Function: push]
Nach dem Login kopieren
Nach dem Login kopieren
  map(x => x + 1, [1, 2, 3, 4, 5])
  // [ 2, 3, 4, 5, 6 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Filter

  Set.prototype['@@transducer/step'] = Set.prototype.add
  // [Function: add]
Nach dem Login kopieren
Nach dem Login kopieren

nehmen

Wie implementiert man Take? Dies erfordert „reduce“, um eine ähnliche Funktionalität wie „break“ zu haben.

  map(x => x + 1, new Set([1, 2, 3, 4, 5]))
  // Set (5) {2, 3, 4, 5, 6}
Nach dem Login kopieren
Nach dem Login kopieren
  function filter(f, xs) {
    const ret = new xs.constructor()
    for (const x of xs) {
      if (f(x)) {
        ret['@@transducer/step'](x)
      }
    }
    return ret
  }
Nach dem Login kopieren
Nach dem Login kopieren
  filter(x => x % 2 === 1, range(10))
  // [ 1, 3, 5, 7, 9 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Wandler

Endlich treffen wir unseren Protagonisten
Überprüfen Sie zunächst noch einmal die vorherige Kartenimplementierung

  filter(x => x > 3, new Set(range(10)))
  // Set (6) {4, 5, 6, 7, 8, 9}
Nach dem Login kopieren
Nach dem Login kopieren

Wir müssen einen Weg finden, die Logik, die vom oben erwähnten Array (Array) abhängt, zu trennen und in einen Reduzierer zu abstrahieren.

  range(10)
    .map(x => x + 1)
    .filter(x => x % 2 === 1)
    .slice(0, 3)
  // [ 1, 3, 5 ]
Nach dem Login kopieren
Nach dem Login kopieren

Die Konstruktion verschwand, die Iteration verschwand und auch die Sammlung von Elementen verschwand.
Durch einen Reduzierer enthält unsere Karte nur die Logik innerhalb ihrer Zuständigkeiten.
Schauen Sie sich den Filter noch einmal an

  function compose(...fns) {
    return fns.reduceRight((acc, fn) => x => fn(acc(x)), x => x)
  }
Nach dem Login kopieren
Nach dem Login kopieren

Beachten Sie rfilter und den Rückgabetyp von rmap oben:

  function curry(f) {
    return (...args) => data => f(...args, data)
  }
Nach dem Login kopieren
Nach dem Login kopieren

Es handelt sich eigentlich um einen Transfomer, dessen Parameter und Rückgabewerte Reduzierer sind, also ein Transducer.
Transformer ist zusammensetzbar, also ist auch Transducer zusammensetzbar.

  var rmap = curry(map)
  var rfilter = curry(filter)

  function take(n, xs) {
    const ret = new xs.constructor()
    for (const x of xs) {
      if (n <= 0) {
        break
      }
      n--
      ret['@@transducer/step'](x)
    }
    return ret
  }
  var rtake = curry(take)
Nach dem Login kopieren
Nach dem Login kopieren

in & umwandeln

Aber wie benutzt man den Wandler?

  take(3, range(10))
  // [ 0, 1, 2 ]
Nach dem Login kopieren
Nach dem Login kopieren
  take(4, new Set(range(10)))
  // Set (4) {0, 1, 2, 3}
Nach dem Login kopieren
Nach dem Login kopieren

Wir müssen Iteration und Sammlung mithilfe eines Reduzierers implementieren.

  const takeFirst3Odd = compose(
    rtake(3),
    rfilter(x => x % 2 === 1),
    rmap(x => x + 1)
  )

  takeFirst3Odd(range(10))
  // [ 1, 3, 5 ]
Nach dem Login kopieren
Nach dem Login kopieren

Es kann jetzt funktionieren und wir haben auch festgestellt, dass die Iteration „on-demand“ erfolgt. Obwohl die Sammlung 100 Elemente enthält, wurden nur die ersten 10 Elemente iteriert.
Als nächstes kapseln wir die obige Logik in eine Funktion.

  const map = f => xs => ...
Nach dem Login kopieren
Nach dem Login kopieren
  type Transformer = (xs: T) => R
Nach dem Login kopieren

Fließen

Fibonacci-Generator.

Angenommen, wir haben eine Art asynchrone Datensammlung, beispielsweise einen asynchronen unendlichen Fibonacci-Generator.

  data ->> map(...) ->> filter(...) ->> reduce(...) -> result
Nach dem Login kopieren
  function pipe(...fns) {
    return x => fns.reduce((ac, f) => f(ac), x)
  }
Nach dem Login kopieren
  const reduce = (f, init) => xs => xs.reduce(f, init)

  const f = pipe(
    rmap(x => x + 1),
    rfilter(x => x % 2 === 1),
    rtake(5),
    reduce((a, b) => a + b, 0)
  )

  f(range(100))
  // 25
Nach dem Login kopieren

Wir müssen die Funktion „into“ implementieren, die die oben genannten Datenstrukturen unterstützt.
Posten Sie die Array-Version des Codes als Referenz daneben:

  type Transformer = (x: T) => T
Nach dem Login kopieren

Hier ist unser Implementierungscode:

  type Reducer = (ac: R, x: T) => R
Nach dem Login kopieren

Der Sammelvorgang ist derselbe, der Iterationsvorgang ist unterschiedlich.

  // add is an reducer
  const add = (a, b) => a + b
  const sum = xs => xs.reduce(add, 0)

  sum(range(11))
  // 55
Nach dem Login kopieren

Die gleiche Logik gilt für verschiedene Datenstrukturen.

Bestellungen

Wenn Sie aufmerksam sind, stellen Sie möglicherweise fest, dass die Parameterreihenfolge der auf „Curry“ basierenden Compose-Version und der auf Reducer basierenden Version unterschiedlich sind.

Curry-Version

  const list = [1, 2, 3, 4, 5]

  list.map(x => x + 1)
  // [ 2, 3, 4, 5, 6 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  function map(f, xs) {
    const ret = []
    for (let i = 0; i < xs.length; i++) {
      ret.push(f(xs[i]))
    }
    return ret
  }
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Die Ausführung der Funktion erfolgt rechtsassoziativ.

Wandlerversion

  map(x => x + 1, [1, 2, 3, 4, 5])
  // [ 2, 3, 4, 5, 6 ]
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  function filter(f, xs) {
    const ret = []
    for (let i = 0; i < xs.length; i++) {
      if (f(xs[i])) {
        ret.push(xs[i])
      }
    }
    return ret
  }
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Referenz

Wandler kommen
Wandler – Clojure-Referenz

Das obige ist der detaillierte Inhalt vonWandler: Ein leistungsstarkes Funktionskompositionsmuster. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage