본문 바로가기
Front-End/JavaScript

[JavaScript] 함수형 프로그래밍

by 김뚱 2019. 3. 27.

참고사이트 : https://www.inflearn.com/course/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/



<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
<h2>TEST DATA</h2>
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>AGE</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Monkey D.Luffy</td>
<td>19</td>
</tr>
<tr>
<td>2</td>
<td>Roronoa Zoro</td>
<td>21</td>
</tr>
<tr>
<td>3</td>
<td>Nami</td>
<td>20</td>
</tr>
<tr>
<td>4</td>
<td>Usopp</td>
<td>19</td>
</tr>
<tr>
<td>5</td>
<td>Sanji</td>
<td>21</td>
</tr>
<tr>
<td>6</td>
<td>TonyTony Chopper</td>
<td>17</td>
</tr>
<tr>
<td>7</td>
<td>Nico Robin</td>
<td>30</td>
</tr>
<tr>
<td>8</td>
<td>Franky</td>
<td>36</td>
</tr>
<tr>
<td>9</td>
<td>Brook</td>
<td>90</td>
</tr>
</tbody>
</table>
</div>

</body>
<script>
var members = [
{ id : 1, name : 'Monkey D.Luffy', age : 19 }
, { id : 2, name : 'Roronoa Zoro', age : 21 }
, { id : 3, name : 'Nami', age : 20 }
, { id : 4, name : 'Usopp', age : 19 }
, { id : 5, name : 'Sanji', age : 21 }
, { id : 6, name : 'TonyTony Chopper', age : 17 }
, { id : 7, name : 'Nico Robin', age : 30 }
, { id : 8, name : 'Franky', age : 36 }
, { id : 9, name : 'Brook', age : 90 }
];

/**
* 1.명령형 코드
*/
//1-1.30세 이상인 memeber만 임시배열에 push한다.
var temp_arry = [];
for(var i = 0; i < members.length; i++) {
if(members[i].age >= 30) {
temp_arry.push(members[i]);
}
}
//console.log(temp_arry);

//1-2.30세 이상인 member의 name을 수집한다.
var temp_names = [];
for(var i = 0; i < temp_arry.length; i++) {
temp_names.push(temp_arry[i].name);
}
//console.log(temp_names);

//1-3.30세 미만인 member만 임시배열에 push한다.
var temp_arry = [];
for(var i = 0; i < members.length; i++) {
if(members[i].age < 30) {
temp_arry.push(members[i]);
}
}
//console.log(temp_arry);

//1-4.30세 미만인 member의 age를 수집한다.
var temp_ages = [];
for(var i = 0; i < temp_arry.length; i++) {
temp_ages.push(temp_arry[i].age);
}
//console.log(temp_ages);

/**
* 2._filter, _map으로 전환하기
* 함수형 프로그래밍이란?
* 기존값을 변경하지 않고 새로운 값을 리턴하는 방식이다.
* 추상화의 단위를 함수를 이용한다.
* 미리 값을 만들어 놓고 변경하는 방식이 아닌 함수를 통과하면서 한번에 값을 새롭게 만드는 방식이다.
*/

/**
* 함수가 함수를 받아서 원하는 시점에 해당하는 함수가 특정한 인자를 받아 평가를 하는 방식으로
* 이를 응용형 프로그래밍, 적용형 프로그래밍이라고 한다.
* 이때 _filter와 같은 함수를 응용형 함수 또는 고차함수라고 함다.
* 고차함수는 함수를 인자로 받거나 함수를 리턴하는 함수를 의미한다.
*/

// function _filter(list, predicate) {
// var new_list = [];
// for(var i = 0; i < list.length; i++) {
// if( predicate(list[i]) ) {
// new_list.push(list[i]);
// }
// }
// return new_list;
// }//end _filter function

//2-1.30세 이상인 memeber만 임시배열에 push한다.
//console.log( _filter(members, function(member) { return member.age >= 30; }) );

//2-2.30세 미만인 member만 임시배열에 push한다.
//console.log( _filter(members, function(member) { return member.age < 30; }) );

// function _map(list, mapper) {
// var new_list = [];
// for(var i = 0; i < list.length; i++) {
// new_list.push(mapper(list[i]));
// }
// return new_list;
// }//end _map function

//2-3.30세 이상인 member의 name을 수집한다.
// var over_30 = _filter(members, function(member) { return member.age >= 30; });
// var names = _map(over_30, function(member) { return member.name; });
//console.log(names);

// console.log(
// _map(
// _filter(members, function(member) { return member.age >= 30; }),
// function(member) { return member.name; }
// ));

//2-4.30세 미만인 member의 age를 수집한다.
// var under_30 = _filter(members, function(member) { return member.age < 30; });
// var ages = _map(under_30, function(member) {return member.age });
//console.log(ages);

// console.log(
// _map(
// _filter(members, function(member) { return member.age < 30; }),
// function(member) { return member.age; }
// ));

/**
* 3.each 함수 만들기
* _filter, _map에서 for문 중복 제거
*/
function _each(list, iter) {
for(var i = 0; i < list.length; i++) {
iter(list[i]);
}
//받은 값을 그대로 return하는 함수이다.
return list;
}//end _each function

function _filter(list, predicate) {
var new_list = [];
_each(list, function(val) {
if( predicate(val) ) { new_list.push(list[i]); }
});
return new_list;
}//end _filter function

function _map(list, mapper) {
var new_list = [];
_each(list, function(val) {
new_list.push(mapper(val));
});
return new_list;
}//end _map function

/**
* 외부 다형성
* 1.array_like, arguments, document.querySelector
* map과 filter는 method이다.
* method는 객체의 상태에 따라 결과가 달라지는 특징을 가지고 있다.
* method는 객체지향 프로그래밍 이다.
* method는 해당 클래스에 정의되기 때문에 해당 클래스의 인스턴스에서만 사용할 수 있는 특징을 가지고 있다.
* 예를 들면 map은 array가 아닌 객체에는 사용할 수 없다.
* 때문에 다형성을 지원하기에 어려운 부분이 있다.
* Array-like-objects 는 많이 사용하고 있다.
* 그 중에서 가장 대표적인것은 jQuery객체이다.
* querySelectorAll은 객체 컬렉션인 NodeList를 반환한다.
* jQuery에서 css selector로 element를 찾을 때 사용하는 함수이다.
* console.log( document.querySelectorAll('body') ); //[body]
* console.log( document.querySelectorAll('*') ); //모든 tag가 배열처럼 보여진다. 하지만 이는 array 객체가 아닌 array_like 객체이다. _proto_: NodeList 이다. 즉 배열이 아니다.
* 만약 배열이라면 map method가 적용될 것이다.
* console.log( document.querySelectorAll('*').map(function(node) { return node.nodeName; }) );
* Uncaught TypeError: document.querySelectorAll(...).map is not a function at <anonymous>:1:45
* 함수형 프로그래밍에서는 함수를 먼저 만들고 함수에 맞는 데이터를 구성해서 적용하는 방식으로 프로그래밍을 하게된다.
* 다형성을 높게 프로그래밍을 할 수 있다.
* console.log( _map(document.querySelectorAll('*'), function(node) { return node.nodeName; }) );
* map method를 함수형으로 전환하였더니 querySelectorAll에는 없었던 함수를 사용할 수 있게 되었다.
* _map 함수는 들어오는 인자가 length가 있고 0부터 (length-1)만큼 반복문이 실행되었을 때 해당하는 key마다 값이 있으면 사용이 가능하다.
*/

/**
* 내부 다형성
* predi, iter, mapper
* 외부의 다형성은 만들고 array_like 객체들을 적용할 수 있는 것은 _map, _filter 등 (순수)함수가 어떻게 구현(구조)되었는지에 따라 달라진다.
* 이 객체안에 어떠한 값이 들어있어도 모두 수행할 수 있는 역할은 보조함수가 담당한다.
*
*/
_map([1,2,3,4], function(v) { return v + 10; });
//2번째 인자에 들어가는 함수를 콜백함수라고 부르는 경우가 있다.
//함수형 프로그래밍에서는 두번째 함수가 어떠한 역할을 하는지에 따라서 다양한 이름을 가지는 것이 굉장히 중요하다.
//콜백함수는 어떠한 일을 모두 수행한 다음에 다시 돌려줄 때 사용하는 함수이다.
//predicate : 조건을 리턴하는 함수, 반복적으로 실행되는 함수, 중간에 맵핑하는 함수 라고 할 수 있다.
//즉 각각의 역할에 맞는 보조함수의 이름을 붙여주는 것이 좋다.

/**
* 커링
* 함수와 인자를 다루는 기법이다.
* 함수에 인자를 한개씩 적용하다가 필요한 인자가 다 채워졌을경우 함수 본체를 실행하는 기법이다.
* 자바스크립트에서는 커링이 지원되지 않지만 일급함수가 지원되고 평가시점을 지정할 수 있는 함수를 구현할 수 있다.
* 인자로 본체 함수를 받고 _curry 함수를 실행하면 즉시실행함수를 리턴한다.
* 인자를 채울때까지 실행하다가 인자가 채워지면 본체 함수를 실행한다.
*/
//인자로 함수를 받고 즉시함수를 return 한다.
// function _curry(fn) {
// return function(a) {
// return function(b) {
// return fn(a, b);
// }
// }
// }//end function _curry

//var add = function(a, b) { return a + b;}
//console.log(add(10, 5)); //15

var add = _curry(function(a, b) { return a + b; });
//var add10 = add(10);
//var add5 = add(5);
//console.log(add10(5)); //15
//실행순서
//a를 받고 b를 받은 후 fn을 실행한다.
//var add = function(a) { return function(b) { return fn(a, b)} }
//add10 = function(b) { return fn(10, b) }
//add10(5) = return fn(a, b)

//console.log( add(5)(3) ); //8
//console.log( add5(3) ); //8
//console.log( add(10)(3) ); //13

//결론은 본체함수인 add를 가지고 있다가 원하는 시점까지 미뤘다가 평가하는 기법이다.
//함수가 함수를 대신 실행하거나 함수가 함수를 리턴하는 방식으로 함수를 조합하여 만드는 방식이 함수형 프로그래밍이다.

//console.log( add(1, 2) ); //ƒ (b) { return fn(a, b); } 실행이 안되고 함수가 리턴하게 된다.
//파라미터 인자가 2개일 경우 함수를 리턴하지 않고 즉시 실행하는 함수를 리턴하도록 변경
function _curry(fn) {
return function(a, b) {
return arguments.length == 2 ? fn(a, b) : function(b) { return fn(a, b); };
}
}//end function _curry

function _curryr(fn) {
return function(a, b) {
return arguments.length == 2 ? fn(a, b) : function(b) { return fn(b, a); };
}
}//end function _curry

//console.log( add(1, 2) ); //3

//빼기 기능만들기
//var sub = _curry(function(a, b) { return a - b; });
//console.log( sub(10, 5) ); //5

// var sub10 = sub(10);
// console.log( sub10(5) ); //5
//위의 경우에는 인자 5에 sub10이라는 것을 적용하는 함수이므로 5 - 10 이 더 자연스럽다. 현재는 표현력이 잘 맞지 않는다.

//오른쪽부터 인자를 적용하는 _curryr이라는 함수를 활용할 수 있다.
var sub = _curryr(function(a, b) { return a - b; });
var sub10 = sub(10);
console.log( sub10(5) ); //-5


/**
* get 함수
* object에 있는 값을 안전하게 참조하는 역할
*/
// var _get = function(obj, key) {
// //key로 접근하기 어려운경우 예외처리 (에러가 발생하지 않도록 한다.)
// return obj == null ? undefined : obj[key];
// }//end _get function

var _get = _curryr(function(obj, key) {
return obj == null ? undefined : obj[key];
});

var member1 = members[0];
// console.log(member1);
// console.log(_get(member1, 'name'));
// console.log(_get('name')(member1));

var get_name = _get('name');

console.log(get_name(member1));

//_get을 이용하여 코드줄이기
console.log(
_map(
_filter(members, function(member) { return member.age >= 30; }),
_get('name')
// function(member) { return member.name; }
));
console.log(
_map(
_filter(members, function(member) { return member.age < 30; }),
_get('age')
// function(member) { return member.age; }
));

//어떠한 함수를 이용하여 또다른 함수를 만들고 map에서 사용하는 iterator 기법을 대신 사용할 수 있다.

/**
* reduce
* 첫번째 인자로는 배열, 두번째 인자로는 함수, 세번째 인자값으로느 시작값을 전달한다.
* 축약하는 함수이다.
* 원하는 새로운 값을 만들때 사용한다.
* 기존인자와 다른 형태로 만들때 주로 사용한다.
* 2번째 인자로 받은 함수를 재귀적으로(연속적으로) 호출하면서 값을 축약해 나가는 함수이다.
* 복잡하거나 어려운 로직을 단순하게 할 수 있도록 도와준다.
* 3번째 인자를 생략할 수 있다. add(1, 2)를 하면서 list.length가 3이면 2번만 수행하도록 하면된다.
*/

//list를 받고 제외시킬만큼의 숫자를 받는다.
var slice = Array.prototype.slice;
function _rest(list, num) {
//만약 넘겨받은 값이 없을 경우에는 1로 처리한다.
return slice.call(list, num || 1);
}

function _reduce(list, iter, memo) {
if(arguments.length == 2) {
memo = list[0];
//slice는 array의 함수이다. array_like 객체가 왔을때는 사용할 수 없다.
//var slice = Array.prototype.slice; 이처럼 변수에 담고
//slice.call(a, 2)을 통해서 a를 this로 사용하고 두번째 인자로 원하는 만큼 숫자를 주어서 array로 변환된 새로운 객체를 받을 수 있다.
//slice.call(a, 2).constructo를 통해서 Array객체인것을 확인할 수 있다.
//var a = { 0:1, 1:20, 2:30 }에서도 적용할 수 있다.
// list = list.slice(1); //새로운 배열을 리턴하는 함수이다.
list = _rest(list);
}
_each(list, function(val) {
memo = iter(memo, val);
});
return memo;
}

console.log( _reduce([1, 2, 3], add) ); //6
console.log( _reduce([1, 2, 3, 4], add, 0) ); //10
console.log( _reduce([1, 2, 3, 4], add, 10) ); //20

//동작원리
// memo = add(0, 1);
// memo = add(memo, 2);
// memo = add(memo, 3);
// return memo;
// add(add(add(0, 1), 2), 3);
</script>
</html>


728x90
반응형

'Front-End > JavaScript' 카테고리의 다른 글

[JavaScript] forEach()  (0) 2019.05.27
[JavaScript] Objects, Arrays, Array-Like objects  (0) 2019.03.28
[JavaScript] 현재시간 Timestamp 얻는 방법  (0) 2019.01.17
[JavaScript] Array Methods  (0) 2018.09.17
[JavaScript] Arrays  (0) 2018.09.16

댓글