기능 코드가 스택 안전하다고 생각할 때

재귀는 기능적 기본 요소이므로 피하려고 합니다. 궁극적으로 재귀는 위장된 불쾌한 명령형 루프일 뿐입니다. FP에서는 일반적으로 접기를 사용하고 접기가 충분히 표현되지 않는 경우에만 재귀에 의존합니다.

Javascript에서는 스택 안전을 추가로 처리해야 합니다. 따라서 각 유형에 적합한 특정 트램폴린으로 폴드를 구현하는 것이 현명한 전략입니다.

// Foldable

const arrFold = f => init => xs => {
  let acc = init;

  for (let i = 0; i < xs.length; i++) // trampoline
    acc = f(acc) (xs[i], i);

  return acc;
};

// identity

const id = x => x;

// function composition

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

const compn = arrFold(comp) (id); // variadic

// MAIN

const inc = x => x + 1;

compn([inc, inc, inc, inc, inc]) (0); // 5

run code
arrFold가 스택 안전 트램폴린으로 구현되어 안전하다고 생각할 수 있습니다. 그러나 당신은 아닙니다:

// MAIN

const inc = x => x + 1;

const xs = Array(1e5).fill(inc);

const foo = compn(xs); // still okay

foo(0); // stack overflow

run code

합성은 두 함수를 결합하여 새 함수에 대한 설명을 결합하는 것을 의미하며, 이는 필수 인수가 제공되는 경우에만 평가됩니다. 따라서 반복적으로 작성하면 실행 대기 중인 설명에 대한 방대한 설명이 작성됩니다.

우리는 그것에 대해 무엇을 할 수 있습니까? 구성을 분리할 방법이 필요합니다. 우리는 이미 트램폴린을 사용했습니다. 적절한 도구인 것 같습니다.

// trampoline for deferred function call trees

const postRec = f => (...args) => {
  let step = f(...args);

  while (step.tag !== "Base")
    step = f(...step.args);

  return init => {
    let {f, x} = step.x(init);

    while (step = f(x)) {
      if (step && step.tag === "Call") {
        step = step.f(step.x);

        if (step && step.tag === "Call") {
          ({f, x} = step);
          continue;
        }

        else break;
      }

      else break;
    }

    return step;
  }
};

const Base = x =>
  ({tag: "Base", x});

const Call = f => x =>
  ({tag: "Call", f, x});

const Step = (...args) =>
  ({tag: "Step", args});

// function composition

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

const compn = xs => // variadic
  postRec((i, acc) =>
    i === xs.length
      ? Base(acc)
      : Step(i + 1, Call(comp(acc) (xs[i]))))
        (0, Call(id));

// MAIN

const inc = x => x + 1;

const xs = Array(1e5).fill(inc);

compn(xs) (0); // 100000

run code
postRec 미인이 아니다. 그것은 모든 추악한 작동 의미를 드러냅니다. Javascript는 아름다움에 관한 것이 아니라 일을 끝내기 위한 것이었습니다.

어쨌든 FP에서 우리는 종종 거대한 지연 함수 호출 트리를 생성하는 계산에 대한 설명을 처리해야 합니다. 특수 트램폴린을 마음대로 사용할 수 있으므로 JS에서 FP에 대해 진지하게 생각할 수 있습니다.

JS의 FP에 대해 더 알고 싶다면 Github에서 제 강의를 살펴보세요.

좋은 웹페이지 즐겨찾기