[개념잡기] 함수형 프로그래밍 - (4) curry, curryr
서론
함수와 인자를 다루는 기법인 curry
와 curryr
을 구현해보자
함수형 프로그래밍
기본 코드
단순히 인자 두개를 받아서 두개를 더한 값을 리턴하는 함수를 작성해보자
const add = (a, b) => a + b
console.log(add(10, 5)) // 15
curry
curry
함수는 함수의 인자를 한개씩 적용하여 필요한 인자가 모두 채워지면 함수를 실행하는 역할을 수행하며 이로 인해 함수의 평가 시점을 늦출수 있다.
const _curry = (fn) => {
return (a) => {
return (b) => {
return fn(a,b)
}
}
}
위 curry
함수를 이용하여 add
함수를 다시 작성하면
const add = _curry((a, b) => a + b)
var add10 = add(10)
console.log( add10(5) ) // 15
console.log( add10(20) ) // 30
console.log( add(10)(5) ) // 15
위에 add10
함수는 현재 add
함수의 인자 a
에 10 을 넣은 함수를 받은 상태이고 add10(5)
를 통해 b
에 5를 넘겨(10 + 5) => 10 + 5
의 리턴값인 15 를 출력할 수 있다.
위에 있는 curry
함수를 조금 더 개선해보자. 현재 add
함수는 인자를 각각 한개씩 받는 것은 가능하지만 두개를 동시에 받았을 경우에는 처리하지 못한다.
console.log(add(10)(5)) // 15
console.log(add(10, 5)) // (b) => { return fn(a,b) }
curry
함수에 arguments가 2개라면 인자로 받은 함수를 곧바로 리턴하는 로직을 구현해보자
const _curry = (fn) => {
return function (a, b) {
return arguments.length == 2 ? fn(a, b) : b => fn(a,b)
}
}
return function(a, b) {}
에서 화살표 함수를 사용하지 못한 이유는 화살표 함수가 arguments
프로퍼티를 지원하지 않기 때문이다.
console.log(add(10)(5)) // 15
console.log(add(10, 5)) // 15
두개 로그 전부 15가 뜨는것을 확인할 수 있다.
그럼 이제 sub
함수를 curry
를 이용하여 작성해보자.
const sub = _curry((a, b) => a - b)
var sub10 = sub(10)
console.log(sub10(5)) // 5
console.log(sub(10, 5)) // 5
console.log(sub(10, 5))
는 원하는 값이 나왔지만 console.log(sub10(5))
는 코드상 5에서 10을 뺀 값인 -5가 나와야 하는데 5가 출력되었다.
curryr
curryr
함수를 만들어 오른쪽부터 인자를 처리하도록 바꿔보자.
const _curryr = (fn) => {
return function (a, b) {
return arguments.length == 2 ? fn(a,b) : b => fn(b, a)
}
}
const sub = _curryr((a, b) => a - b)
다시 콘솔에 찍어 보면 원하는 방식으로 로그가 찍힐 것이다.
console.log(sub10(5)) // -5
console.log(sub(10, 5)) // 5
get
오브젝트에 접근해 해당하는 key의 value 를 가져오는 함수를 작성해보겠다
const _get = (obj, key) => {
return obj == null ? undefined : obj[key]
}
console.log( _get(users[0], "name")) // ID
console.log( _get(users[9], "name")) // undefined
여기서 users
는 지난 시간부터 다뤘던 유저의 객체 배열이고, obj 의 존재 여부를 판단하는 방어코드를 작성하여 error 가 발생하지 않게 하였다.
이러한 get
함수에 커링을 적용하면
const _get = _curryr((obj, key) => {
return obj == null ? undefined : obj[key]
})
var getName = _get("name")
console.log( _get(users[0], "name")) // ID
console.log(getName(users[0])) // ID
console.log(getName(users[1])) // QW
get
함수를 이용해 만든 getName
이라는 함수는 인자로 객체를 받으면 해당 객체에 있는 name
을 반환한다.
코드 개선
저번에 작성했던 코드 중 30세 이상 유저의 name 을 출력하는 코드가 있었다. get
함수를 이용하여 해당 코드를 개선해 보자
// 개선 전
console.log(
_map(
_filter(users, (user) => user.age >= 30 ),
user => user.name
)
)
// 개선 후
console.log(
_map(
_filter(user, function(user){ return user.age >= 30 }),
_get("name")
)
)
정리
지금까지 작성한 함수들이다.
var users = [
{id: 1, name: 'ID', age: 29},
{id: 2, name: 'QW', age: 30},
{id: 3, name: 'WE', age: 31},
{id: 4, name: 'ER', age: 32},
{id: 5, name: 'RT', age: 21},
{id: 6, name: 'DF', age: 28},
{id: 7, name: 'AS', age: 35},
{id: 8, name: 'IS', age: 25}
]
const _filter = (list, predi) => {
var new_list = []
_each(list, (val) => {
if(predi(val)) {
new_list.push(val)
}
})
return new_list
}
const _map = (list, mapper) => {
var new_list = []
_each(list, (val) => {
new_list.push(mapper(val))
})
return new_list
}
const _each = (list, iter) => {
for(var i = 0; i < list.length; i++) {
iter(list[i])
}
}
const _curry = (fn) => {
return function (a, b) {
return arguments.length == 2 ? fn(a,b) : b => fn(a, b)
}
}
const _curryr = (fn) => {
return function (a, b) {
return arguments.length == 2 ? fn(a,b) : b => fn(b, a)
}
}
const _get = _curryr((obj, key) => {
return obj == null ? undefined : obj[key]
})
const add = _curry((a, b) => a + b)
const sub = _curryr((a, b) => a - b)
console.log(
_map(
_filter(users, function(user){ return user.age >= 30 }),
_get("name")
)
)
'Study > VanillaJS' 카테고리의 다른 글
[개념잡기] 함수형 프로그래밍 - (6) call (0) | 2019.03.06 |
---|---|
[개념잡기] 함수형 프로그래밍 - (5) reduce (0) | 2019.03.04 |
[개념잡기] 함수형 프로그래밍 - (3) each (0) | 2019.03.04 |
[개념잡기] 함수형 프로그래밍 - (2) map, filter (0) | 2019.03.03 |
[개념잡기] 함수형 프로그래밍 - (1) 시작 (0) | 2019.03.03 |