일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- SCSS extend
- 리액트
- 프로그래머스 데브코스
- nextjs사용법
- intersection opserver
- 이벤트 수식어
- 리스트 렌더링
- 프로그래머스 데브코스 프론트엔드
- Spacer
- git 같은계정 다른 컴퓨터
- vue mixin
- SCSS forward
- 다른컴퓨터에서 git사용
- 고양이 사진 검색기
- vue 지역 컴포넌트
- flex
- postcss
- Vue
- KDT 프로그래머스 데브코스 프론트엔드
- 프로그래머스 프론트엔드 데브코스
- 폼 입력 바인딩
- SCSS import
- 쌓임맥락
- 프로그래머스 K_Digital Training
- SCSS use
- vue 이벤트 수신
- react next
- netlify redirect
- KDT 프로그래머스
- vuex map
- Today
- Total
혼자 적어보는 노트
프로그래머스 데브코스 TIL - Day 7 본문
✅ 학습 목차
- [Day 7] JavaScript 주요 문법 (7)
- 함수형 프로그래밍과 ES6+ (1)
- 함수형 프로그래밍과 ES6+ (2)
✅ 새롭게 학습한 부분
- 백트래킹
- 동적계획법
- 제너레이터
- 함수형 프로그래밍 / map, filter, reduce 직접 만들기
백트래킹
- 모든 가능한 경우의 수 중에서 특정한 조건을 만족하는 경우만 살펴보는 것.
- DFS나 BFS를 이용할 수 있다.
- 효율을 위해 탐색하지 않아도 되는 곳을 미리 막는 것을 가지치기라고 한다.
- 자바스크립트는 재귀 효율이 나쁘기 때문에 DFS를 규현할 경우 스택을 이용하는 것이 좋다.
- 코딩 테스트에선 이를 고려하여 재귀로 작성해도 풀 수 있도록 문제를 제출하는 경우도 있다.
- 탐색에서 순환이 발생할 수 있다면 BFS를 이용하는 것이 편하다.
동적계획법
- 작은문제로 큰 문제를 해결하는 문제풀이 방식
- 처음 진행되는 연산은 기록해 두고, 이미 진행했던 연산이라면 다시 연산하는 것이 아니라
기록되어 있는 값을 가져오는 것
- 그리디나 백트래킹처럼 특정 알고리즘이 아닌 문제 해결 방식을 의미
- Dynamic programing이라고도 부른다
- 메모리를 많이 사용하는 대신 빠른 성능을 자랑한다.
제너레이터
: 이터레이터를 리턴하는 함수
function* gen() {
yield 1;
yield 2;
yield 3;
return 5;
}
const iterator = gen();
console.log(iterator[Symbol.iterator]() == iterator); // true
iterator.next();
iterator.next();
for (const a of iterator) console.log(a); // 3
이전의 iterator처럼 next()와 for..of 사용이 가능하다.
map함수 직접 만들기
const people = [
{ name: "Jay", age: 29 },
{ name: "Hey", age: 24 },
];
const map = (f, items) => {
let res = [];
for (const x of items) {
res.push(f(x));
}
return res;
};
console.log(map((p) => p.name, people));
// ['Jay', 'Hey']
이터러블 프로토콜을 따른 map의 다형성
const map = (f, iter) => {
let res = [];
for (const x of iter) {
res.push(f(x));
}
return res;
};
let m = new Map();
m.set("a", 10);
m.set("b", 20);
console.log(m); // Map(2) {'a' => 10, 'b' => 20}
console.log(new Map(map(([k, a]) => [k, a * 2], m))); // Map(2) {'a' => 20, 'b' => 40}
맵 안의 내용을 변경하여 새로운 map을 만들 수 있다.
filter 함수 직접 만들기
const people = [
{ name: "Jay", age: 29 },
{ name: "Hey", age: 24 },
];
const filter = (f, iter) => {
let res = [];
for (const x of iter) {
if (f(x)) res.push(x);
}
return res;
};
console.log(filter((p) => p.age > 25, people));
// [{name: 'Jay', age: 29}]
console.log(filter((p) => p > 2, [1, 2, 3, 4]));
// 3, 4
reduce 함수 직접 만들기
const reduce = (f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const x of iter) {
acc = f(acc, x);
}
return acc;
};
console.log(reduce((a, b) => a + b, 0, [1, 2, 3, 4])); // 10
console.log(reduce((a, b) => a + b, [1, 2, 3, 4])); // 10
map, filter, reduce 중첩사용
console.log(
reduce(
(acc, v) => acc + v, 0,
map(
(x) => x.age,
filter((p) => p.age > 21, people)
)
)
);
오른쪽에서 왼쪽으로 값을 평가해 나가면서 중첩적으로 처리할 수 있다.
go를 사용하여 가독성을 높인 중첩
const go = (...args) => reduce((a, f) => f(a), args);
go(
people,
(people) => filter((p) => p.age > 22, people),
(people) => map((p) => p.age, people),
(people) => reduce((a, b) => a + b, people),
console.log
); // 53
go와 pipe 함수 사용
const go = (...args) => reduce((a, f) => f(a), args);
const pipe = (...fs) => (a) => go(a, ...fs);
go(
0,
(a) => a + 1,
(a) => a + 2,
(a) => a + 3,
console.log
); // 6
//go는 reduce의 return값을 받는다.
// reduce의 iter값으로는 args가 전달된다,
const f = pipe(
(a) => a + 1,
(a) => a + 2,
(a) => a + 3
);
console.log(f(0)); // 6
curry 를 사용하여 코드 간소화
const curry =
(f) =>
(a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
// 받아둔 함수를 원하는 시점에 평가를 하는 함수
// 함수를 받고 함수를 리턴, 인자를 받아서 원하는 갯수 만큼의 인자가 들어왔을 때 나중에 평가시키는 함수
const mult = curry((a, b) => a * b);
console.log(mult(1)); // 인자를 하나만 전달하면 함수가 리턴,
console.log(mult(4)(2)); // 8
인자를 하나만 적고 즉시실행을 통해 2번째 인자를 보낼 수 있다.
const mult2 = mult(4); // 미리 인자를 4를 넣어놓고 이후에 나머지 인자를 추가할 수 있음
console.log(mult2(2)); // 8
console.log(mult2(5)); // 20
console.log(mult2(10)); // 40
map, filter, reduce에 curry 감싸기
const curry = (f) => (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
const map = curry((f, iter) => {
let res = [];
for (const x of iter) {
res.push(f(x));
}
return res;
});
const filter = curry((f, iter) => {
let res = [];
for (const x of iter) {
if (f(x)) res.push(x);
}
return res;
});
const reduce = curry((f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const x of iter) {
acc = f(acc, x);
}
return acc;
});
go로 구현했던 함수를 조금 더 간소화 시킬 수 있다.
go(
people,
(people) => filter((p) => p.age > 22, people),
(people) => map((p) => p.age, people),
(people) => reduce((a, b) => a + b, people),
console.log
);
// curry로 감싸면 아래와 같이 처리할 수 있다.
go(
people,
(people) => filter((p) => p.age > 22)(people),
(people) => map((p) => p.age)(people),
(people) => reduce((a, b) => a + b)(people),
console.log
);
// 인자를 받아서 그대로 전달하는 것은 생략이 가능하는 말이다.
go(
people,
filter((p) => p.age > 22),
map((p) => p.age),
reduce((a, b) => a + b),
console.log
);
// 위의 3개의 함수들은 인자를 하나 받아놓고. 이후 인자를 받는 것을 기다리는 함수를 리턴함
go, pipe, curry로 중복되는 코드 수정
go(
people,
filter((p) => p.age > 22),
map((p) => p.age),
reduce((a, b) => a + b),
console.log
);
go(
people,
filter((p) => p.age <= 22),
map((p) => p.age),
reduce((a, b) => a + b),
console.log
);
filter의 조건만 다른 2개의 함수의 중복되는 코드에
pipe를 사용하여 아래와 같이 중복을 줄일 수 있다 (curry는 이미 적용되어 있음)
const total_age = pipe(
map((p) => p.age),
reduce((a, b) => a + b)
);
const base_total_age = predi => pipe(
filter(predi),
total_age
)
go(
people,
base_total_age((p) => p.age > 22),
console.log
);
go(
people,
base_total_age((p) => p.age <= 22),
console.log
);
✍ 느낀 점
함수형 프로그래밍에서 함수와 함께 사고를 하는 것이 굉장히 어렵다고 느껴졌다.
똑같이 따라서 쳐 보는데도 불구하고 함수를 리턴하고
그 값을 받아서 또 리턴을 하니, 값의 흐름을 읽기가 어려웠다 😢
하지만 이해가 되는 부분들은 신기하기도 하고.. 잘 익힌다면 여러방면에서 분명히 도움이 될 것 같다.
아직은 익숙하지는 않지만 꼭 익숙해져서 잘 이해해보고싶다.
'스터디' 카테고리의 다른 글
프로그래머스 데브코스 TIL - Day 9 (0) | 2022.04.01 |
---|---|
프로그래머스 데브코스 TIL - Day 8 (0) | 2022.03.30 |
프로그래머스 데브코스 TIL - Day 6 (0) | 2022.03.28 |
프로그래머스 데브코스 TIL - 1주차 보충 (0) | 2022.03.26 |
프로그래머스 데브코스 TIL - Day 5 (0) | 2022.03.25 |