TIL 1일차(함수형프로그래밍 지연평가 및 실습)
2021.06.28
- 지연평가(lodash 사용)
- 지연평가를 시작하고 유지시키는 것
- map
- filter, reject
- 지연평가를 끝내는 것
- take
- filter,reject
- 지연평가를 시작하고 유지시키는 것
//Lodash는 차례차례 100번씩 실행하는 것이 아니라 하나씩 모든 로직을 타게된다
//즉 L.some의 값이 10보다 클때 함수가 종료되어 루프는 총 12번만 돔
_.go(_.range(100),
L.map(function (val) {
++mi;
return val * val;
}),
L.filter(function (val) {
++fi;
return val % 2;
}),
L.some(function (val) {
return val > 10
}),
console.log);
-
함수형 자바스크립트 요약
- 함수는 최대한 작게만들기
- 다형성이 높은 함수 만들기(타입이나 값체크를 하지말고 어떠한 형태의 값도 함수에 적용 가능)
- javascript map, filter등은 함수가 아니라, 객체의 메소드이다.
- 메소드는 해당클래스의 인스턴스에서만 작동 (map,filter는 array 에서만 작동)
- jquery 객체 => array like 객체 , array가 아니라 map과 filter를 쓰지 못함.
- 상태를 변경하지 않거나 정확하게 다루어 부수효과 최소
- 동일한 인자를 받으면 동일한 결과를 만드는 순수함수 만들기
- 복잡한 객체 하나 인자보다 일반적인 값 여러개 받기
- 큰 로직을 고차함수로 만들고 세부 로직을 보조함수로 만들기
- 모델이나 컬렉션 등의 커스텀 객체보다 기본객체 사용
- 로직은 단방향
- 작은 함수를 조합하여 큰 함수 만들기
- 데이터 흐름 프로그래밍
_go(users, _filter(function (list) { return list.age > 30; }), _map(_get('name')), console.log )
- 함수형 프로그래밍 closure, elixir
- 지연평가 + 동시성 + 병렬성
- reduce와 결과는 동일하지만 fold를 사용하면 for문이 아니라 병렬로 접기 방식으로 아이디어를 낼 수 있다
-
함수 실습
var users = [ {id: 101, name: 'ID'}, {id: 102, name: 'BJ'}, {id: 103, name: 'PJ'}, {id: 104, name: 'HA'}, {id: 105, name: 'JE'}, {id: 106, name: 'JI'} ]; var posts = [ {id: 201, body: '내용1', user_id: 101}, {id: 202, body: '내용2', user_id: 102}, {id: 203, body: '내용3', user_id: 103}, {id: 204, body: '내용4', user_id: 102}, {id: 205, body: '내용5', user_id: 101}, ]; var comments = [ {id: 301, body: '댓글1', user_id: 105, post_id: 201}, {id: 302, body: '댓글2', user_id: 104, post_id: 201}, {id: 303, body: '댓글3', user_id: 104, post_id: 202}, {id: 304, body: '댓글4', user_id: 105, post_id: 203}, {id: 305, body: '댓글5', user_id: 106, post_id: 203}, {id: 306, body: '댓글6', user_id: 106, post_id: 204}, {id: 307, body: '댓글7', user_id: 102, post_id: 205}, {id: 308, body: '댓글8', user_id: 103, post_id: 204}, {id: 309, body: '댓글9', user_id: 103, post_id: 202}, {id: 310, body: '댓글10', user_id: 105, post_id: 201} ];
-
- 특정인의 posts의 모든 comments 거르기
_go( // _filter(posts, function (post) { // return post.user_id == 101 // }), _.where(posts, {user_id:101}), // _.map(function(posts){ // return posts.id // }), _.pluck('id'), function (post_ids) { return _filter(comments, function (comment) { return _.contains(post_ids, comment.post_id); //_.contains([1,2,3], 4); //false }) }, console.log ) //간략화 하기 var f1 = _.pipe(post_by, comments_by_posts); console.log(f1({user_id: '101'}));
-
- 특정인의 posts에 comments를 단 친구의 이름들 뽑기
// 2. 특정인의 posts에 comments를 단 친구의 이름들 뽑기 function post_by(attr) { return _.where(posts, attr) } var comments_by_posts = _.pipe( _.pluck('id'), function (post_ids) { return _.filter(comments, function (comment) { return _.contains(post_ids, comment.post_id) }) }); _.go( {user_id: '101'}, post_by, comments_by_posts, _.map(function (comment) { return _.find(users, function (user) { return user.id == comment.user_id }).name }), _.uniq, console.log )
-
- 특정인의 posts에 comments를 단 친구들 카운트 정보
_.go( {user_id: '101'}, f1, comments_to_user_names, _.count_by, console.log )
-
- 특정인이 comment를 단 posts 거르기
_.go( _.where(comments, {user_id: '105'}), _.pluck('post_id'), function (post_ids) { return _.filter(posts, function (post) { return _.contains(post_ids, post.id) }) }, console.log )
-
- users + posts + comments (index_by와 group_by로 효율 높이기)
var users2 = _.index_by(users, 'id'); //1:1로 매핑되는 경우 사용 가능 id가 객체의 키가됨 //기존에 user 전체를 순환하는 구조에서 key 값으로 원하는 값만 가져 function find_user_by_id(user_id) { return users2[user_id]; // return _.find(users, function(user){ // return user.id==user_id // }) } //comment + user + post_id 그룹(post_id별 데이터 그룹) var comments2 = _.go(comments, _.map(function (comment) { return _.extend({ user: find_user_by_id(comment.user_id) }, comment) }), _.group_by('post_id') //post_id를 기준으로 comment 그룹화 ) var post2 = _.go( posts, _.map(function (post) { return _.extend({ // comment: _.filter(comments2, function (comment) { // return post.id == comment.post_id // }), comment: comments2[post.id], user: find_user_by_id(post.user_id) }, post) }) ); var post3 = _.group_by(post2, 'user_id'); //user2에 위에만든 post3 넣음 //원본은 절대로 건드리지 않는 var users3 = _.map(users2, function (user) { return _.extend({ // post: _.filter(post3, function(post){ // return post.user_id==user.id // }) post: post3[user.id] || [] }, user) }) console.clear(); // 5.1. 특정인의 posts의 모든 comments 거르기 var user= users3[0]; _.go( user.post, _.pluck('comment'), _.flatten, //배열합치기(납작하게만들다 ) console.log ) // 5.2. 특정인의 posts에 comments를 단 친구의 이름들 뽑기 _.go( user.post, _.pluck('comment'), _.flatten, //배열합치기(납작하게만들다 ) _.pluck('user'), _.pluck('name'), _.uniq, console.log ) // 5.3. 특정인의 posts에 comments를 단 친구들 카운트 정보 _.go( user.post, _.pluck('comment'), _.flatten, //배열합치기(납작하게만들다 ) _.pluck('user'), _.pluck('name'), _.count_by, console.log ) // 5.4. 특정인이 comment를 단 posts 거르기 console.log(_.filter(post2, function (post) { console.log(post); return _.find(post.comment, function (comment) { return comment.user_id == 105 }) }));
- reduce를 활용한 함수형 프로그래밍 실전
var products = [ { is_selected: true, // <--- 장바구니에서 체크 박스 선택 name: "반팔티", price: 10000, // <--- 기본 가격 sizes: [ // <---- 장바구니에 담은 동일 상품의 사이즈 별 수량과 가격 {name: "L", quantity: 4, price: 0}, {name: "XL", quantity: 2, price: 0}, {name: "2XL", quantity: 3, price: 2000}, // <-- 옵션의 추가 가격 ] }, { is_selected: true, name: "후드티", price: 21000, sizes: [ {name: "L", quantity: 2, price: -1000}, {name: "2XL", quantity: 4, price: 2000}, ] }, { is_selected: false, name: "맨투맨", price: 16000, sizes: [ {name: "L", quantity: 10, price: 0} ] } ]; //1. 모든 수량 var total_quantity =_.reduce(function (tq, product) { return _.reduce(product.sizes, function (tq, size) { return tq + size.quantity; }, tq) //size가 중첩되어있어서 reduce 중첩, webstorm에서 tq를 클릭하면 어디서 사용하는 변수인지 확인 가능 }, 0); var total_price =_.reduce(function (tp, product) { return _.reduce(product.sizes, function (tp, size) { return tp + size.quantity*(product.price+ size.price); }, tp) //size가 중첩되어있어서 reduce 중첩, webstorm에서 tp를 클릭하면 어디서 사용하는 변수인지 확인 가능 }, 0); _.go( products, total_quantity, console.log ) //2. 선택 된 총 수량 _.go( products, // _.filter(product=>product.is_selected), _.filter(_get('is_selected')), total_quantity, console.log ) //3. 모든 가격 _.go( products, // _.filter(product=>product.is_selected), total_price, console.log ) //4. 선택 된 총 가격 _.go( products, // _.filter(product=>product.is_selected), _.filter(_get('is_selected')), total_price, console.log )
- 함수형 프로그래밍을 활용한 비동기 제어
function square(a) { return new Promise(function (resolve) { setTimeout(function () { return resolve(a * a); }, 500) }) } square(10) .then(square) .then(square) .then(function (res) { console.log(res); }); _.go( square(10), square, square, console.log ) var list = [2, 3, 4]; new Promise(function (resolve) { (function recur(res) { if (res.length == list.length) { return resolve(res); } square(list[res.length]).then(function (val) { res.push(val); recur(res); }); })([]) //빈 배열로 처음 recur 실행 }).then(console.log); _.go( list, _.map(square), _.map(square), console.log )
-
댓글을 작성해보세요.