숫자 배열의 합을 구하고 싶다고 해봐요. 이 목표를 이루는 방법은 여러 가지가 있어요.
여러 언어에서는 이 문제를 누산기 변수를 사용한 루프로 표현해요.
이 문제는 재귀 함수로 작성할 수도 있어요. 의사 코드로 나타내면 다음과 같아요.
function Add(X, Sum=0):
if X is empty then
return Sum
else
return Add(rest(X), Sum + first(X))
end
end
문제를 더 작은 조각으로 나누는 이 방법은 "기저 사례를 향해 줄여 나가기"라고 표현할 수도 있어요.
Reduce는 데이터 구조의 모든 원소를 하나의 값으로 결합하는 방법이에요. 이 과정은 데이터 구조를 반복하면서 각 원소에 함수를 적용해 누적된 결과를 갱신해요.
jq에서는 이 과정이 reduce 필터로 구현되어 있어요.
다른 언어에서는 "fold", "fold-left", "inject", "aggregate"라고 부르기도 해요.
jq의 reduce 표현식은 다음과 같아요.
reduce STREAM_EXPRESSION as $var (INITIAL_VALUE; UPDATE_EXPRESSION)
STREAM_EXPRESSION은 항목의 _스트림_이고, 각 항목은 차례로 $var 변수에 저장돼요.
.[]를 사용한다는 걸 기억해요: $myArray | .[].INITIAL_VALUE는 누적된 결과의 시작 값이에요("누산기"라고도 해요).UPDATE_EXPRESSION은 현재 값($var)을 누산기에 결합("fold")해요.
.은 누산기의 값이에요.reduce의 출력이에요.예를 하나 살펴봐요: 배열에 있는 숫자들을 모두 더하는 거예요.
add 필터가 바로 이 일을 하지만, 이걸 어떻게 구현하는지 살펴볼게요.
[10, 20, 30, 40]을 입력으로 사용하고 0을 초기 상태로 두면, 각 단계는 다음과 같아요.
| # | 상태 | 원소 | 리듀서 | 결과 |
|---|---|---|---|---|
| 1 | 0 | 10 | 0 + 10 | 10 |
| 2 | 10 | 20 | 10 + 20 | 30 |
| 3 | 30 | 30 | 30 + 30 | 60 |
| 4 | 60 | 40 | 60 + 40 | 100 |
jq 문법으로 나타내면 이런 코드가 돼요.
0 + 10 | . + 20 | . + 30 | . + 40
이걸 reduce 필터로 표현할 수 있어요.
[10, 20, 30, 40] | reduce .[] as $n (0; . + $n) # => 100
add 내장 함수는 사실 reduce로 구현되어 있지만, 초기 상태로 "null"을 사용해요(어떤 데이터 타입이든 null에 더할 수 있어요).
def add: reduce .[] as $x (null; . + $x);
리듀싱 표현식에서 .은 누산기예요.
리듀싱 함수 안에서 참조해야 하는 객체가 입력으로 들어온다면, 그 객체를 변수에 저장해야 해요.
{"apple": 10, "banana": 16, "carrot": 4}
| . as $obj
| reduce (keys | .[]) as $key (0; . + $obj[$key]) # => 30
누산기는 어떤 데이터 타입이든 될 수 있어요. 예를 들어 배열을 뒤집고 싶을 수도 있어요.
["A", "B", "C", "D"]
| reduce .[] as $elem ([]; [$elem] + .) # => ["D", "C", "B", "A"]
선생님이에요. 학년 말이 되면 학생마다 숫자로 성적을 매겨 두었어요. 이제 그 성적을 알파벳 등급으로 바꾸고, 각 등급을 받은 학생이 몇 명인지 세어야 해요.
letter_grade 함수는 숫자 성적을 입력으로 받아서 해당 등급 문자를 출력해요.
다음 범위를 사용해요:
| 등급 | 점수 |
|---|---|
| A | 90% - 100% |
| B | 80% - 89% |
| C | 70% - 79% |
| D | 60% - 69% |
| F | 0% - 59% |
예시:
75 | letter_grade # => "C"
count_letter_grades 함수는 학생 이름을 성적에 매핑한 객체를 입력으로 받아요.
출력은 각 등급을 그 등급을 받은 학생 수에 매핑한 객체예요.
예시:
{"Joe": 78, "Jane": 93, "Richard": 72} | count_letter_grades
# => {"A": 1, "B": 0, "C": 2, "D": 0, "F": 0}
이 문제를 푸는 방법은 여러 가지가 있어요.
연습 삼아 reduce 필터를 사용해 봐요.