Ich habe die folgende Funktion.

const array = [1, 2, 3];

// Impure function

function addElementToArray(element) {
  array.push(element);
}

Dies ist eine unreine Funktion, da sie das globale Array mutiert. Daher dachte ich, dass die Angabe des gesamten Arrays als Funktionsargument die Funktion rein macht.

function addElement(array, element) {
  array.push(element);
}

Aber ich fand, dass es auch Nebenwirkungen hat.

Also, was wäre der beste Ansatz, um es zu einer reinen Funktion zu machen?

1
Ashik 23 Feb. 2020 im 16:43

3 Antworten

Die einfachste Lösung besteht darin, eine rein funktionale Datenstruktur wie eine Liste zu verwenden.

// empty :: List a -- an empty list of any type
const empty = null;

// cons :: (a, List a) -> List a -- add an element to the list
const cons = (head, tail) => ({ head, tail });

// toArray :: List a -> [a] -- convert the list to an array in reverse
const toArray = list => {
    if (list === empty) return [];
    const array = toArray(list.tail);
    array.push(list.head); // although we use mutation here, yet toArray is pure
    return array;
};

// example :: List Int
const example = cons(3, cons(2, cons(1, empty)));

// [1, 2, 3] :: [Int]
console.log(toArray(example));

Beachten Sie, dass die Funktion toArray zwar eine Mutation verwendet, diese jedoch rein ist, weil:

  1. Es gibt dieselbe Ausgabe für dieselben Eingaben zurück.
  2. Es hat keine Nebenwirkungen.

Ich habe in einer früheren Antwort genau erklärt, was es bedeutet, dass eine Funktion als rein betrachtet wird.

0
Aadit M Shah 24 Feb. 2020 im 03:03

Das Kopieren des Arrays für jede Operation ist unglaublich ineffizient. Alternativ können Sie den unreinen Push-Vorgang bis zur Laufzeit verschieben (in der folgenden Skizze als main bezeichnet) und das Array nur einmal pro Komposition kopieren:

/*** pure ***/

const Defer = thunk => ({get runDefer() {return thunk()}});

const defMap = f => tx =>
  Defer(() => f(tx.runDefer));

const pipe = g => f => x => f(g(x));

const catDef = xs => ys =>
  Defer(() => xs.concat(ys)); // pass [] to copy the array

const pushDef = x => tx =>
  defMap(xs => (xs.push(x), xs)) (tx);

const xs = ["foo", "bar"];

const main = pipe(
  pushDef("baz"))
   (pushDef("bat"))
    (catDef(xs) ([]));

/*** impure ***/

console.log(
  main.runDefer); // runs the side effect within the composition only at runtime

console.log(xs); // no global side effect
2
bob 23 Feb. 2020 im 15:03

Innerhalb des Funktionskörpers können Sie das Eingabearray kopieren und das Element am Ende dieses neuen Arrays anhängen. Sie können dies problemlos mit dem es6-Spread-Operator tun

function addElementIntoArray(a, element) {
  // returning a brand new array
  return [...a, element];
}

Damit mutiert das globale Array nicht und die Funktion ist eine reine Funktion.

3
Ashik 23 Feb. 2020 im 13:43