[무료] 자바스크립트로 알아보는 함수형 프로그래밍 (ES5) - 인프런 | 강의

마플(http://www.marpple.com)의 CTO 유인동님이 알려주는 함수형 프로그래밍에 대한 강좌 입니다. 함수형 프로그래밍으로 라이브러리를 직접 만들어가며 함수형 프로그래밍의 패러다임과 코딩의 즐거

www.inflearn.com

find

filter와 유사하나 predi에 걸러진 처음으로 만나는 요소를 반환한다.

/*찾아내기 대표 함수 find
filter와 유사하나 predi에 걸러진 처음으로 만나는 요소를 반환한다.*/
function find(list, predi){
    const _keys = keys(list);
    for(let i=0;i<_keys.length;i++){
        const val = list[_keys[i]];
        if(predi(val)) return val;
    }
    return undefined; //생략 가능, 자동으로 undefined 리턴될 것
}
console.log(
    _.find(users, user => user.age >= 30),
    _.filter(users, user => user.age >= 30),
);

find_index

predi에 걸러진 처음으로 만나는 요소의 인덱스를 리턴한다.

/*predi에 걸러진 처음으로 만나는 요소의 인덱스를 리턴한다.*/
function find_index(list, predi){
    const _keys = keys(list);
    for(let i=0;i<_keys.length;i++){
        if(predi(list[_keys[i]])) return _keys[i];
    }
    return -1; //자바스크립트 표준에 맞춤
}
console.log(
    _.find_index(users, user => user.age <= 25),
    _.find_index(users, user => user.age > 40),
);

주의사항

설명으로 처음으로 만나는 요소라고 했는데, 이것이 순차 탐색을 의미하는 것이 아니다. 

언어를 넘어서 함수형을 부수효과를 제거하고, 항상 새로운 값을 만들어 리턴한다. 이러한 점은 병렬 처리에 강점이된다. 따라서 대부분의 언어에서 멀티 쓰레드로 처리할 수도 있다. 이 경우 가장 처음 조우하는 값을 리턴하게 된다.

 

some

주어진 조건에 값이 하나라도 존재하는지 검사한다.

/* find_index 특화 함수 주어진 조건에 값이 하나라도 존재하는지 검사한다. */
function some(list, predi){
    //하나라도 해당하면 -1을 리턴할 수 없다.
    return find_index(list, predi) !== -1;
}

every

주어진 조건에 값이 전부 일치하는지 검사한다.

/* find_index 특화 함수 주어진 조건에 값이 전부 일치하는지 검사한다.*/
function every(list, predi){
    //주어진 조건 반대에 해당하는 값을 못찾으면, 전부 해당하는 것
    return find_index(list, negate(predi)) === -1;
}
function every2(list, predi){
    return find_index(list, function(val){
        return !predi(val);
    })
}
function every3(list, predi){
    return find_index(list, (val)=> !predi(val) )
}

전부 일치를 확인하려면, 전제 조건이 모든 요소를 전부 순회해야한다.

따라서 find_index가 -1을 리턴하도록 유도한다. 그리고 나서 주어진 조건식을 활용할 방법을 생각하면, every 동작 방식이 쉽게 이해된다.

함수를 사용해 조건식 결과를 반대로 만들면 된다. 이때 이미 만들어둔 negate를 사용했다.

 

console.log(
    _.some(users, user=>user.age>30),
    _.every(users, user=>user.age>30),
    _.every(users, user=>user.age>10),
)

더 다듬기

조건식이 주어지지 않을때 기본적으로 동작할 방식을 지정해준다.

이에 identity 함수를 사용했다.

/* find_index 특화 함수 주어진 조건에 값이 하나라도 존재하는지 검사한다.*/
function some(list, predi){
    //하나라도 해당하면 -1을 리턴할 수 없다.
    return find_index(list, predi||identity) !== -1;
}
/* find_index 특화 함수 주어진 조건에 값이 전부 일치하는지 검사한다.*/
function every(list, predi){
    //주어진 조건 반대에 해당하는 값을 못찾으면, 전부 해당하는 것
    return find_index(list, negate(predi||identity)) === -1;
}

아무런 의미도 없을 것 같던 identity 함수가 요긴하게 쓰인다.

console.log(
    _.some(users),
    _.every(users),
    _.some(testArr),
    _.every(testArr),
)

contains

주어진 값이 존재하는지 검사하는 함수

/*주어진 값이 존재하는지 검사하는 함수*/
function contains(list, data){
    return find_index(list, val => val === data) !== -1;
}
console.log(
    _.contains(users, users[2] ),
    _.contains(users[2], "JM" ),
    _.contains(users[2], "홍길동" ),
)

 

function.html
0.01MB

 
 

function.html

0.01MB

 

 

[무료] 자바스크립트로 알아보는 함수형 프로그래밍 (ES5) - 인프런 | 강의

마플(http://www.marpple.com)의 CTO 유인동님이 알려주는 함수형 프로그래밍에 대한 강좌 입니다. 함수형 프로그래밍으로 라이브러리를 직접 만들어가며 함수형 프로그래밍의 패러다임과 코딩의 즐거

www.inflearn.com

reject

주어진 조건에 해당하는 요소만 거르는 함수

단순히 filter함수에 들어온 보조함수 predi를 함수로 캡슐화하여 평가 시점을 조절

/*단순히 filter에 반대로 행동하는 함수
(== 주어진 조건에 해당하는 값을 반환하는 함수*/
function reject(list, predi){
    return filter(list, val => !predi(val));
}
console.log(
    _.filter(users, user=>user.age>30),
    _.reject(users, user=>user.age>30),
)

negate

결과를 반전시키는 함수

/*결과를 반대로 바꾸는 함수*/
function negate(predi){
    return val => !predi(val);
}

단순히 predi 함수를 인자로 받아 평가 시점을 조절

const testArr = [undefined, Infinity, null, NaN, 0, 1, 'true','false',true,false];
console.log(
    _.filter(_.identity)(testArr),
    _.filter(_.negate(_.identity))(testArr)
);

/*단순히 filter에 반대로 행동하는 함수
(== 주어진 조건에 해당하는 값을 반환하는 함수*/
function reject(list, predi){
    return filter(list, negate(predi) );
}

negate 를 적용했다. 

compact

참으로 판단되는 값들만 걸러내는 함수

/*참으로 판단되는 값을 걸러내는 함수*/
function compact(list){
    return filter(list, identity);
}

const compactEx = _.filter(_.identity);
console.log(
    compactEx(testArr),//이것과 완전히 같다.
    _.compact(testArr)
)
/*
compactEx
b => fn(b,a)   b는 _.filter  , a는 _.identity  , b로 testArr
_.compact
function compact(list){
    return filter(list, identity);
}
화살표 함수로 바꾸면
list => filter(list, identity)
기호만 바꾸면
b => fn(b,a)
*/

compactEx 디버거로 확인
compact 디버거로 확인

 

지금까지 예제를 보면 함수르를 중첩해서 사용하고 있음을 볼 수 있다.

이처럼 순수함수들의 평가 시점을 다루거나 함수가 함수를 리턴하거나 함수가 함수를 대신 실행해주거나 함수들의 응용과 조합을 강조하는 것이 함수형 프로그래밍이다.

 

10개 기능이 많은 함수나 클래스 보다 서로 다른 작은 100개의 함수를 만들어 함수를 조합하는 것이 프로그래밍에 유리하다. 이는 객체지향 SCP(single responsibility principle)를 준수하는 것과 비슷하다.

단일 책임 원칙을 준수하면 좋은 디버깅 포인트를 제공하며, 리팩터링에 유리해져 변화에 강한 프로그램을 만들어 준다.

function.html
0.01MB

 

[무료] 자바스크립트로 알아보는 함수형 프로그래밍 (ES5) - 인프런 | 강의

마플(http://www.marpple.com)의 CTO 유인동님이 알려주는 함수형 프로그래밍에 대한 강좌 입니다. 함수형 프로그래밍으로 라이브러리를 직접 만들어가며 함수형 프로그래밍의 패러다임과 코딩의 즐거

www.inflearn.com

map을 좀 더 특수화한 함수를 만들어본다. 이말은 map이 수집하기 함수중 가장 추상화 수준이 높다는 것을 의미한다.

values

요소 값을 반환하는 함수

/*요소 값을 반환하는 함수*/
function values(list){
    return map(list,d=>d);
}
console.log(
    _.values(users),
    _.values(users[0]),
)

each는 돌림직한 데이터도 순회하도록 만들었기 때문에 위와같이 재미있는 표현을 할 수 있다.

 

identity

자기자신을 기대로 반환하는 함수

/*자기자신을 그대로 반환하는 함수*/
function identity(arg){
    return arg;
}
const testArr = [undefined, Infinity, null, NaN, 0, 1, 'true','false',true,false];
console.log(
    _.filter(_.identity)(testArr)
)

다른 함수에 보조함수 성격이 강하다. filter()함수와 조합하면 true로 평가되는 값을 손쉽게 걸러낼 수 있다.

 

추가로 map에서 보조함수로 사용해도 된다.

/*요소 값을 반환하는 함수*/
function values(list){
    return map(list,identity);
}

pluck

요소에서 특정 값만 추출하는 함수

/*요소에서 특정 값만 추출하는 함수 */
function pluck(list, key){
    return map(list, obj => obj[key]);
}
console.log(
    _.pluck(users, "age"),
    _.pluck(users, "name"),
)

 

여기까지 예제를 보면, 모든 수집하기는 map을 통해서 이루어짐을 알 수 있다.

map은 수집하기 함수 중에서 가장 추상화 수준이 높다.

function.html
0.01MB

 

[무료] 자바스크립트로 알아보는 함수형 프로그래밍 (ES5) - 인프런 | 강의

마플(http://www.marpple.com)의 CTO 유인동님이 알려주는 함수형 프로그래밍에 대한 강좌 입니다. 함수형 프로그래밍으로 라이브러리를 직접 만들어가며 함수형 프로그래밍의 패러다임과 코딩의 즐거

www.inflearn.com

_.each([1,2,3,4], n=> console.log(n))
_.each(null, n=> console.log(n))

list 가 null이면 프로그램이 멈춘다. 

each는 다른 함수 반복문에 자주 쓰이는 중요한 함수다. null이 인자로 들어와도 그럴싸하게 동작할 방법이 필요하다.

함수형 프로그래밍은 불완전한 값이 들어와도 그럴싸한 값을 리턴해 전체 프로그램이 동작하게 하도록 프로그래밍하는 경향이 있다.

 

get

값을 안전하게 가져오는 함수

/* 요소 리스트 값을 하나씩 꺼내어 준다.*/
function each(list, iter){
    for(let i=0;i<get(list,"length");i++){
        iter(list[i]);
    } 
    return list;
}
/*안전하게 값 가져오기*/
function get (obj,key){
    return obj === null ? undefined : obj[key];
}

null을 만나면 undefined라는 그럴싸한 값을 반환해 프로그램이 멈추지 않게 한다.

get을 each에 적용했다.

_.each([1,2,3,4], n=> console.log(n))
_.each(null, n=> console.log(n))
console.log( _.filter(null, n=> true));
console.log( _.map(null, n=> n));

이제 프로그램이 안 멈춘다. 

더불어 each를 사용한 모든 함수들이 혜택을 받게 됐다.

 

추가개선, keys, is_object

each가 돌림직한 데이터도 순회를 했으면 좋겠다.

우선 보조함수가 필요

/* 객체의 keys를 안전하게 리턴하는 함수*/
function keys(obj){
    return is_object(obj) ? Object.keys(obj) : [];
}
/* 객체인지 안전하게 확인하는 함수 */
function is_object(obj){
    return typeof obj === "object" && !!obj;
}

!!obj 의미

_.each({a:1,b:2,c:3}, a=>console.log(a))
//기대하는 동작 => 1 , 2,  3 출력
/* 요소 리스트 값을 하나씩 꺼내어 준다.*/
function each(list, iter){
    const _keys = keys(list);
    for(let i=0;i<_keys.length;i++){
        iter(list[_keys[i]]);
    } 
    return list;
}
_.each({a:1,b:2,c:3}, a=>console.log(a));
_.each([1,2,3], a=>console.log(a));

이제 object도 순회할 수 있게 됐다. 

원리는 object의 key들을 배열로 만들어 key를 순회할 수 있도록 만든다.

자바스크립트는 친절하게도 object 속성 접근방식과 array 인덱스 접근 방식이 동일하다.

object[key]  ,  array[index]  

배열의 경우 추상화해서 생각하면 배열 요소의 key는 인덱스로 볼 수 있다. 

배열의 인덱스를 key로 하는 key 배열이 생성된다. 

 

이 유연한 동작이 가능한 것은 Object.keys() 동작방식과 배열과 오브젝트를 같은 방식으로 접근할 수 있는 자바스크립트 특징 덕분이다.

 

불필요한 관심사를 제거하고, 돌림직한 데이터를 마치 같은 자료구조처럼 다룰 수 있게 됐다.

 

좀 더 알아보기 Object.keys()

열거할만한 데이터를 keys()함수에 이자로 들어올 수 있다.

//Object.keys 동작방식
console.log(Object.keys(["a","b","c"]).join("-")  );
console.log(Object.keys("가나다라").join("-")   );
console.log(Object.keys({고:"바",구:"바",마:"나"}).join("-")   );
//Object.values 동일
console.log(Object.values(["a","b","c"]).join("-")  );
console.log(Object.values("가나다라").join("-")   );
console.log(Object.values({고:"바",구:"바",마:"나"}).join("-")   );

문자열을 결국 char 배열이므로 똑같이 돌림직한 데이터다.

for...in , for ... of

//Object.keys 는 for...in 과 유사
function forIn(list){
    const result = [];
    for(const arg in list) result.push(arg);
    return result.join("-");
}
//Object.values 는 for...of 과 유사
function forOf(list){
    const result = [];
    for(const arg of list) result.push(arg);
    return result.join("-");
}
console.log(forIn(["a","b","c"]) );
console.log(forIn("가나다라") );
console.log(forIn({고:"바",구:"바",마:"나"}));
console.log(forOf(["a","b","c"]) );
console.log(forOf("가나다라") );
console.log(forOf({고:"바",구:"바",마:"나"}));//불가능

 

단일 접근 원칙

동일한 접근 방식에 관련된 법칙,

데이터 접근에 불필요한 관심사를 제거하고 동일한 방식으로 다룬다.

 

bliki: UniformAccessPrinciple

a bliki entry for UniformAccessPrinciple

martinfowler.com

 

 
//단일 접근 원칙
class Data{
    constructor(name, age){
        this._name = name;
        this._age =age;
    }
    get name(){
        console.log("name 게터 호출");
        return this._name;
    }
    get age(){
        console.log("age 게터 호출");
        return this._age;
    }
    set name(arg){
        console.log("name 세터 호출");
        this._name = arg;
    }
    set age(arg){
        console.log("age 세터 호출");
        this._age = arg;
    }
}
const data1 = new Data("홍길동", 50);
console.log(data1.name); //변수처럼 다룬다
data1.name = "임꺽정"
console.log(data1.name); //변수처럼 다룬다

function.html
0.01MB

 

[무료] 자바스크립트로 알아보는 함수형 프로그래밍 (ES5) - 인프런 | 강의

마플(http://www.marpple.com)의 CTO 유인동님이 알려주는 함수형 프로그래밍에 대한 강좌 입니다. 함수형 프로그래밍으로 라이브러리를 직접 만들어가며 함수형 프로그래밍의 패러다임과 코딩의 즐거

www.inflearn.com

파이프 라인 함수

함수만을 인자로 받는 함수로, 함수 여러 개를 입력받아

첫번째 함수 출력이 그 다음 함수의 입력이 된다.

 

data = 입력 => fn1 = 출력/입력=> fn2 ==........... => 최종

pipe

reduce 특화함수

함수만을 인자로 받는 함수, 즉, 함수를 하나로 축약한다.

pipe의 인자로 올수 있는 함수는 전부 curryr 이 적용된 함수여야 한다.

/*리듀스 특화 함수, 함수만을 인자로 받는 함수*/
function pipe(){
    const fns = arguments;
    return function(val){
        return reduce(fns,function(val,fn){
            return fn(val);
        }, val );
    };
}
/*이전 reduce 실습 내용*/
console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val,0));
console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val,10));
console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val,-10));

console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val ));

//30세 이상 사용자 age 합
console.log(_.reduce(_.map(upperAge30(users),u=>u.age),(sum,val)=>sum+val ));
//30세 이상 사용자 age 합, pipe 사용
_.pipe(
    _.filter(u=>u.age>=30),
    _.map(u=>u.age),
    _.reduce((sum,val)=>sum+val),
    console.log
)(users);

/* 단순히 인자 순서만 변경, 화살표 함수로만 표현 */
function curryr(fn){
    return function(a,b,c){
        if(arguments.length === 2) return fn(a,b);
        else if (arguments.length === 3) return fn(a,b,c); //수정
        else return b => fn(b,a);
    };
}

커링이 적용된 함수만 pipe에 올 수 있는 이유

//30세 이상 사용자 age 합, pipe 사용
const tmpFilter = _.filter(u=>u.age>=30);
const tmpMap = _.map(u=>u.age);
const tmpReduce = _.reduce((sum,val)=>sum+val);

console.log(tmpFilter);
console.log(tmpMap);
console.log(tmpReduce);

_.pipe(
    tmpFilter,
    tmpMap,
    tmpReduce,
    console.log
)(users);

간략 설명

상세 풀이

_.pipe에 함수인자를 넣어 호출(아직 users 호출전)
each함수 list가 함수들인 것을 알 수 있다.
list[0] 상세 설명
다음 함수 map

이제 부터는 위 과정을 console.log에 도달할 때까지 반복한다.

 

요약

pipe는 함수만을 인자로 받는 고차함수로 reduce의 특화함수다.

단, 함수는 커링이 적용된 함수여야 한다. 평가 시점을 조정해 데이터가 들어오면, 바로 사전에 적용된 클로저 상태 함수가 줄줄이 수행되게 하기 위함이다.

 

go

/*pipe 즉시 실행 버전*/
function go(data){
    //첫번째 데이터만 제외하면 함수 배열이다.
    const fns = rest(arguments);
    //함수 배열이기 때문에 arg1, arg2 ... argN 풀어주려면 apply를 사용한ㄷ.
    return pipe.apply(null,fns)(data);
}
_.go(users,
    tmpFilter,
    tmpMap,
    tmpReduce,
    console.log
);

go 함수는 단순히 pipe를 실행하는데 함수로 인자 순서를 조정해 실행할 뿐이다.

function.html
0.01MB

 

[무료] 자바스크립트로 알아보는 함수형 프로그래밍 (ES5) - 인프런 | 강의

마플(http://www.marpple.com)의 CTO 유인동님이 알려주는 함수형 프로그래밍에 대한 강좌 입니다. 함수형 프로그래밍으로 라이브러리를 직접 만들어가며 함수형 프로그래밍의 패러다임과 코딩의 즐거

www.inflearn.com

map, filter에 curryr 적용

map, filter의 평가 시점을 조절

const _ = (()=>{
    return {
        filter:curryr(filter) , //커링
        map:curryr(map) ,      //커링
        each: each,
        curry:curry,
        curryr:curryr,
        reduce: reduce,
    };
    //.... 생략

map도 동일하다.

console.log(_.filter);
console.log(_.map);

const upperAge30 = _.filter(u=>u.age>=30);
const userName = _.map(u=>u.name);
console.log(upperAge30);
console.log(userName);

디버거로 확인

console.log(upperAge30(users));
console.log(userName(users));
console.log(userName(upperAge30(users)));

 

reduce

요소를 하나로 접어가는 함수

function reduce(list, iter, memo){
    each(list, val =>{
        memo = iter(memo, val);
    });
    return memo;
}
console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val,0));
console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val,10));
console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val,-10));

reduce 개선

현재 아쉬운 점은 반드시 3번째 인자 memo가 있어야 한다.

memo가 없을 시 list[0] 인자를 memo로 사용하도록 수정

/*요소를 하나로 접어가는 함수
list 요소, iter 보조함수, memo 초기값*/
function reduce(list, iter, memo){
    if(arguments.length===2){
        memo = list[0];
        list = rest(list);
    }
    each(list, val =>{
        //memo에 계속 누적하여 적용한다.
        memo = iter(memo, val);
    });
    return memo;
}
/* 배열 첫 번째 요소를 잘라 나머지 배열을 리턴*/
function rest(list, num){
    return Array.prototype.slice.call(list,num||1);
}
console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val ));
//30세 이상 사용자 age 합
console.log(_.reduce(_.map(upperAge30(users),u=>u.age),(sum,val)=>sum+val ));

memo 인자 없이도 문제없이 동작

function.html
0.01MB

'개발 > 함수형 프로그래밍' 카테고리의 다른 글

07 - 컬렉션 중심 프로그래밍 - map (수집하기)  (0) 2023.06.17
06 - 다형성 높이기  (0) 2023.06.16
05 - 파이프라인, pipe, go  (1) 2023.06.14
03 - 커링, curry, curryr  (0) 2023.06.11
02 - map, filter, each  (0) 2023.06.09
 

[무료] 자바스크립트로 알아보는 함수형 프로그래밍 (ES5) - 인프런 | 강의

마플(http://www.marpple.com)의 CTO 유인동님이 알려주는 함수형 프로그래밍에 대한 강좌 입니다. 함수형 프로그래밍으로 라이브러리를 직접 만들어가며 함수형 프로그래밍의 패러다임과 코딩의 즐거

www.inflearn.com

커링, curry, curryr, 클로저

함수의 평가 시점을 조정하는 기법

/* 커링 함수 오직 함수만을 인자로 받아 함수를 리턴함, 평가 시점을 조율*/
function curry(fn){
    return function(a){
        return function (b){
            return fn(a,b);
        }
    }
}
/* 단순히 인자 순서만 변경, 화살표 함수로만 표현 */
function curryr(fn){
    return a => b => fn(b,a);
}
//중복이름으로 구분자 _
const _add = _.curry((a,b)=>a+b);
const _add10 = _add(10);
console.log(_.curry);
console.log(_add);
console.log(_add10);
console.log(_add10(20));

함수를 인자로 받아 함수를 리턴하는 방식으로 평가 시점을 조율한 것을 볼 수 있다. 이 과정에서 curry가 적용된 함수는 모두 클로저 상태가 된다.

함수가 함수를 계속 리턴하면서 최종적으로 맨 처음 (a,b)=>a+b 함수가 실행된 것을 볼 수 있다.

클로저

const _add = _.curryr((a,b)=>a+b); //curryr
const _add10 = _add(10);
console.log(_.curry);
console.log(_add);
console.log(_add10);
console.log(_add10(20));

인자가 두 개일 때는 그 즉시 평가하도록 수정

/* 커링 함수 오직 함수만을 인자로 받아 함수를 리턴함, 평가 시점을 조율*/
function curry(fn){
    return function(a,b){
        return arguments.length === 2 ? fn(a,b) : function (b){
            return fn(a,b);
        }
    }
}
/* 단순히 인자 순서만 변경, 화살표 함수로만 표현 */
function curryr(fn){
    return (a,b) => arguments.length === 2 ? fn(a,b) : b => fn(b,a);
}
})();
const _add = _.curry((a,b)=>a+b);
const _add10 = _add(10);
console.log(_add);
console.log(_add10);
console.log(_add10(20));
console.log(_add(10,20));

curry 정상동작 확인

const _add = _.curryr((a,b)=>a+b);
const _add10 = _add(10);
console.log(_add);
console.log(_add10);
console.log(_add10(20));
console.log(_add(10,20));

curryr 비정상 동작

화살표 함수는 this 바인딩이 없어 그 상위 문맥 curryr 함수로 잡혀 arguments.length가 1이 나온다.

/* 단순히 인자 순서만 변경, 화살표 함수로만 표현 */
function curryr(fn){
    return function(a,b){
        return arguments.length === 2 ? fn(a,b) : b => fn(b,a)
    };
}

익명 함수로 변경 후 정상 동작 확인

 

curry, curryr 효용

const _sub = _.curry((a,b)=>a-b); //curry
const _sub10 = _sub(10);
console.log( _sub10(20) ); // 인지적으로 20 - 10 = 10 일 것 같지만  - 10

const _sub = _.curryr((a,b)=>a-b);
const _sub10 = _sub(10);
console.log( _sub10(20) );

인자 순서만 변경했을 뿐이지만, 더 명확한 표현력을 가진다.

 

[무료] 자바스크립트로 알아보는 함수형 프로그래밍 (ES5) - 인프런 | 강의

마플(http://www.marpple.com)의 CTO 유인동님이 알려주는 함수형 프로그래밍에 대한 강좌 입니다. 함수형 프로그래밍으로 라이브러리를 직접 만들어가며 함수형 프로그래밍의 패러다임과 코딩의 즐거

www.inflearn.com

 

//테스트 데이터
var users = [
  { id: 10, name: 'ID', age: 36 },
  { id: 20, name: 'BJ', age: 32 },
  { id: 30, name: 'JM', age: 32 },
  { id: 40, name: 'PJ', age: 27 },
  { id: 50, name: 'HA', age: 25 },
  { id: 60, name: 'JE', age: 26 },
  { id: 70, name: 'JI', age: 31 },
  { id: 80, name: 'MP', age: 23 }
];
//앞으로 함수를 담을 obj
const _ = (()=>{
    return {
        
    };
})();

 

filter

요소를 걸러내는 함수

인자로 받은 요소 수와 같거나 적은 요소를 리턴한다.

//함수를 담은 obj
const _ = (()=>{
    return {
        filter:filter ,
    };

/* 요소를 걸러내는 함수, predi라는 보조함수를 인자로 받는다. */
function filter(list, predi){
    //함수형 스타일은 기존 요소를 손상시키지 않고 항상 새로운 값을 리턴한다.
    const result = [];
    for(const val of list){
        //보조함수를 인자로 받아 
        if(predi(val)) result.push(val);
    }
    return result;
}
})();
//나이 25세 이상 사용자 걸러내기
//절차지향 코드
const tmp = [];
for(const user of users){
    if(user.age>=25) tmp.push(user);
}
console.log(tmp);
//함수형 코드
console.log(
    _.filter(users, u=>u.age>=25)
);

console.log(
    _.filter(users, u=>u.age-(u.age%10) === 20), //20대만
    _.filter(users, u=>u.name.startsWith("J")) //J로 시작하는 user
);

map

요소를 변환시키는 함수

들어온 요소와 같은 수의 변환된 요소를 리턴한다.

/* 요소를 변환시키는 함수*/
function map(list , mapper){
    const result = [];
    for(const val of list)
        result.push(mapper(val));
    return result;
}
//map 사용
console.log(
    _.map(users, u=>u.age) ,
    _.map(users, u=>u.name) ,
    _.map(users, u=>u.id) ,
)

중간 점검

//함수를 담은 obj
const _ = (()=>{
    return {
        filter:filter ,
        map:map ,
    };

/* 요소를 걸러내는 함수, predi라는 보조함수를 인자로 받는다. */
function filter(list, predi){
    //함수형 스타일은 기존 요소를 손상시키지 않고 항상 새로운 값을 리턴한다.
    const result = [];
    for(const val of list){
        //보조함수를 인자로 받아 
        if(predi(val)) result.push(val);
    }
    return result;
}
/* 요소를 변환시키는 함수*/
function map(list , mapper){
    const result = [];
    for(const val of list)
        result.push(mapper(val));
    return result;
}

})();

향상된 for문을 사용하고 있어 편리하지만, 분명히 반복문에서 코드 중복이 발생하고있다.

 

each

들어온 요소 리스트에서 값을 하나씩 꺼내어 주는 함수

function each(list, iter){
    for(const val of list) iter(val);
    return list;
}
_.each(users,u=>console.log(u));

filter, map 에 적용 후 동작 확인

//함수를 담은 obj
const _ = (()=>{
    return {
        filter:filter ,
        map:map ,
        each: each,
    };

/* 요소를 걸러내는 함수, predi라는 보조함수를 인자로 받는다. */
function filter(list, predi){
    //함수형 스타일은 기존 요소를 손상시키지 않고 항상 새로운 값을 리턴한다.
    const result = [];
    //보조함수를 인자로 받아 
    each(list, val =>{
        if(predi(val)) result.push(val);
    });
    return result;
}
/* 요소를 변환시키는 함수*/
function map(list , mapper){
    const result = [];
    each(list, val => result.push(mapper(val)));
    return result;
}

function each(list, iter){
    for(const val of list) iter(val);
    return list;
}
})();

함수가 함수를 받아서 처리하는 것을 고차함수 혹은 응용형 함수라 한다.

함수 내부를 보면, 기존 값에 대한 변형 없이 항상 새로운 값을 리턴하고 있다. 그래서 코딩을 계속 진행하는 과정에도 원본 데이터에는 손상이 가지 않을 것을 알 수 있다.

섞어써보기

//섞어쓰기, 30세 이상, user 이름
console.log(
    _.map(_.filter(users, u=> u.age >=30), u=>u.name)
);

function.html
0.00MB

+ Recent posts