JavaScript/함수형 프로그래밍

[JavaScript]함수형 프로그래밍 - 재귀(Recursion)

DevStory 2021. 8. 15.

재귀(recursion) 함수는 자기 자신을 호출하는 함수를 말합니다.

 

for문과 같은 반복문재귀로 변환할 수 있으며, 재귀로 코드를 작성하는 방법이 더 효율적일 수도 있습니다.

 

이번 포스팅에서는 반복문재귀 함수로 변경하는 코드를 기반으로 재귀에 대한 내용을 정리합니다.

 

재귀에 대한 개념 및 이론적인 설명은 하지 않으며, 함수형 프로그래밍과 연관 지어 설명합니다.


배열의 합계를 재귀 함수로 작성

반복문을 사용하여 5부터 0까지의 합계를 반환하는 코드입니다.

var totalValue = function(param) {
  var sum = 0;
  for(var loop = param; loop > 0; loop--) {
    sum += loop;
  }
  return sum;
}

console.log(totalValue(5));
// 15

 

반복문을 재귀 함수로 작성한 코드입니다.

보기 좋은 코드는 아니지만, 코드가 좀 더 심플해졌습니다.

var recurFunc = function(param) {
  return (param > 0) ? (param + recurFunc(param - 1)) : param;
}

console.log(recurFunc(5));
// 15

 

이렇게 반복문을 재귀 함수로 변환하여 코드를 좀 더 심플하게 작성할 수 있습니다.


비동기 프로세스에서 재귀 함수를 사용

아래와 같은 경우는 재귀 함수를 사용하는 것이 좋습니다.

 

api 요청을 하는 코드를 작성한다고 가정합니다.

만약, api 요청을 실패하면 다시 요청을 합니다.

최대 5번 재요청을 합니다.

 

아래 코드는 크롬 Console에서 실행 가능합니다.

// 요청 횟수
var requestMaxCount = 5;

// url
var urlString = "https://jsonplaceholder.typicode.com/posts/1";

var apiReqest = function(url, count) {
  console.log(`*****${count}번째 요청중*****`);
  if(count > 0) {
    fetch(url)
      .then(function(response) {
        console.log(response);
        // 응답은 받았으나 404 error와 같은 에러일 경우 재요청
        if(response.ok === false) {
          apiReqest(url, count - 1);
        }
      })
      // 에러 발생시 재요청
      .catch(function(error) {
        console.log("error:", error);
        apiReqest(url, count - 1);
      });
  }
}

console.log(apiReqest(urlString, 5));

 

만약, url이 잘못되었거나 네트워크 에러가 발생할 경우 재요청을 하게 됩니다.

0번째 요청인 경우 if문 조건에 의해 api 요청을 수행하지 않습니다.

 

아래는 reponse는 받았으나 404에러인 경우입니다.

url이 잘못되었을 경우


데이터 구조를 검색

재귀 함수중첩된 객체의 프로퍼티 값을 추출 또는 접근하는 용도로 사용할 수 있습니다.

 

다음 코드는 중첩된 객체에서 next 프로퍼티의 값을 배열로 반환하는 재귀 함수입니다.

let list = { 
  value: 1,
  next: { 
    value: 2, 
    next: { 
      value: 3, 
      next: { 
        value: 4, 
        next: null 
      } 
    } 
  } 
};

function printList({ value, next }) {
    return [value, ...(next ? printList(next) : [])]
}

console.log(printList(list));

단점도 존재하는 재귀

이러한 재귀 함수는 코드를 간결해주고 비동기 프로세스에서 유용해 보이지만, 단점도 존재합니다.

 

재귀 함수의 호출 횟수가 많아질수록 재귀의 깊이가 깊어집니다.

 

자바스크립트 엔진은 재귀의 깊이가 깊은 경우 정상적으로 최적화 불가능하며, 오류가 발생합니다.

 

이러한 경우 트램폴린(trampoline)스트림(stream) 같은 기법을 사용하여 재귀를 수동으로 최적화할 수 있습니다.

 

자바스크립트 엔진에서 스택의 제한을 없애고자 하는 계획이 있기 때문에 트램폴린스트림은 정리하지 않았습니다.


마무리

이번 포스팅에서는 재귀에 대해 정리하였습니다.

 

정리하자면, 다음과 같습니다.

  • 반복문은 재귀 함수로 변환할 수 있다.
  • 반복문보다는 재귀가 더 유용하다.
  • 단, 재귀의 깊이가 길어질수록 스택의 제한으로 오류가 발생할 가능성이 높다.

 

트램폴린스트림에 대해 정리를 하고자 했으나 자료가 별로 없고 심플한 내용은 아니라서 작성하지 않았습니다.

 

트램폴린에 대해 궁금하신 분들은 아래 링크에서 설명하고 있으니 참고하시면 되겠습니다.

 

https://medium.com/@jooyunghan/js-%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0%EB%A1%9C-%EC%8A%A4%ED%83%9D%EC%98%A4%EB%B2%84%ED%94%8C%EB%A1%9C-%ED%94%BC%ED%95%98%EA%B8%B0-a070ebbe5dc7

 

JS 제너레이터로 스택오버플로 피하기

“모나딕 파서”글에서 만든 파서 같은 형태를 Recursive Descent Parser 라고 한다. 재귀적 문법 구조를 처리하는 파서를 직접 만든다고 했을 때 제일 구현하기 쉬운 방법 중 하나다. 문법에 따라 거의

medium.com

 

반응형

댓글