본문 바로가기
DEVELOPMENT/BOOK

실용주의 프로그래머 TIL(22.03.28)

by Z@__ 2022. 3. 28.
반응형

오늘의 책 읽기

  • 연습문제 풀고 복습하기

 

오늘의 TIL

연습 문제 23
"x 언어에는 파이프라인이 없는데요"에서 우리는 다음과 같은 코드를 썼다.

아래 가지 코드의 차이는 무엇인가? 여러분이 보기에 우리는 어느 쪽을 선호할 것 같은가?
const content = File.read(file_name);
const lines = find_matching_lines(content, pattern);
const result = truncate_lines(lines);
const result = content_of(file_name)
                .find_matching_lines(pattern)
                .truncate_lines();

 

해설

const content = File.read(file_name);
const no_comments = remove_comments(content);
const line = find_matching_lines(no_comments, pattern);
const result = truncate_lines(lines);
  • 해설에서는 content_of에서 반환하는 객체가 find_matching_lines 메서드를 구현해야 해서 결합이 높다고 설명
    요구 사항이 바뀌어 #문자로 시작하는 줄은 주석이므로 무시해야 할 경우를 가정해보자
  • 이렇게 구현하면 remove_comments와 find_matching_lines 함수의 호출이 바꾸어도 잘 동작
  • 하지만 호출연쇄 방식에서는 더 까다롭다. 
  • content_of가 반환하는 객체일까 아니면 matching_lines가 반환하는 객체일까?
  • 우리가 객체들을 바꾸면 또 다른 코드가 망가지지 않을까? 이런 난감한 결합 때문에 호출 연쇄 방식이 '열차 사고'라는 평가를 받게 됨.

내 생각


나는 아래와 같이 작성했을 때의 위 코드와의 차이점을 잘 모르겠다. 
아래의 코드도 remove_comments와 find_matching_lines를 바꾸어서 호출한다고 하더라도 잘 동작하는 게 맞지 않나 싶다.

위의 코드는 코드를 해석할 때는

1. File.read로 읽어온 데이터를 content라 하고
2. 그 컨텐츠에서 코멘트를 지운 데이터를 no_comments라는 데이터라함
3. 그리고 no_comments를 특정한 패턴에 따라 매칭되는 데이터를 line에 담고
4.  truncate(?)라는 걸 해서 결과로 반환한다고 해석

 

 

이렇게 좀 복잡하고 길어보이지만 아래의 코드를 해석할 때는 

const result = content_of(file_name)
                .remove_comments(contents)
                .find_matching_lines(pattern)
                .truncate_lines();
1. file_name으로 데이터를 가져와서
2. comment를 지우고
3. 패턴에 맞는 라인만 뽑아서 
4. truncate(?)한 결과를 반환

라고 훨씬 짧고 명확하게 해석해야 할 수 있다. 

 

 

 

또한 각 절차별로 변수를 선언해야하고 해당 변수명을 짓는 것에 대해 훨씬 더 많은 고민이 들어갈 것 같다.

 

 

그래서 내 생각과 책의 해석이 달라 함수형 프로그래밍과 파이프에 대해 조금 정리해보려고 한다.



자바스크립트에서의 파이프란?

"파이프"는 주로 배관들을 A에서 B지점으로 연결하여 무언가를 이동시킬 수 있는 터널과 같은 개념
파이프 함수는 단방향 통신을 위한 용도로 사용되고, 하나의 파이프는 그 이전의 파이프에서 전달된 결과를 파라미터로 삼아 새로운 결과를 반환

 

 

순수함수란?

- 같은 입력 값에 따라 항상 같은 반환 값을 보장한다.
- 함수 외부 스코프의 그 어떠한 변수의 값도 바꾸지 않는다.


파이프에서는 순수함수가 중요한데,한 파이프가 반환하는 값은 다음 파이프의 입력값으로 전달되기 때문
파이프가 상황에 따라 다른 값을 반환하면 그 다음 파이프의 입장에서는 상황에 따라 바뀌는 불안정한 값이 입력으로 들어오기 때문에 불안정하게 됨.

 

 

 


파이프를 사용하지 않은 상태에서는 어떤 숫자에 4를 더하고 5를 빼기 위해선

const addFour = a => a + 4;
const minusFive = a => a - 5;

const initNumber = 3
const addedNumber = addFour(initNumber)
const result = minusFive(addedNumber)

혹은

const addFour = a => a + 4;
const minusFive = a => a - 5;

const initNumber = 3
const result = minusFive(addFour(initNumber))


이렇게 작성해야 하지만

const pipe = (funcs, v) => {
  return funcs.reduce((res, func) => {
  return func(res);
  }, v);
};

console.log(pipe([
  addFour, 
  minusFive
], initNumber));


이렇게 정리할 수 있다.

 

하지만 이런 파이프는 배열밖에 받지 못해 단일 함수로 사용할 수 없다
// pipe(addFive, 5) // error

 

파이프가 단일 함수 역시도 인자로 받을 수 있도록 Rest파라미터를 이용하고
함수와 값의 전달 위치를 변경하기 위해 클로져를 이용한 파이프는 아래와 같다

 

const pipe = (...funcs) => v => {
  return funcs.reduce((res, func) => {
    return func(res);
  }, v);
};

const result = pipe(
  minusFive,
  addFour,
  Math.abs
) (initNumber);

 

 

그래서 결론적으로 순수함수를 잘 유지하면 파이프를 쓰는 게 훨씬 읽기도 좋고, 쓰기도 편하지 않나 싶다.

 

 

참고링크: 함수형프로그래밍(Pipe)

 

반응형

댓글