JavaScript Generator활용해보기

Benza·2022년 12월 13일
0
post-thumbnail

JavaScript Generator란 무엇인가?

JavaScript generator는 언제든지 실행을 일시 중지하고 재개할 수 있는 기능으로, 시간이 지남에 따라 일련의 값을 생성할 수 있습니다.
그러므로 가장 큰 소수 찾기, 미로 통과 하기 같은 수학적 문제를 푸는데 유용합니다.
이 기능을 사용하면 계산을 더 작고 관리하기 쉬운 청크로 분할하여 병렬로 또는 한 번에 하나씩 실행할 수 있습니다. 이는 대량 데이터에대한 머신러닝 알고리즘을 푸는 대량의 데이터를 처리해야 하는 상황에서 유용할 수 있습니다.

Generator 사용법

JavaScript generator를 사용하려면 먼저 function* 구문을 사용하여 생성기 함수를 '정의'해야 합니다. 이 함수는 계산 또는 데이터 처리를 위한 코드를 포함하며, yield 키워드를 사용하여 일시 중지했다가 다시 시작할 수 있습니다.
다음은 간단한 제너레이터 함수의 예입니다.

function* myGenerator() {
  // perform some initial computation
  let result = computeInitialValue();
  
  // yield the first result
  yield result;

  // perform some additional computation
  result = computeNextValue(result);

  // yield the next result
  yield result;

  // continue performing computation and yielding results until finished
  while (result !== finalValue) {
    result = computeNextValue(result);
    yield result;
  }
}

제너레이터 함수를 정의했으면 제너레이터 객체에서 next() 메서드를 호출하여 이 함수를 사용할 수 있습니다.
이렇게 하면 첫 번째 yield 키워드가 발견될 때까지 생성기 함수에서 코드가 실행되며, 이 시점에서 실행을 일시 중지하고 산출된 값(yielded value)을 반환합니다. 그런 다음 next()를 다시 호출하여 일시 중지된 지점에서 실행을 다시 시작하고 다음으로 산출된 값(yielded value)을 생성할 수 있습니다. 다음은 위에서 정의한 myGenerator 기능을 사용하는 방법의 예입니다.

// create a generator object
const generator = myGenerator();

// get the first yielded value
const result1 = generator.next();

// get the second yielded value
const result2 = generator.next();

// continue getting yielded values until the generator is finished
while (!result.done) {
  const result = generator.next();
  // process the yielded value
  processValue(result.value);
}

이와 같은 루프에서 next() 메서드를 호출하면 산출된 값을 한 번에 하나씩 처리할 수 있으므로 자바스크립트 프로그램의 메인 스레드를 차단하지 않고 장기 실행 계산을 수행하거나 대량의 데이터를 처리할 수 있습니다.

generator는 비동기인가?

그렇습니다, javascript genenrator는 본질적으로 비동기적입니다. generator는 일시 중지했다가 다시 시작할 수 있는 특수한 유형의 기능으로, 시간이 지남에 따라 여러 값을 생성할 수 있습니다.
이를 통해 promise이나 async/await로 작업할 때와 같은 비동기 프로그래밍에 generator를 사용할 수 있습니다.

async function generateValues() {
  yield 1;
  yield 2;
  yield 3;
}

async function consumeValues() {
  const generator = generateValues();
  const firstValue = await generator.next(); // { value: 1, done: false }
  const secondValue = await generator.next(); // { value: 2, done: false }
  const thirdValue = await generator.next(); // { value: 3, done: false }
}

위의 코드에서 consumeValues() 함수는 await 키워드를 사용하여 실행을 일시 중지하고 generator가 각 값을 반환할 때까지 기다립니다. 이는 asynchronous이고 non-blocking적인 상태에서 코드를 더 synchronous으로 보이는 스타일로 작성할 수 있게 해줍니다.

재귀함수 구현하기

generator를 사용하여 재귀 함수를 생성하려면 yield* 키워드를 사용하여 generator 함수 내에서 재귀적으로 generator 함수를 호출하는 방식으로 구현할 수 있습니다.
이를 통해 generator 함수는 각 재귀 호출에서 실행을 일시 중지하고 다시 시작할 수 있으며, 시간이 지남에 따라 일련의 값을 생성할 수 있습니다.
다음은 재귀 함수를 구현하는 방법의 예입니다.

function* myGenerator(n) {
  if (n <= 0) {
    // base case: return the initial value
    return 0;
  } else {
    // recursive case: yield the current value and call the generator recursively
    yield n;
    yield* myGenerator(n - 1);
  }
}

이 예제에서, myGenerator 함수 작업은 argument로 받은 n부터 0까지 값을 생성하는 계산을 재귀적으로 수행합니다. 이 함수를 사용하려면 next() 메서드를 호출하여 한 번에 하나씩 값을 return 받아 처리할 수 있습니다.
다음은 myGenerator 기능을 사용하는 방법의 예입니다.

// continue getting yielded values until the generator is finished
for (const result of myGenerator(5)) {
  doSomethingWithResult(result);
}

이러한 방식으로 JavaScript 생성기를 사용하면 언제든지 실행을 일시 중지했다가 다시 시작할 수 있는 방식으로 재귀 계산을 수행할 수 있으므로 오래 실행되거나 복잡한 계산을 더 쉽게 관리할 수 있습니다.


아래의 DFS/BFS 코드는 각 노드와 이웃 노드가 iterable하다고(예: 배열) 가정합니다.

DFS 구현하기

function* dfs(node, visited = new Set()) {
  // Mark the current node as visited
  visited.add(node);

  // Yield the current node
  yield node;

  // For each of the unvisited neighbors of the current node,
  // run DFS on the neighbor
  for (const neighbor of node.neighbors) {
    if (!visited.has(neighbor)) {
      yield* dfs(neighbor, visited);
    }
  }
}

위 DFS 예제는 그래프의 각 노드가 인접 노드에서 방문할 수 있는 인접 노드 속성을 가지고 있다고 가정합니다.

visitedset으로 이미 방문한 노드를 추적하는 데 사용되며 방문한 노드를 다시 방문하지 않도록 합니다.

dfs generator 함수를 사용하기 위해서 generator 함수를 호출하고 반복되어 생성되는 값을 받을 수 있습니다.

const startingNode = getStartingNode();

for (const node of dfs(startingNode)) {
  // Do something with each visited node
}

BFS 구현하기

DFS를 구현한 것처럼 BFS도 Set을 활용한 visited로 이미 방문한 노드를 추적하는 데 사용되므로 노드를 다시 방문하지 않습니다.

function* bfs(node, visited = new Set()) {
  // Create a queue to store the nodes we need to visit
  const queue = [node];

  // While there are nodes in the queue, visit the next one
  while (queue.length > 0) {
    // Get the next node in the queue
    const current = queue.shift();

    // Mark the current node as visited
    visited.add(current);

    // Yield the current node
    yield current;

    // Add the unvisited neighbors of the current node to the queue
    for (const neighbor of current.neighbors) {
      if (!visited.has(neighbor)) {
        queue.push(neighbor);
      }
    }
  }
}
const startingNode = getStartingNode();

for (const node of bfs(startingNode)) {
  // Do something with each visited node
}
profile
Understanding the impression

0개의 댓글