간단하게 map, filter, reduce를 구현해보자.
1. map은 인자를 두개를 받으며, 첫번째 인자를 받아서 두번째 인자로 평가 후 배열로 리턴해준다.
const map = ( iter, func ) => {
const ret = []
for( const item of iter ) {
ret.push( func( item ) )
}
return ret
}
위와같이 간단하게 짤 수 있다.
2. filter를 구현해보자 map과 두개의 인자를 받으며, 첫번째 인자를 받아서 두번째 인자로 평가 후 값이 있다며 배열로 리턴을 해준다.
const filter = ( iter, func ) => {
const ret = []
for( const item of iter ) {
if( func( item ) ) {
ret.push( item )
}
}
return ret
}
3. reduce는 세개의 인자를 받으며, 첫번째 인자를 받아서 두번째 인자로 받은 함수로 평가하며 축적된 값을 리턴을 한다, 이때에 세번째 인자를 초기값으로 이용한다.
const reduce = ( iter, func, acc ) => {
for( const item of iter ) {
acc = func(acc, item)
}
return acc
}
이와 같이 표현 할 수 있지만, 초기값을 안넣은 경우에는 iter의 첫번째 값을 초기값으로 사용하게 변경 해보자.
const reduce = ( ...args ) => {
let iter = args[0]
const func = args[1]
let acc = args[2]
if( args.length < 3 ) {
iter = iter[Symbol.iterator]()
acc = iter.next().value
}
for( const item of iter ) {
acc = func( acc, item )
}
return acc
}
코드가 좀 길어지긴 했지만, 3번째 인자가 있느냐 없느냐로 처리하여 초기값을 입력 해준 것이다.
자 이제 만들어진 함수들로 홀수들을 필터한 후 해당 값들을 제곲을 하고 그 값들을 더해보자.
console.log(
reduce(
map(
filter( [1, 2, 3], a => a % 2 ),
a => a * a ),
( a, b ) => a + b
)
)
자 위처럼 해당 함수를 적용 시킬수 있다. 위의 코드를 보면 엄청나게 가독성이 떨어진다. 위에 설명한 순서를 filter를 먹이고, map을 한 후 reduce를 처리하였는데, 코드상으로 보이는 것은 반대이다.
자 이것을 go 함수를 만들어서 해결해 보자. 이때에 reduce를 이용할 수 있다.
const go = ( ...args ) => reduce( args, ( item, func ) => func( item ) )
위의 코드를 잘 살펴 보시기 바란다.
go를 이용 하여 위의 코드를 수정해보자.
이 처럼 위에 설명했던 코드가 언어적 표현 순서대로 콜백 지옥에 빠지지 않고, 평평하게 나열된 것을 볼 수 있다.
자 여기서 한가지 눈여겨 볼 것이 있다. numbers라는 변수는 위에 코드에는 없었던 변수 인데 생겨난 변수이다. 저 변수는 앞에서 평가한 값을 계속 전달 할 수 있게 하는 매개체 일 뿐 큰 의미는 없다. 저 변수를 없애보도록 하자.
cuury는 인자로 함수를 받고, 함수를 리턴한다. 리턴된 함 수에 인자가 두개 이상이면 curry를 실행할 때 사용한 함수를 실행하고 하나면 다음 인자를 받을 수 있도록 또 한번 함수를 리턴하고 그 함수에 인자를 넣어 실행하면 실행 된다.
const curry =
func => (a, ...args) => args.length ? func(a, ...args) : (...args) => func(a, ...args)
자 사용예를 살펴보자
1. curry는 함수를 받아 함수를 리턴 한다.
2. curry를 실행하여 반환된 함수에 인자를 두개 이상 넣으면 실행 된다.
3. curry를 실행하여 반환된 함수에 인자를 한개를 넣으면 함수가 받환되고, 또한번 인자 하나를 전달하여 실행하면 실행된다.
****. 아래 처럼 함수를 넣어 평가하는 것 또한 가능하다.
자 이제 map, reduce, filter를 curry를 이용하여 만들자.
const map = curry(( func, iter ) => {
const ret = []
for( const item of iter ) {
ret.push( func( item ) )
}
return ret
})
const filter = curry(( func, iter ) => {
const ret = []
for( const item of iter ) {
if( func( item ) ) {
ret.push( item )
}
}
return ret
})
const reduce = curry(( ...args ) => {
const func = args[0]
let iter = args[1]
let acc = args[2]
if( args.length < 3 ) {
iter = iter[Symbol.iterator]()
acc = iter.next().value
}
for( const item of iter ) {
acc = func( acc, item )
}
return acc
})
처음에 선언한 함수들과 달리 func인자를 가장 처음에 두었는데 이 뒤에 나올 코드를 위해 미리 바꿔두었다.
const go = ( ...args ) => reduce( ( item, func ) => func( item ), args )
reduce가 바뀌었기에 go도 같이 변경 되었다.
그리고 아래 코드를 실행해보자.
console.log(
go(
[1, 2, 3],
filter( a => a % 2 ),
map( a => a * a ),
reduce( (a, b) => a + b )
)
)
이제는 numbers라는 변수 없이 위의 함수들이 실행되는 것을 볼 수 있다. filter, map, reduce들을 실행한 결과를 함수가 됨으로써 go안의 reduce를 사용하여 축적된 결과물을 얻을 수 있다.
댓글
댓글 쓰기