오늘의 책 읽기
- 연습문제 풀고 복습하기
오늘의 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)
'DEVELOPMENT > BOOK' 카테고리의 다른 글
실용주의 프로그래머 TIL(22.04.02) (0) | 2022.04.03 |
---|---|
실용주의 프로그래머 TIL(22.03.30) (0) | 2022.03.30 |
실용주의 프로그래머 TIL(22.03.26) (2) | 2022.03.27 |
실용주의 프로그래머 TIL(22.03.24) (0) | 2022.03.25 |
실용주의 프로그래머 TIL(22.03.21) (0) | 2022.03.21 |
댓글